{"id":15493483,"url":"https://github.com/sinclairzx81/smoke-rs","last_synced_at":"2025-10-20T02:52:03.642Z","repository":{"id":66035006,"uuid":"43547376","full_name":"sinclairzx81/smoke-rs","owner":"sinclairzx81","description":"lightweight async task and stream library for Rust","archived":false,"fork":false,"pushed_at":"2017-08-09T01:24:01.000Z","size":104,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-03T08:07:02.006Z","etag":null,"topics":["async","mpsc","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sinclairzx81.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-10-02T10:33:12.000Z","updated_at":"2022-02-17T21:05:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"90f55ade-844a-4758-a94a-3eb06550c926","html_url":"https://github.com/sinclairzx81/smoke-rs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fsmoke-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fsmoke-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fsmoke-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fsmoke-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sinclairzx81","download_url":"https://codeload.github.com/sinclairzx81/smoke-rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250313531,"owners_count":21410242,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","mpsc","rust"],"created_at":"2024-10-02T08:07:08.731Z","updated_at":"2025-10-20T02:51:58.608Z","avatar_url":"https://github.com/sinclairzx81.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# smoke-rs\n\n#### A lightweight async Task and Stream library for Rust\n\n```rust\nfn hello() -\u003e Task\u003c\u0026'static str\u003e {\n  Task::new(|sender| {\n    sender.send(\"hello world!!\")\n  })\n}\n```\n\n## Overview\n\nSmoke is a lightweight Task and Stream library for Rust. The library aims to help simplify \nasynchronous, concurrent and parallel programming in Rust by providing familiar primitives \nseen in languages like C# and JavaScript. \n\nSmoke's primary motive is to strike a good balance between Rust's thread safe semantics and the\nease of use of Tasks.\n\n* [Task\u0026lt;T\u0026gt;](#task)\n  * [Create Task](#creating_tasks)\n  * [Run Sync](#run_sync)\n  * [Run Async](#run_async)\n  * [Run Parallel](#run_parallel)\n  * [Scheduling](#scheduling)\n* [Stream\u0026lt;T\u0026gt;](#stream)\n  * [Output Streams](#output_streams)\n  * [Input Streams](#input_streams)\n  * [Merging](#merging_streams)\n  * [Operators](#stream_operators)\n\n\u003ca name='task'\u003e\u003c/a\u003e\n## Task\u0026lt;T\u0026gt;\n\nA Task encapsulates an asynchronous operation. \n\n\u003ca name='creating_tasks'\u003e\u003c/a\u003e\n### Create Task\n\nThe following will create a Task\u0026lt;i32\u0026gt;. The value for this task is \ngiven by the call to sender.send() function.\n\n```rust\nuse smoke::async::Task;\n\nfn main() {\n    let task = Task::new(|sender| {\n        sender.send(123)\n    });\n}\n```\n\n\u003ca name='run_sync'\u003e\u003c/a\u003e\n### Run Sync\n\nTo run a task synchronously, use the .wait() function. The .wait() \nfunction will block the current thread and return a Result\u0026lt;T, RecvError\u0026gt; \nwhen it can.\n\nThe following synchronously waits on a Task.\n\n```rust\nuse smoke::async::Task;\n\nfn main() {\n    let task = Task::new(|sender| {\n        sender.send(\"hello from task\")\n    });\n    \n    // blocking..\n    println!(\"{}\", task.wait().unwrap());\n}\n```\n\n\u003ca name='run_async'\u003e\u003c/a\u003e\n### Run Async\n\nA task can be run asynchronously with the .async() function. The .async() \nfunction will pass a Result\u0026lt;T, RecvError\u0026gt; into the closure provided\nand return a wait handle to the caller. The caller can use the handle to\nsynchronize the result back to the calling thread.\n\nThe following runs a Task asynchronously.\n\n```rust\nuse smoke::async::Task;\n\nfn main() {\n    let task = Task::new(|sender| {\n        sender.send(\"hello from task\")\n    });\n    \n    let handle = task.async(|result| {\n      println!(\"{}\", result.unwrap());\n    });\n    \n    // sometime later...\n    \n    handle.wait();\n}\n```\n\n\u003ca name='run_parallel'\u003e\u003c/a\u003e\n### Run Parallel\n\nMany tasks can be run in parallel by with the .all() function. This function accepts a vector\nof type Vec\u0026lt;Task\u0026lt;T\u0026gt;\u0026gt; and gives back a new task of type Task\u0026lt;Vec\u0026lt;T\u0026gt;\u0026gt;.\n\nThe following demonstrates running tasks in parallel.\n\n```rust\nuse smoke::async::Task;\n\nfn compute() -\u003e Task\u003ci32\u003e {\n  Task::new(move |sender| {\n    // emulate compute...\n    Task::delay(1000).wait();\n    sender.send(10)\n  })\n}\n\nfn main() {\n   // allocate 3 threads to process\n   // these tasks.\n   let result = Task::all(3, vec![\n     compute(),\n     compute(),\n     compute()\n   ]).wait().unwrap();\n}\n```\n\n\u003ca name=\"scheduling\"\u003e\u003c/a\u003e\n### Scheduling\n\nFor convenience, tasks manage scheduling on behalf of the caller, however, in some\nscenarios, it maybe desirable to have more control over task scheduling behavior.\n\nSmoke provides 3 built-in scheduler types users can use to schedule tasks manually. \n\nThese include:\n* SyncScheduler - Tasks scheduled here will be executed in the current thread.\n* ThreadScheduler - Tasks scheduled here will be executed in their own thread.\n* ThreadPoolScheduler - Tasks executed here will be executed on a bounded threadpool.\n\n```rust\nuse smoke::async::Task;\nuse smoke::async:: {\n  SyncScheduler,\n  ThreadScheduler,\n  ThreadPoolScheduler\n};\n\nfn hello() -\u003e Task\u003c\u0026'static str\u003e {\n  Task::delay(1).map(|_| \"hello\")\n}\n\nfn main() {\n   // runs the task synchronously.\n   let scheduler = SyncScheduler;\n   let result    = hello().schedule(scheduler).wait();\n   \n   // creates a new thread for each task run.\n   let scheduler = ThreadScheduler;\n   let result    = hello().schedule(scheduler).wait();\n      \n   // schedules the task to be run on a \n   // threadpool, in this example, there\n   // are 8 available threads.\n   let scheduler = ThreadPoolScheduler::new(8);\n   let result    = hello().schedule(scheduler).wait();   \n}\n```\n\n\u003ca name='stream'\u003e\u003c/a\u003e\n## Stream\u0026lt;T\u0026gt;\n\nStream\u0026lt;T\u0026gt; provides a simple abstraction to read and write streams of values asynchronously. \nInternally, Stream\u0026lt;T\u0026gt; is built over Rust mpsc channels, and manages the threading internals.\n\n\u003ca name='output_streams'\u003e\u003c/a\u003e\n### Output Streams\n\nThe following creates a function that creates a output stream of integer values. \nThe main() function iterates values on the stream by calling .read(). Output \nstreams are streams intended to be read externally to the stream.\n\n```rust\nuse smoke::async::Stream;\n\nfn numbers() -\u003e Stream\u003ci32\u003e {\n  let stream = Stream::output(|sender| {\n      sender.send(1).unwrap();\n      sender.send(2).unwrap();\n      sender.send(3).unwrap();\n      sender.send(4) // return mpsc \n  });  \n}\n\nfn main() {\n  let stream = numbers();\n  for n in stream.read() {\n    // n = 1, 2, 3, 4\n  }\n}\n```\n\n\u003ca name='input_streams'\u003e\u003c/a\u003e\n### Input Streams\n\nInput streams are the direct inverse of a output stream. When creating\na input stream, the caller is returned a sender in which to send values. \nValues sent from the caller are received on the input streams receiver.\n\n```rust\nuse smoke::async::{Stream, StreamSender};\n\nfn numbers() -\u003e StreamSender\u003ci32\u003e {\n  let stream = Stream::input(|receiver| {\n    for n in receiver {\n      // n = 1, 2, 3, 4\n    }\n  });  \n}\n\nfn main() {\n  let sender = numbers();\n  sender.send(1).unwrap();\n  sender.send(2).unwrap();\n  sender.send(3).unwrap();\n  sender.send(4).unwrap();\n}\n```\n\n\u003ca name='merging_streams'\u003e\u003c/a\u003e\n### Merging Streams\n\nOutput streams of the same type can be merged into a single stream with the .merge() function.\n\n```rust\nuse smoke::async::Stream;\n\n#[derive(Debug)]\nenum Item {\n  Number(i32),\n  Word(\u0026'static str)\n}\n\nfn numbers() -\u003e Stream\u003cItem\u003e {\n  Stream::output(|sender| {\n    try! (sender.send(Item::Number(1)) );\n    try! (sender.send(Item::Number(2)) );\n    try! (sender.send(Item::Number(3)) );\n    try! (sender.send(Item::Number(4)) );\n    Ok(())\n  }) \n}\n\nfn words() -\u003e Stream\u003cItem\u003e {\n  Stream::output(|sender| {\n      \"the quick brown fox jumps over the lazy dog\"\n        .split(\" \")\n        .map(|n| sender.send(Item::Word(n)))\n        .last()\n        .unwrap()\n  }) \n}\n\nfn main() {\n  let streams = vec![numbers(), words()];\n  let merged  = Stream::merge(streams);\n  for item in stream.read() {\n    match item {\n      Item::Word(word)     =\u003e println!(\"word: {:?}\", word),\n      Item::Number(number) =\u003e println!(\"number: {:?}\", number)\n    }\n  }\n}\n```\n\n\u003ca name='stream_operators'\u003e\u003c/a\u003e\n### Stream Operators\n\nStreams support .filter(), .map() and .fold() operators for stream composition. These operators are \nare deferred, and executed only when the stream is read().\n\n```rust\nuse smoke::async::Stream;\n\nfn numbers() -\u003e Stream\u003ci32\u003e {\n  Stream::output(|sender| {\n    try! (sender.send(1) );\n    try! (sender.send(2) );\n    try! (sender.send(3) );\n    sender.send(4)\n  }) \n}\n\nuse std::fmt::Debug;\nfn read\u003cT: Debug + Send + 'static\u003e(stream: Stream\u003cT\u003e) {\n    for n in stream.read(0) {\n      println!(\"{:?}\", n);\n    }\n}\n\nfn main() {\n  // filter\n  read(numbers().filter(|n| n % 2 == 0));\n  // map\n  read(numbers().map(|n| format!(\"num: {}\", n)));\n  // fold\n  println!(\"{}\", \n    numbers().fold(0, |p, c| p + c)\n             .wait()\n             .unwrap());\n  // everything\n  println!(\"{}\", \n    numbers().filter(|n| n % 2 == 0)\n             .map(|n| format!(\"{}\", n))\n             .fold(String::new(), |p, c| \n                    format!(\"{} {} and\", p, c))\n             .wait()\n             .unwrap());\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinclairzx81%2Fsmoke-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsinclairzx81%2Fsmoke-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinclairzx81%2Fsmoke-rs/lists"}