{"id":13419466,"url":"https://github.com/jamboree/co2","last_synced_at":"2026-01-20T22:37:42.917Z","repository":{"id":28103407,"uuid":"31601737","full_name":"jamboree/co2","owner":"jamboree","description":"A C++ await/yield emulation library for stackless coroutine","archived":false,"fork":false,"pushed_at":"2024-01-17T13:09:15.000Z","size":343,"stargazers_count":331,"open_issues_count":1,"forks_count":32,"subscribers_count":33,"default_branch":"master","last_synced_at":"2024-07-31T22:49:39.277Z","etag":null,"topics":["async-programming","await","c-plus-plus","c-plus-plus-14","concurrency","cooperative","coroutines","generator","header-only","multitasking","stackless","yield"],"latest_commit_sha":null,"homepage":"","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/jamboree.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":"2015-03-03T14:14:22.000Z","updated_at":"2024-07-24T21:08:39.000Z","dependencies_parsed_at":"2024-10-26T16:04:58.347Z","dependency_job_id":"5596a8bc-ea35-4728-9437-cd3b7b105912","html_url":"https://github.com/jamboree/co2","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/jamboree%2Fco2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamboree%2Fco2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamboree%2Fco2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamboree%2Fco2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamboree","download_url":"https://codeload.github.com/jamboree/co2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243690114,"owners_count":20331726,"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-programming","await","c-plus-plus","c-plus-plus-14","concurrency","cooperative","coroutines","generator","header-only","multitasking","stackless","yield"],"created_at":"2024-07-30T22:01:16.442Z","updated_at":"2026-01-20T22:37:42.888Z","avatar_url":"https://github.com/jamboree.png","language":"C++","readme":"CO2 - Coroutine II [![Try it online][badge.wandbox]](https://wandbox.org/permlink/hl2KlNuVWPFwggGE)\n===\n\nA header-only C++ stackless coroutine emulation library, providing interface close to [N4286](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4286.pdf).\n\n\u003e __Note__ \\\n\u003e All the major compilers support coroutine now, CO2 has accomplished its mission and we don't recommed using it for new code.\n However, it does have a successor - [COZ](https://github.com/jamboree/coz), which features zero-allocation.\n\n## Requirements\n\n- C++14\n- [Boost](http://www.boost.org/)\n\n## Overview\n\nMany of the concepts are similar to N4286, if you're not familiar with the proposal, please read the paper first.\n\nA coroutine written in this library looks like below:\n```c++\nauto function(Args... args) CO2_BEG(return_type, (args...), locals...)\n{\n    \u003ccoroutine-body\u003e\n} CO2_END\n```\n\n`function` is really just a plain-old function, you can forward declare it as usual:\n```c++\nauto function(Args... args) -\u003e return_type;\nreturn_type function(Args... args); // same as above\n```\n\nOf course, lambda expressions can be used as well:\n```c++\n[](Args... args) CO2_BEG(return_type, (args...), locals...)\n{\n    \u003ccoroutine-body\u003e\n} CO2_END\n```\n\nThe coroutine body has to be surrounded with 2 macros: `CO2_BEG` and `CO2_END`.\n\nThe macro `CO2_BEG` requires you to provide some parameters:\n* _return-type_ - the function's return-type, e.g. `co2::task\u003c\u003e`\n* _captures_ - a list of comma separated args with an optional `new` clause, e.g. `(a, b) new(alloc)`\n* _locals_ - a list of local-variable definitions, e.g. `int a;`\n\nIf there's no _captures_ and _locals_, it looks like:\n```c++\nCO2_BEG(return_type, ())\n```\n\nYou can intialize the local variables as below:\n```c++\nauto f(int i) CO2_BEG(return_type, (i),\n    int i2 = i * 2;\n    std::string msg{\"hello\"};\n)\n{\n    // coroutine-body\n} CO2_END\n```\n\nNote that the `()` initializer cannot be used here, e.g. `int i2(i * 2);`, due to some emulation restrictions.\nBesides, `auto` deduced variable cannot be used directly, i.e. `auto var{expr};`, you have to use `CO2_AUTO(var, expr);` instead.\n\nNote that in this emulation, local variables intialization happens before `initial_suspend`, and if any exception is thrown during the intialization, `set_exception` won't be called, instead, the exception will propagate to the caller directly.\n\nBy default, the library allocates memory for coroutines using `std::allocator`, you can specify the allocator by appending the `new` clause after the args-list, for example:\n\n```c++\ntemplate\u003cclass Alloc\u003e\nauto coro(Alloc alloc, int i) CO2_BEG(return_type, (i) new(alloc))\n```\n\nThe `alloc` doesn't have to appear in the args-list if it's not used inside the coroutine-body. The `new` clause accepts an expression that evaluates to an _Allocator_, it's not restricted to identifiers as in the args-list.\n\nInside the coroutine body, there are some restrictions:\n* local variables with automatic storage cannot cross suspend-resume points - you should specify them in local variables section of `CO2_BEG` as described above\n* `return` should be replaced with `CO2_RETURN`/`CO2_RETURN_FROM`/`CO2_RETURN_LOCAL`\n* try-catch block surrouding suspend-resume points should be replaced with `CO2_TRY` \u0026 `CO2_CATCH`\n* identifiers starting with `_co2_` are reserved for this library\n\nAfter defining the coroutine body, remember to close it with `CO2_END`.\n\n### await \u0026 yield\n\nIn _CO2_, `await` is implemented as a statement instead of an expression due to the emulation limitation, and it has 4 variants: `CO2_AWAIT`, `CO2_AWAIT_SET`, `CO2_AWAIT_LET` and `CO2_AWAIT_RETURN`.\n\n* `CO2_AWAIT(expr)`\n\nEquivalent to `await expr`.\n\n* `CO2_AWAIT_SET(var, expr)`\n\nEquivalent to `var = await expr`.\n\n* `CO2_AWAIT_LET(var-decl, expr, body)`\n\nThis allows you bind the awaited result to a temporary and do something to it.\n```c++\nCO2_AWAIT_LET(auto i, task,\n{\n    doSomething(i);\n});\n```\n\n* `CO2_AWAIT_RETURN(expr)`\n\nEquivalent to `return await expr`.\n\n* `CO2_AWAIT_APPLY(f, expr)`\n\nEquivalent to `f(await expr)`, where `f` can be a unary function or macro.\n\n\u003e *Note* -\n\u003e If your compiler supports _Statement Expression_ extension (e.g. GCC \u0026 Clang), you can use `CO2_AWAIT` as an expression.\nHowever, don't use more than one `CO2_AWAIT` in a single statement, and don't use it as an argument of a function in company with other arguments.\n\n* `CO2_YIELD(expr)`\n\nEquivalent to `CO2_AWAIT(\u003cthis-promise\u003e.yield_value(expr))`, as how `yield` is defined in N4286.\n\n* `CO2_SUSPEND(fn)`\n\nSuspend the coroutine with the callable object `fn`. This signature of `fn` is the same as `await_suspend`.\n\n\nThe fact that `await` in _CO2_ is not an expression has an implication on object lifetime, consider this case:\n\n`await something{temporaries}` and `something` holds references to temporaries.\n\nIt's safe if `await` is an expression as in N4286, but in _CO2_, `CO2_AWAIT(something{temporaries})` is an emulated statement, the `temporaries` will go out of scope.\n\nBesides, the awaiter itself has to be stored somewhere, by default, _CO2_ reserves `(sizeof(pointer) + sizeof(int)) * 2` bytes for that, if the size of awaiter is larger than that, dynamic allocation will be used.\nIf the default size is too large or too small for you, you can specify the desired size with `CO2_TEMP_SIZE` anywhere in the local variables section:\n```c++\nauto f() CO2_BEG(return_type, (),\n    CO2_TEMP_SIZE(bytes);\n)\n{\n    ...\n} CO2_END\n```\n\nIf you want to avoid dynamic allocation, you can define `CO2_WARN_DYN_ALLOC` to turn on dynamic allocation warning and enlarge `CO2_TEMP_SIZE` accordingly.\n\n### Replacements for normal language constructs\n\nSometimes you can't use the normal language constructs directly, in such cases, you need to use the macro replacements instead.\n\n#### return\n\n* `return` -\u003e `CO2_RETURN()`\n* `return non-void-expr` -\u003e `CO2_RETURN(non-void-expr)`\n* `return maybe-void-expr` -\u003e `CO2_RETURN_FROM(maybe-void-expr)` (useful in generic code)\n* `return local-variable` -\u003e `CO2_RETURN_LOCAL(local-variable)` (RV w/o explicit move)\n\n#### try-catch\n\nNeeded only if the try-block is involved with the suspend-resume points.\n\n```c++\nCO2_TRY {...}\nCO2_CATCH (std::runtime_error\u0026 e) {...}\ncatch (std::exception\u0026 e) {...}\n```\n\nNote that only the first `catch` clause needs to be spelled as `CO2_CATCH`, the subsequent ones should use the plain `catch`.\n\n#### switch-case\n\nNeeded only if the switch-body is involved with the suspend-resume points. There are 2 variants:\n* `CO2_SWITCH`\n* `CO2_SWITCH_CONT` - use when switch-body contains `continue`.\n\n```c++\nCO2_SWITCH (which,\ncase 1,\n(\n    ...\n),\ncase N,\n(\n    ...\n),\ndefault,\n(\n    ...\n))\n```\n\nNote that `break` is still needed if you don't want the control flow to fall through the subsequent cases, also note that `continue` **cannot** be used in `CO2_SWITCH` to continue the outer loop, use `CO2_SWITCH_CONT` instead in that case.\n\n## Difference from N4286\n\n* Unlike `coroutine_handle` in N4286 which has raw-pointer semantic (i.e. no RAII), `coroutine` has unique-semantic (move-only).\n* `coroutine_traits` depends on return_type only.\n\n### Additional customization points for promise_type\n\n* `void cancel()`\n\nThis allows you specify the behavior of the coroutine when it is cancelled (i.e. when `cancellation_requested()` returns true or coroutine is reset).\n\n* `bool try_suspend()`\n\nThis is called before the coroutine is suspended, if it returns `false`, the coroutine won't be suspended, instead, it will be cancelled.\nHowever, it won't be called for `final_suspend`.\n\n* `bool try_resume()`\n\nThis is called before the coroutine is resumed, if it returns `false`, the coroutine won't be resumed, instead, it will be detached.\n\n* `bool try_cancel()`\n\nThis is called before the coroutine is reset, if it returns `false`, the coroutine won't be cancelled, instead, it will be detached.\n\n## Reference\n\n__Headers__\n* `#include \u003cco2/coroutine.hpp\u003e`\n* `#include \u003cco2/generator.hpp\u003e`\n* `#include \u003cco2/recursive_generator.hpp\u003e`\n* `#include \u003cco2/task.hpp\u003e`\n* `#include \u003cco2/shared_task.hpp\u003e`\n* `#include \u003cco2/lazy_task.hpp\u003e`\n* `#include \u003cco2/sync/event.hpp\u003e`\n* `#include \u003cco2/sync/mutex.hpp\u003e`\n* `#include \u003cco2/sync/work_group.hpp\u003e`\n* `#include \u003cco2/sync/when_all.hpp\u003e`\n* `#include \u003cco2/sync/when_any.hpp\u003e`\n* `#include \u003cco2/blocking.hpp\u003e`\n* `#include \u003cco2/adapted/boost_future.hpp\u003e`\n* `#include \u003cco2/adapted/boost_optional.hpp\u003e`\n* `#include \u003cco2/utility/stack_allocator.hpp\u003e`\n\n__Macros__\n* `CO2_BEG`\n* `CO2_END`\n* `CO2_AWAIT`\n* `CO2_AWAIT_SET`\n* `CO2_AWAIT_LET`\n* `CO2_AWAIT_RETURN`\n* `CO2_AWAIT_APPLY`\n* `CO2_YIELD`\n* `CO2_SUSPEND`\n* `CO2_RETURN`\n* `CO2_RETURN_FROM`\n* `CO2_RETURN_LOCAL`\n* `CO2_TRY`\n* `CO2_CATCH`\n* `CO2_SWITCH`\n* `CO2_TEMP_SIZE`\n* `CO2_AUTO`\n\n__Classes__\n* `co2::coroutine_traits\u003cR\u003e`\n* `co2::coroutine\u003cPromise\u003e`\n* `co2::generator\u003cT\u003e`\n* `co2::recursive_generator\u003cT\u003e`\n* `co2::task\u003cT\u003e`\n* `co2::shared_task\u003cT\u003e`\n* `co2::lazy_task\u003cT\u003e`\n* `co2::event`\n* `co2::mutex`\n* `co2::work_group`\n* `co2::suspend_always`\n* `co2::suspend_never`\n* `co2::stack_manager`\n* `co2::stack_buffer\u003cBytes\u003e`\n* `co2::stack_allocator\u003cT\u003e`\n\n## Example\n\n### Generator\n\n__Define a generator__\n```c++\nauto range(int i, int e) CO2_BEG(co2::generator\u003cint\u003e, (i, e))\n{\n    for ( ; i != e; ++i)\n        CO2_YIELD(i);\n} CO2_END\n```\nFor those interested in the black magic, [here](https://gist.github.com/jamboree/d6c324b6cd4a11676cda) is the preprocessed output (formatted for reading).\n\n__Use a generator__\n```c++\nfor (auto i : range(1, 10))\n{\n    std::cout \u003c\u003c i \u003c\u003c \", \";\n}\n```\n\n### Recursive Generator\n\nSame example as above, using `recursive_generator` with custom allocator:\n```c++\ntemplate\u003cclass Alloc\u003e\nauto recursive_range(Alloc alloc, int a, int b)\nCO2_BEG(co2::recursive_generator\u003cint\u003e, (alloc, a, b) new(alloc),\n    int n = b - a;\n)\n{\n    if (n \u003c= 0)\n        CO2_RETURN();\n\n    if (n == 1)\n    {\n        CO2_YIELD(a);\n        CO2_RETURN();\n    }\n\n    n = a + n / 2;\n    CO2_YIELD(recursive_range(alloc, a, n));\n    CO2_YIELD(recursive_range(alloc, n, b));\n} CO2_END\n```\nWe use `stack_allocator` here:\n```c++\nco2::stack_buffer\u003c64 * 1024\u003e buf;\nco2::stack_allocator\u003c\u003e alloc(buf);\nfor (auto i : recursive_range(alloc, 1, 10))\n{\n    std::cout \u003c\u003c i \u003c\u003c \", \";\n}\n```\n\n### Task scheduling\nIt's very easy to write a generic task that can be used with different schedulers.\nFor example, a `fib` task that works with [`concurrency::task_group`](https://msdn.microsoft.com/en-us/library/dd470722.aspx) and [`tbb::task_group`](https://software.intel.com/en-us/node/506287) can be defined as below:\n```c++\ntemplate\u003cclass Scheduler\u003e\nauto fib(Scheduler\u0026 sched, int n) CO2_BEG(co2::task\u003cint\u003e, (sched, n),\n    co2::task\u003cint\u003e a, b;\n)\n{\n    // Schedule the continuation.\n    CO2_SUSPEND([\u0026](co2::coroutine\u003c\u003e\u0026 c) { sched.run([h = c.detach()]{ co2::coroutine\u003c\u003e{h}(); }); });\n    // From now on, the code is executed on the Scheduler.\n    if (n \u003e= 2)\n    {\n        a = fib(sched, n - 1);\n        b = fib(sched, n - 2);\n        CO2_AWAIT_SET(n, a);\n        CO2_AWAIT_APPLY(n +=, b);\n    }\n    CO2_RETURN(n);\n} CO2_END\n```\n\n#### PPL Usage\n```c++\nconcurrency::task_group sched;\nauto val = fib(sched, 16);\nstd::cout \u003c\u003c \"ans: \" \u003c\u003c co2::get(val);\nsched.wait();\n```\n\n#### TBB Usage\n```c++\ntbb::task_group sched;\nauto val = fib(sched, 16);\nstd::cout \u003c\u003c \"ans: \" \u003c\u003c co2::get(val);\nsched.wait();\n```\n\n### ASIO echo server\n\nThis example uses the sister library [act](https://github.com/jamboree/act) to change ASIO style callback into await.\n\n```c++\nauto session(asio::ip::tcp::socket sock) CO2_BEG(void, (sock),\n    char buf[1024];\n    std::size_t len;\n    act::error_code ec;\n)\n{\n    CO2_TRY\n    {\n        std::cout \u003c\u003c \"connected: \" \u003c\u003c sock.remote_endpoint() \u003c\u003c std::endl;\n        for ( ; ; )\n        {\n            CO2_AWAIT_SET(len, act::read_some(sock, asio::buffer(buf), ec));\n            if (ec == asio::error::eof)\n                CO2_RETURN();\n            CO2_AWAIT(act::write(sock, asio::buffer(buf, len)));\n        }\n    }\n    CO2_CATCH (std::exception\u0026 e)\n    {\n        std::cout \u003c\u003c \"error: \" \u003c\u003c sock.remote_endpoint() \u003c\u003c \": \" \u003c\u003c e.what() \u003c\u003c std::endl;\n    }\n} CO2_END\n\nauto server(asio::io_service\u0026 io, unsigned short port) CO2_BEG(void, (io, port),\n    asio::ip::tcp::endpoint endpoint{asio::ip::tcp::v4(), port};\n    asio::ip::tcp::acceptor acceptor{io, endpoint};\n    asio::ip::tcp::socket sock{io};\n)\n{\n    std::cout \u003c\u003c \"server running at: \" \u003c\u003c endpoint \u003c\u003c std::endl;\n    for ( ; ; )\n    {\n        CO2_AWAIT(act::accept(acceptor, sock));\n        session(std::move(sock));\n    }\n} CO2_END\n```\n\n## Performance\nThe overhead of context-switch. See [benchmark.cpp](test/benchmark.cpp).\n\nSample run (VS2015 Update 3, boost 1.63.0, 64-bit release build):\n```\nRun on (4 X 3200 MHz CPU s)\nBenchmark                  Time           CPU Iterations\n--------------------------------------------------------\nbench_coroutine2         82 ns         80 ns    8960000\nbench_co2                  6 ns          6 ns  112000000\nbench_msvc                 5 ns          5 ns  112000000\n```\nLower is better.\n\n![benchmark](doc/benchmark.png?raw=true)\n\n## License\n\n    Copyright (c) 2015-2018 Jamboree\n\n    Distributed under the Boost Software License, Version 1.0. (See accompanying\n    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n\n\u003c!-- Links --\u003e\n[badge.Wandbox]: https://img.shields.io/badge/try%20it-online-green.svg\n","funding_links":[],"categories":["TODO scan for Android support in followings","HarmonyOS","C++"],"sub_categories":["Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamboree%2Fco2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamboree%2Fco2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamboree%2Fco2/lists"}