{"id":20077214,"url":"https://github.com/boostorg/cobalt","last_synced_at":"2025-05-16T15:07:35.372Z","repository":{"id":147513395,"uuid":"564394636","full_name":"boostorg/cobalt","owner":"boostorg","description":"Coroutines for C++20 \u0026 asio","archived":false,"fork":false,"pushed_at":"2025-04-12T04:20:58.000Z","size":1861,"stargazers_count":264,"open_issues_count":36,"forks_count":29,"subscribers_count":13,"default_branch":"develop","last_synced_at":"2025-04-12T14:17:11.667Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.boost.org/doc/libs/master/libs/cobalt/doc/html/index.html","language":"C++","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/boostorg.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,"zenodo":null}},"created_at":"2022-11-10T16:14:29.000Z","updated_at":"2025-04-10T19:25:51.000Z","dependencies_parsed_at":"2023-10-23T03:21:45.072Z","dependency_job_id":"694a5e4f-edf2-4f1f-b3dd-cd91b0bc9280","html_url":"https://github.com/boostorg/cobalt","commit_stats":{"total_commits":454,"total_committers":11,"mean_commits":41.27272727272727,"dds":0.05947136563876654,"last_synced_commit":"e69138f36bee13cb8f1bcb7a9f9c018add86b14d"},"previous_names":["boostorg/cobalt"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostorg%2Fcobalt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostorg%2Fcobalt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostorg%2Fcobalt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostorg%2Fcobalt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boostorg","download_url":"https://codeload.github.com/boostorg/cobalt/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553958,"owners_count":22090417,"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-11-13T15:06:36.051Z","updated_at":"2025-05-16T15:07:33.691Z","avatar_url":"https://github.com/boostorg.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# boost.cobalt\n\nThis library provides a set of easy to use coroutine primitives \u0026 utilities running on top of boost.asio.\nThese will be of interest for applications that perform a lot of IO that want to not block unnecessarily,\nyet still want to have linear \u0026 readable code (i..e. avoid callbacks).\n\nA minimum of Boost 1.82 is necessary as the ASIO in that version has needed support. C++ 20 is needed for C++ coroutines.\n\nBelow is a showcase of features, if you're new to coroutines or asynchronous programming, please see the [primer](https://www.boost.org/doc/libs/master/libs/cobalt/doc/html/index.html#coroutine_primer).\n\nThe assumptions are:\n\n - `io_context` is the execution_context of choice.\n - If `asio::io_context` is the executor, no more than one kernel thread executes within it at a time.\n - Eager execution is the way to go.\n - A thread created with promise is only using promise stuff.\n\n## Entry points\n\n```cpp\n// a single threaded main running on an io_context\ncobalt::main co_main(int argc, char ** argv)\n{\n    // wrapper around asio::steady_timer\n    asio::steady_timer tim{co_await cobalt::this_coro::executor};\n    tim.expires_after(std::chrono::milliseconds(100));\n\n    co_await tim.async_wait(cobalt::use_op);\n    co_return 0;\n}\n```\n\nThat is, [`main`](doc/reference/main.adoc) runs on a single threaded `io_context`.\n\nIt also hooks up signals, so that things like `Ctrl+C` get forwarded as cancellations automatically\n\nAlternatively, [`run`](doc/reference/run.adoc) can be used manually.\n\n```cpp\ncobalt::task\u003cint\u003e main_func()\n{\n    asio::steady_timer tim{co_await cobalt::this_coro::executor};\n    tim.expires_after(std::chrono::milliseconds(100));\n\n    co_await tim.async_wait(cobalt::use_op);\n    co_return 0;\n}\n\n\nint main(int argc, char ** argv)\n{\n    return run(main_func());\n}\n```\n\n## Promises\n\nThe core primitive for creating your own functions is [`cobalt::promise\u003cT\u003e`](doc/reference/promise.adoc).\nIt is eager, i.e. it starts execution immediately, before you `co_await`.\n\n```cpp\ncobalt::promise\u003cvoid\u003e test()\n{\n    printf(\"test-1\\n\");\n    asio::steady_timer tim{co_await cobalt::this_coro::executor};\n    tim.expires_after(std::chrono::milliseconds(100));\n    co_await tim.async_wait(cobalt::use_op);\n    printf(\"test-2\\n\");\n}\n\ncobalt::main co_main(int argc, char ** argv)\n{\n    printf(\"main-1\\n\");\n    auto tt = test();\n    printf(\"main-2\\n\");\n    co_await tt;\n    printf(\"main-3\\n\");\n    return 0;\n}\n```\n\nThe output of the above will be:\n\n```cpp\nmain-1\ntest-1\nmain-2\ntest-2\nmain-3\n```\n\nUnlike ops, returned by .wait, the promise can be disregarded; disregarding the promise does not cancel it, but rather detaches is. This makes it easy to \nspin up multiple tasks to run in parallel. In order to avoid accidental detaching the promise type uses `nodiscard` unless one uses `+` to detach it:\n\n```cpp\ncobalt::promise\u003cvoid\u003e my_task();\n\ncobalt::main co_main()\n{\n    // warns \u0026 cancels the task\n    my_task();\n    // ok\n    +my_task();\n    co_return 0;\n}\n```\n\n## Task\n\nA [`task`](doc/reference/task.adoc) is a lazy alternative to a promise, that can be spawned onto or `co_await`ed on another executor.\n\nAn `cobalt::task` can also be used with `spawn` to turn it into an asio operation.\n\n## Generator\n\nA [`generator`](doc/reference/generator.adoc) is a coroutine that produces a series of values instead of one, but otherwise similar to `promise`.\n\n```cpp\ncobalt::generator\u003cint\u003e test()\n{\n  printf(\"test-1\\n\");\n  co_yield 1;\n  printf(\"test-2\\n\");\n  co_yield 2;\n  printf(\"test-3\\n\");\n  co_return 3;\n}\n\ncobalt::main co_main(int argc, char ** argv)\n{\n    printf(\"main-1\\n\");\n    auto tt = test();\n    printf(\"main-2\\n\");\n    i = co_await tt; // 1\n    printf(\"main-3: %d\\n\", i);\n    i = co_await tt; // 2\n    printf(\"main-4: %d\\n\", i);\n    i = co_await tt; // 3\n    printf(\"main-5: %d\\n\", i);\n    co_return 0;\n}\n```\n\n```\nmain-1\ntest-1\nmain-2\nmain-3: 1\ntest-2\nmain-4: 2\ntest-3\nmain-5: 3\n```\n\n## Channels\n\nChannels are modeled on golang; they are different from boost.asio channels in that they don't go through the executor.\nInstead they directly context switch when possible.\n\n```cpp\ncobalt::promise\u003cvoid\u003e test(cobalt::channel\u003cint\u003e \u0026 chan)\n{\n  printf(\"Reader 1: %d\\n\", co_await chan.read());\n  printf(\"Reader 2: %d\\n\", co_await chan.read());\n  printf(\"Reader 3: %d\\n\", co_await chan.read());\n}\n\ncobalt::main co_main(int argc, char ** argv)\n{\n  cobalt::channel\u003cint\u003e chan{0u /* buffer size */};\n  \n  auto p = test(chan);\n  \n  printf(\"Writer 1\\n\");\n  co_await chan.write(10);\n  printf(\"Writer 2\\n\");\n  co_await chan.write(11);\n  printf(\"Writer 3\\n\");\n  co_await chan.write(12);\n  printf(\"Writer 4\\n\");\n  \n  co_await p;\n  co_return 0u;\n}\n```\n\n````\nWriter-1\nReader-1: 10\nWriter-2\nReader-1: 11\nWriter-3\nReader-1: 12\nWriter-4\n````\n\n## Ops\n\nTo make writing asio operations that have an early completion easier, cobalt has an op-helper:\n\n```cpp\ntemplate\u003ctypename Timer\u003e\nstruct wait_op : cobalt::op\u003csystem::error_code\u003e // enable_op is to use ADL\n{\n  Timer \u0026 tim;\n\n  wait_op(Timer \u0026 tim) : tim(tim) {}\n  \n  // this gets used to determine if it needs to suspend for the op\n  void ready(cobalt::handler\u003csystem::error_code\u003e h)\n  {\n    if (tim.expiry() \u003c Timer::clock_type::now())\n      h(system::error_code(asio::error::operation_aborted));\n  }\n  \n  // this gets used to initiate the op if ti needs to suspend\n  void initiate(cobalt::completion_handler\u003csystem::error_code\u003e complete)\n  {\n    tim.async_wait(std::move(complete));\n  }\n};\n\ncobalt::main co_main(int argc, char ** argv)\n{\n  cobalt::steady_timer tim{co_await cobalt::this_coro::executor}; // already expired\n  co_await wait_op(tim); // will not suspend, since its ready\n}\n\n```\n\n\n## race\n\n[`race`](doc/reference/race.adoc) let's you await multiple awaitables at once. \n\n```cpp\ncobalt::promise\u003cvoid\u003e delay(int ms)\n{\n    asio::steady_timer tim{co_await cobalt::this_coro::executor};\n    tim.expires_after(std::chrono::milliseconds(ms));\n    co_await tim.async_wait(cobalt::use_op);\n}\n\ncobalt::main co_main(int argc, char ** argv)\n{\n  auto res = co_await race(delay(100), delay(50));\n  asert(res == 1); // delay(50) completes earlier, delay(100) is not cancelled  \n  co_return 0u;\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboostorg%2Fcobalt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboostorg%2Fcobalt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboostorg%2Fcobalt/lists"}