{"id":22691556,"url":"https://github.com/dx3mod/tiny-async-lib","last_synced_at":"2025-04-12T06:24:34.950Z","repository":{"id":265250906,"uuid":"895588656","full_name":"dx3mod/tiny-async-lib","owner":"dx3mod","description":"Tiny concurrent I/O and promises library ","archived":false,"fork":false,"pushed_at":"2024-12-02T07:38:43.000Z","size":34,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-26T01:51:07.277Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dx3mod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-11-28T13:44:11.000Z","updated_at":"2024-12-04T18:37:58.000Z","dependencies_parsed_at":"2025-02-04T17:38:03.692Z","dependency_job_id":"1db0577a-3ee8-473a-bd3a-a076c7a47107","html_url":"https://github.com/dx3mod/tiny-async-lib","commit_stats":null,"previous_names":["dx3mod/tiny-async-lib"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dx3mod%2Ftiny-async-lib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dx3mod%2Ftiny-async-lib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dx3mod%2Ftiny-async-lib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dx3mod%2Ftiny-async-lib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dx3mod","download_url":"https://codeload.github.com/dx3mod/tiny-async-lib/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248526343,"owners_count":21118866,"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":[],"created_at":"2024-12-10T01:11:34.332Z","updated_at":"2025-04-12T06:24:34.926Z","avatar_url":"https://github.com/dx3mod.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tiny-async-lib\n\nTiny [concurrent I/O] and [promises] library inspired by [Lwt].\nIt's just an educational project, not for use.\n\nYou can think of it as a lightweight mental model of asynchronous frameworks like Lwt, etc. For an understanding of how it really works under the hood.\n\n```ocaml\nopen Tiny_async_lib\nopen Promise.Syntax\n\nlet main =\n  let* () = Io.(write_all stdout) \"Hi! What's your name? \" in\n  let* name = Io.(read_line stdin) in\n  Io.(write_all stdout) (\"Hello, \" ^ name ^ \"!\\n\")\n\nlet () = Engine.run main\n````\n\n```console\n$ dune exec ./examples/hello.exe\nHi! What's your name? Артём      \nHello, Артём!\n```\n\nSee more examples in [the directory](./examples/). \n\n## See also\n\n- Chapter [8.7. Promises](https://cs3110.github.io/textbook/chapters/ds/promises.html), where I got my understanding of how to implement promises and how they work\n- Beautiful [Lwt's source code](https://github.com/ocsigen/lwt/tree/master/src) with detailed implementation comments\n- Whitepaper [Lwt: a Cooperative Thread Library](https://www.irif.fr/~vouillon/publi/lwt.pdf) is a really accessible article to understand the core of Lwt\n- Another great resource is the book [Unix system programming in OCaml](https://ocaml.github.io/ocamlunix/) for writing OS-dependent code\n\n## Quick start\n\nFor build the library, you should have OCaml 4.14 (LTS) and above, and the Dune build system. No out-of-the-box dependencies are required. \n\nTo play with the source code you can just do it:\n```console\n$ git clone https://github.com/dx3mod/tiny-async-lib.git\n$ cd ./tiny-async-lib\n$ dune build\n```\n\nVia an interactive toplevel environment using the Utop:\n```console\n$ dune utop\n```\n\nYou can also install library using the OPAM package manager:\n```console\n$ opam tiny-async-lib.dev https://github.com/dx3mod/tiny-async-lib.git\n```\n\nAmong other things, it is useful to have API references for easy navigation through the library using odoc. \n```console\n$ dune build @doc\n$ python -m http.server 8080 --directory _build/default/_doc/_html/ \n```\n## In Depth\n\nThe `Tiny_async_lib` consists of three important parts: **promises**, asynchronous **engine** and **I/O** module.\n\n### Promises\n\nPromise is the first key abstraction, an abstraction for synchronizing program execution in concurrent (non-sequential) evaluations.\n\nIn simple terms, it’s an abstraction over callbacks. Promises allow us to build (monadic) sequential evaluations inside non-sequential evaluations.\n\nTypical example of callbacks for asynchronous (non-sequential) code:\n```ocaml\nlet read_two_files (file1, file2) callback = \n  async_read_file file1 (fun _ -\u003e \n    async_read_file file2 (fun _ -\u003e \n      (* ... *)))\n\nread_two_files (\"file-1\", \"file-2\") (fun _ -\u003e \n  (* ... *))\n```\n\nSame thing, but with promises:\n\n```ocaml\nlet read_two_files (file1, file2) =\n  let* _ = async_read_file file1 in \n  let* _ = async_read_file file2 in \n  (* ... *)\n\nlet* _ = read_two_files (\"file-1\", \"file-2\") in\n(* ... *)\n```\n\nA promise is basically an object that acts as a proxy for a result that we don't know yet, usually because we haven't finished computing its value.\n\nIt's very much the `lazy_t` type.\n```ocaml\n# lazy (1 + 1);;\n- : int lazy_t = \u003clazy\u003e\n```\n\nA promis can have one of three states: *fulfilled* (contains a value), *rejected* (contains an exception), and *pending* (contains callbacks).\n\nIf a promise is fulfilled or rejected, it is called *resolved*.\n\nCallbacks are functions that are called when a promise is resolved.\nSo when we (monadic) bind, if the promise is in pending state, we add a callback that calls the following monadic sequence when the promise is resolved.\n\n#### Make a promise\n\nTypical pattern of making *raw* promises i. e. wrapping callbacks.\n\n```ocaml\nlet async_event () = \n  (* The promise is public read-only interface. \n     The resolver is private interface for resolve the promise. *)\n  let promise, resolver = Promise.make () in \n\n  (* Callback wrapping. *)\n  on_event (fun event -\u003e \n    (* ... *)\n    Promise.fulfill resolver event);\n\n  (* Returns the public interface, promise. *)\n  promise\n```\nNow we can write linear code on how to process the promised value. \n```ocaml\nasync_event () \u003e\u003e= do_something \u003e\u003e= do_something_yet\n```\n\nIn details:\n```ocaml\n# let p = async_event ();;\n\n# Promise.state p;;\n- : event Promise.state = Pending []\n\n# p \u003e\u003e= fun _ -\u003e Promise.return ();;\n\n# Promise.state p;;\n- : event Promise.state = Pending [\u003cabstr\u003e]\n```\n\n### Engine\n\nThe second key abstraction and part of the library is an [asynchronous I/O] engine that polls I/O events and dispatches them to handlers. With this we have multiplexed I/O, event subscription, etc.\n\n```ocaml\nlet sleep delay =\n  let promise, resolver = Promise.make () in\n\n  Engine.(on_timer instance) delay (fun handler -\u003e\n      Engine.Handler.stop handler;\n      Promise.fulfill resolver ());\n\n  promise\n```\n\nThe engine implemented in the library is based on the (unix) [select] mechanism. Select is very easy to use. It queries read and write ready file descriptors, i.e. those that are ready for processing, and dispatches them to their handlers.\n\nThe (typical) asynchronous engine in internals has an [event loop]. At each iteration of the event loop, the engine polls for new events and calls handlers to handle them.\n\n```ocaml\nlet iter engine =\n  (* ... *)\n\n  let readable_fds, writable_fds, _ =\n    Unix.select readable_fds writable_fds [] timeout\n  in\n\n  engine.sleepers \u003c- restart_sleeper_handlers engine.sleepers ~now;\n\n  invoke_io_handlers engine.wait_readable readable_fds;\n  invoke_io_handlers engine.wait_writable writable_fds\n```\n\nWith all this in place, it is possible to resolve I/O promises. It's not a big deal. We just have to loop the event loop until the promis is resolved.\n\n```ocaml\nlet rec run promise =\n  match Promise.state promise with\n  | Fulfilled value -\u003e value\n  | Rejected exc -\u003e raise exc\n  | Pending _ -\u003e\n      Event_loop.iter instance;\n      run promise\n```\n\n[asynchronous I/O]: https://en.wikipedia.org/wiki/Asynchronous_I/O\n[select]: https://en.wikipedia.org/wiki/Select_(Unix)\n[event loop]: https://en.wikipedia.org/wiki/Event_loop\n\n### I/O\n\nThis part of the library couples promises and engine to do useful programs. There was an example of a `sleep` function earlier. \n\nAsynchronous engine callback functions (called handlers) are wrapped to create I/O promises. For example, the `write_all` function:\n\n```ocaml\nlet write_all fd contents =\n  let promise, resolver = Promise.make () in\n\n  (* ... *)\n\n  let handler handler = \n    let bytes_write = Unix.write fd bytes !all_bytes_write length in\n    (* ... *)\n    if !all_bytes_write = length then begin\n      (* ... *)\n      Promise.fulfill resolver ()\n    end\n  in\n\n  Engine.(on_writable instance) fd handler;\n\n  promise\n```\n\n### Afterword\n\nEnjoy it! :\u003c\n\n## License\n\nIt's not very useful code for real things. Many parts of the implementation are lifted from other solutions. It's a crazy mix. Do whatever you want with that code.\n\n[promises]: https://en.wikipedia.org/wiki/Futures_and_promises\n[concurrent I/O]: https://en.wikipedia.org/wiki/Asynchronous_I/O\n[Lwt]: https://github.com/ocsigen/lwt","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdx3mod%2Ftiny-async-lib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdx3mod%2Ftiny-async-lib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdx3mod%2Ftiny-async-lib/lists"}