{"id":21946522,"url":"https://github.com/fuyutsubaki/coffin-goroutine","last_synced_at":"2025-09-14T13:07:11.215Z","repository":{"id":56688427,"uuid":"294996568","full_name":"Fuyutsubaki/coffin-goroutine","owner":"Fuyutsubaki","description":"single header library for supporting goroutine-like concurrency in C++","archived":false,"fork":false,"pushed_at":"2023-02-26T17:53:09.000Z","size":55,"stargazers_count":3,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-18T16:23:42.480Z","etag":null,"topics":["c-plus-plus","concurrency","cpp20","header-only","single-header-lib"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Fuyutsubaki.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":"2020-09-12T17:50:03.000Z","updated_at":"2023-02-26T17:16:22.000Z","dependencies_parsed_at":"2024-11-29T09:31:16.818Z","dependency_job_id":null,"html_url":"https://github.com/Fuyutsubaki/coffin-goroutine","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuyutsubaki%2Fcoffin-goroutine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuyutsubaki%2Fcoffin-goroutine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuyutsubaki%2Fcoffin-goroutine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuyutsubaki%2Fcoffin-goroutine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Fuyutsubaki","download_url":"https://codeload.github.com/Fuyutsubaki/coffin-goroutine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250332423,"owners_count":21413204,"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":["c-plus-plus","concurrency","cpp20","header-only","single-header-lib"],"created_at":"2024-11-29T04:32:39.821Z","updated_at":"2025-04-22T22:22:59.034Z","avatar_url":"https://github.com/Fuyutsubaki.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# coffin/goroutine\r\n\r\n\"coffin/goroutine\" は goroutine/channelのような非同期実行をサポートする シングルヘッダライブラリである\r\n\r\n## チュートリアル\r\n\r\nチュートリアルで説明するすべてのコードは、下のリンクからブラウザで試すことができる\r\nhttps://wandbox.org/permlink/3LBaAqOACoKCTmB3\r\n\r\n### 準備\r\n\r\n#### 1. Scheduler\r\nもしあなたのアプリケーションがSchedulerを持っていない場合、Schedulerを用意する必要がある\r\n- coffin/goroutineはSchedulerを含んでいない。これはアプリケーションにすでにSchedulerが存在する場合、それと共存するためである\r\n\r\n```C++\r\n// Scheduler\r\nstruct MyScheduler {\r\n    boost::asio::io_service io_service_;\r\n    std::shared_ptr\u003cboost::asio::io_service::work\u003e work_;\r\n    void post(std::function\u003cvoid()\u003e \u0026\u0026 f){\r\n        io_service_.post(std::move(f));\r\n    }\r\n\r\n    void run(std::size_t n){\r\n        work_ = std::make_shared\u003cboost::asio::io_service::work\u003e(boost::asio::io_service::work(io_service_));\r\n        std::vector\u003cstd::thread\u003e thread_list;\r\n        for (std::size_t i = 0; i \u003c n; ++i) {\r\n            thread_list.push_back(std::thread{[\u0026]{io_service_.run();}});\r\n        }\r\n        for(auto\u0026th:thread_list)\r\n            th.join();\r\n\r\n        io_service_.reset();\r\n    }\r\n\r\n    void stop(){\r\n        work_.reset();\r\n    }\r\n};\r\n\r\nstatic inline MyScheduler global_scheduler;\r\n```\r\n\r\n#### 2. ChannelStrategy\r\n1のSchedulerとChannnelをつなぐ、 `ChannelStrategy` を定義する必要がある\r\n\r\nするべきことは、渡された goroutineを一度だけ実行する処理をScheduler に postする関数`post_goroutine` を定義することである\r\n\r\n```C++\r\nstruct MyStrategy{\r\n    void post_goroutine(std::shared_ptr\u003ccfn::Goroutine\u003e \u0026\u0026 g){\r\n        global_scheduler.post([=]()mutable{g-\u003eexecute();}); \r\n    }\r\n};\r\n```\r\n\r\n### Taskと Goroutine\r\n- TaskとGoroutineを用いることで、Schedulerにtaskをpostすることができる\r\n- GoroutineはTaskから変換することができる\r\n\r\n```C++\r\nvoid spown_task(cfn::Task\u003c\u003e \u0026\u0026 task){\r\n    global_scheduler.post([g = std::make_shared\u003ccfn::Goroutine\u003e(std::move(task))]{\r\n        g-\u003eexecute();\r\n    });\r\n}\r\n\r\ncfn::Task\u003c\u003e example1_1(){\r\n    std::cout\u003c\u003c1\u003c\u003cstd::endl;\r\n    co_return;\r\n}\r\nvoid example1(){\r\n    spown_task(example1_1());\r\n    spown_task([]()-\u003ecfn::Task\u003c\u003e{\r\n        std::cout\u003c\u003c2\u003c\u003cstd::endl;\r\n        co_return;\r\n    }());\r\n}\r\n\r\n```\r\n\r\n### TaskとChannnelによる非同期処理\r\n\r\n- TaskとChannelを用いることで非同期処理を行うことができる\r\n\r\n```C++\r\ncfn::Task\u003c\u003e fibonacci_n(int n, std::shared_ptr\u003ccfn::Sender\u003cMyStrategy,int\u003e\u003e ch){\r\n    int x=0;\r\n    int y=1;\r\n    for(int i=0;i\u003cn;++i){\r\n        co_await ch-\u003esend(x);\r\n        int next = x+y;\r\n        x=y;\r\n        y=next;\r\n    }\r\n}\r\ncfn::Task\u003c\u003e example2(){\r\n    auto [sender,recver] = cfn::makeChannel\u003cMyStrategy,int\u003e(MyStrategy{},0);\r\n    spown_task(fibonacci_n(10, std::move(sender)));\r\n    for(;;){\r\n        auto ret = co_await recver-\u003erecv();\r\n        if(!ret) break;\r\n        std::cout\u003c\u003c*ret\u003c\u003cstd::endl;\r\n    }\r\n}\r\n\r\n```\r\n\r\n### select\r\n\r\nselectを用いて複数のChannelについて待ち処理を行うことができる\r\n\r\n```C++\r\ncfn::Task\u003c\u003e fibonacci_seq(std::shared_ptr\u003ccfn::Sender\u003cMyStrategy,int\u003e\u003e ret_ch, std::shared_ptr\u003ccfn::Recver\u003cMyStrategy,int\u003e\u003e quit_ch){\r\n    int x=0;\r\n    int y=1;\r\n    for(;;){\r\n        auto [ret, quit] = co_await select(ret_ch-\u003esend(x), quit_ch-\u003erecv());\r\n        if(ret){\r\n            int next = x+y;\r\n            x=y;\r\n            y=next;\r\n        }else if(quit){\r\n            break;\r\n        }\r\n    }\r\n}\r\ncfn::Task\u003c\u003e example3(){\r\n    auto [ret_send,ret_recv] = cfn::makeChannel\u003cMyStrategy,int\u003e(MyStrategy{},0);\r\n    auto [quit_send,quit_recv] = cfn::makeChannel\u003cMyStrategy,int\u003e(MyStrategy{},0);\r\n    spown_task(fibonacci_seq(std::move(ret_send), std::move(quit_recv)));\r\n    for(int i=0;i\u003c10;++i){\r\n        auto x = co_await ret_recv-\u003erecv();\r\n        std::cout\u003c\u003c*x\u003c\u003cstd::endl;\r\n    }\r\n    quit_send.reset(); // 明示的にclose\r\n}\r\n\r\n```\r\n\r\n\r\n## usage\r\n\r\n### Task\u003cT\u003e\r\n\r\n```C++\r\ntemplate \u003cclass value_type = void\u003e \r\nclass Task {\r\npublic:\r\n  auto operator co_await();\r\n  using promise_type = \u003cunspecified\u003e;\r\n};\r\n\r\n```\r\n\r\n`Task` は コルーチンによる非同期実行をサポートするクラスである\r\n\r\n1. `co_await チャンネル` のように記述することで非同期処理を行うことができる\r\n2. `co_return` を用いて 型`T`の値をコルーチンから返すことができる\r\n3. `Task` の `co_await`に`Task\u003cT\u003e`を渡すことで、`co_return` の結果を受け取ることができる\r\n4. `Goroutine`に渡すことで実行できるようになる\r\n\r\n```C++\r\n// 1\r\nauto [sender, recver] = cfn::makeChannel\u003cMyAppStrategy, int\u003e(MyAppStrategy{gbts}, 0);\r\nauto t = \r\n    [](auto sender) -\u003e cfn::Task\u003c\u003e {\r\n        for(int i=0;i\u003c10;++i){\r\n            co_await sender-\u003esend(i);\r\n        }\r\n    }(sender);\r\n```\r\n\r\n```C++\r\n// 2,3\r\ncfn::Task\u003cint\u003e task2(){\r\n    co_return 42;\r\n};\r\ncfn::Task\u003c\u003e task1(){\r\n    auto n = co_await task2();\r\n    std::cout\u003c\u003cn\u003c\u003cstd::endl;\r\n};\r\n\r\n```\r\n\r\n### makeChannel()\r\n\r\n\r\n```C++\r\ntemplate \u003cclass T\u003e concept ChannelStrategy = requires(T strategy) {\r\n  // - require: thread safe\r\n  strategy.post_goroutine(std::declval\u003cstd::shared_ptr\u003cGoroutine\u003e\u003e());\r\n};\r\n\r\ntemplate \u003cChannelStrategy Strategy, class value_type\u003e \r\nclass BasicChannel{\r\npublic:\r\n    template \u003cclass T\u003e SendAwaiter send(T \u0026\u0026 val);\r\n    RecvAwaiter send();\r\n    void close();\r\n};\r\n\r\ntemplate \u003cChannelStrategy Strategy, class value_type\u003e \r\nclass Sender {\r\npublic:\r\n  template \u003cclass T\u003e auto send(T \u0026\u0026val);\r\n};\r\ntemplate \u003cChannelStrategy Strategy, class value_type\u003e \r\nclass Recver {\r\npublic:\r\n  auto recv();\r\n};\r\n\r\ntemplate \u003cChannelStrategy Strategy, class value_type\u003e\r\nstd::tuple\u003cstd::shared_ptr\u003cSender\u003cStrategy, value_type\u003e\u003e,\r\n           std::shared_ptr\u003cRecver\u003cStrategy, value_type\u003e\u003e\u003e\r\nmakeChannel(Strategy strategy, std::size_t n);\r\n```\r\n\r\nチャンネルは非同期通信をサポートする\r\n\r\n- makeChannelは queue size nのチャンネルを生成し、それへの参照をもつ SenderとRecverを返す\r\n- Senderはデストラクト時にChannelをcloseする\r\n- Channelを使用するにはChannelStrategy を定義する必要がある。詳しくは 準備 の項を参照\r\n\r\n\r\n```C++\r\nauto [sender, recver] = cfn::makeChannel\u003cMyStrategy, int\u003e(my_strategy, 0);\r\nauto t1 = \r\n    [](auto sender) -\u003e cfn::Task\u003c\u003e {\r\n        for(int i=0;i\u003c10;++i){\r\n            co_await sender-\u003esend(i);\r\n        }\r\n    }(sender);\r\n        \r\nauto t2 = \r\n    [\u0026](auto recver) -\u003e cfn::Task\u003c\u003e {\r\n        for(;;){\r\n            auto r = co_await recver-\u003erecv();\r\n            if(!r)break;\r\n            tmp.push_back(*r);\r\n        }\r\n    }(recver);\r\n```\r\n\r\n\r\n\r\n### select()\r\n\r\nselectは複数のchannel.send()/recv()を受け取る。いずれかのchannelで値取得可能になり次第、そのchannelから値を取得する\r\n\r\n```C++\r\nauto [done, ret] = \r\n    co_await cfn::select(done_ch-\u003erecv(), ch-\u003erecv());\r\nif(done){\r\n    break;\r\n}else if(ret){\r\n    std::cout\u003c\u003c**ret\u003c\u003cstd::endl;\r\n}\r\n```\r\n\r\n### try_select()\r\n\r\ntry_selectはselectのnon block版である。  \r\nselectでは実行可能な処理がない場合co_awaitで待機するが、try_selectでは待機せず即座に値を返す\r\n\r\n```C++\r\nauto [done, ret] = \r\n    cfn::try_select(done_ch-\u003erecv(), ch-\u003erecv());\r\nif(done){\r\n    break;\r\n}else if(ret){\r\n    std::cout\u003c\u003c**ret\u003c\u003cstd::endl;\r\n}else{\r\n    std::cout\u003c\u003c\"none\"\u003c\u003cstd::endl;\r\n}\r\n```\r\n\r\n### Goroutine\r\n\r\n`Goroutine` は 以下の役割を持つ\r\n- TaskでChannelを使えるようにする\r\n    - TaskをSchedulerで使えるようにする\r\n\r\n```C++\r\nstruct Goroutine {\r\n  Goroutine(Task\u003c\u003e \u0026\u0026t);\r\n  void execute();\r\n};\r\n```\r\n\r\n### concept ChannelStrategy  \r\n\r\n```C++\r\ntemplate \u003cclass T\u003e concept ChannelStrategy = requires(T strategy) {\r\n  strategy.post_goroutine(std::declval\u003cstd::shared_ptr\u003cGoroutine\u003e\u003e());\r\n};\r\n```\r\n\r\n- `void post_goroutine(cfn::Goroutine \u0026\u0026)`  を実装する必要がある\r\n    - この関数は Channelで blockされた GoroutineをSchedulerにpostするために使う\r\n    - 複数threadでChannnelを使用する場合は、`post_goroutine` はthread safeで無ければならない\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuyutsubaki%2Fcoffin-goroutine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuyutsubaki%2Fcoffin-goroutine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuyutsubaki%2Fcoffin-goroutine/lists"}