An open API service indexing awesome lists of open source software.

https://github.com/fuyutsubaki/coffin-goroutine

single header library for supporting goroutine-like concurrency in C++
https://github.com/fuyutsubaki/coffin-goroutine

c-plus-plus concurrency cpp20 header-only single-header-lib

Last synced: 10 months ago
JSON representation

single header library for supporting goroutine-like concurrency in C++

Awesome Lists containing this project

README

          

# coffin/goroutine

"coffin/goroutine" は goroutine/channelのような非同期実行をサポートする シングルヘッダライブラリである

## チュートリアル

チュートリアルで説明するすべてのコードは、下のリンクからブラウザで試すことができる
https://wandbox.org/permlink/3LBaAqOACoKCTmB3

### 準備

#### 1. Scheduler
もしあなたのアプリケーションがSchedulerを持っていない場合、Schedulerを用意する必要がある
- coffin/goroutineはSchedulerを含んでいない。これはアプリケーションにすでにSchedulerが存在する場合、それと共存するためである

```C++
// Scheduler
struct MyScheduler {
boost::asio::io_service io_service_;
std::shared_ptr work_;
void post(std::function && f){
io_service_.post(std::move(f));
}

void run(std::size_t n){
work_ = std::make_shared(boost::asio::io_service::work(io_service_));
std::vector thread_list;
for (std::size_t i = 0; i < n; ++i) {
thread_list.push_back(std::thread{[&]{io_service_.run();}});
}
for(auto&th:thread_list)
th.join();

io_service_.reset();
}

void stop(){
work_.reset();
}
};

static inline MyScheduler global_scheduler;
```

#### 2. ChannelStrategy
1のSchedulerとChannnelをつなぐ、 `ChannelStrategy` を定義する必要がある

するべきことは、渡された goroutineを一度だけ実行する処理をScheduler に postする関数`post_goroutine` を定義することである

```C++
struct MyStrategy{
void post_goroutine(std::shared_ptr && g){
global_scheduler.post([=]()mutable{g->execute();});
}
};
```

### Taskと Goroutine
- TaskとGoroutineを用いることで、Schedulerにtaskをpostすることができる
- GoroutineはTaskから変換することができる

```C++
void spown_task(cfn::Task<> && task){
global_scheduler.post([g = std::make_shared(std::move(task))]{
g->execute();
});
}

cfn::Task<> example1_1(){
std::cout<<1<cfn::Task<>{
std::cout<<2< fibonacci_n(int n, std::shared_ptr> ch){
int x=0;
int y=1;
for(int i=0;isend(x);
int next = x+y;
x=y;
y=next;
}
}
cfn::Task<> example2(){
auto [sender,recver] = cfn::makeChannel(MyStrategy{},0);
spown_task(fibonacci_n(10, std::move(sender)));
for(;;){
auto ret = co_await recver->recv();
if(!ret) break;
std::cout<<*ret< fibonacci_seq(std::shared_ptr> ret_ch, std::shared_ptr> quit_ch){
int x=0;
int y=1;
for(;;){
auto [ret, quit] = co_await select(ret_ch->send(x), quit_ch->recv());
if(ret){
int next = x+y;
x=y;
y=next;
}else if(quit){
break;
}
}
}
cfn::Task<> example3(){
auto [ret_send,ret_recv] = cfn::makeChannel(MyStrategy{},0);
auto [quit_send,quit_recv] = cfn::makeChannel(MyStrategy{},0);
spown_task(fibonacci_seq(std::move(ret_send), std::move(quit_recv)));
for(int i=0;i<10;++i){
auto x = co_await ret_recv->recv();
std::cout<<*x<

```C++
template
class Task {
public:
auto operator co_await();
using promise_type = ;
};

```

`Task` は コルーチンによる非同期実行をサポートするクラスである

1. `co_await チャンネル` のように記述することで非同期処理を行うことができる
2. `co_return` を用いて 型`T`の値をコルーチンから返すことができる
3. `Task` の `co_await`に`Task`を渡すことで、`co_return` の結果を受け取ることができる
4. `Goroutine`に渡すことで実行できるようになる

```C++
// 1
auto [sender, recver] = cfn::makeChannel(MyAppStrategy{gbts}, 0);
auto t =
[](auto sender) -> cfn::Task<> {
for(int i=0;i<10;++i){
co_await sender->send(i);
}
}(sender);
```

```C++
// 2,3
cfn::Task task2(){
co_return 42;
};
cfn::Task<> task1(){
auto n = co_await task2();
std::cout< concept ChannelStrategy = requires(T strategy) {
// - require: thread safe
strategy.post_goroutine(std::declval>());
};

template
class BasicChannel{
public:
template SendAwaiter send(T && val);
RecvAwaiter send();
void close();
};

template
class Sender {
public:
template auto send(T &&val);
};
template
class Recver {
public:
auto recv();
};

template
std::tuple>,
std::shared_ptr>>
makeChannel(Strategy strategy, std::size_t n);
```

チャンネルは非同期通信をサポートする

- makeChannelは queue size nのチャンネルを生成し、それへの参照をもつ SenderとRecverを返す
- Senderはデストラクト時にChannelをcloseする
- Channelを使用するにはChannelStrategy を定義する必要がある。詳しくは 準備 の項を参照

```C++
auto [sender, recver] = cfn::makeChannel(my_strategy, 0);
auto t1 =
[](auto sender) -> cfn::Task<> {
for(int i=0;i<10;++i){
co_await sender->send(i);
}
}(sender);

auto t2 =
[&](auto recver) -> cfn::Task<> {
for(;;){
auto r = co_await recver->recv();
if(!r)break;
tmp.push_back(*r);
}
}(recver);
```

### select()

selectは複数のchannel.send()/recv()を受け取る。いずれかのchannelで値取得可能になり次第、そのchannelから値を取得する

```C++
auto [done, ret] =
co_await cfn::select(done_ch->recv(), ch->recv());
if(done){
break;
}else if(ret){
std::cout<<**ret<recv(), ch->recv());
if(done){
break;
}else if(ret){
std::cout<<**ret< &&t);
void execute();
};
```

### concept ChannelStrategy

```C++
template concept ChannelStrategy = requires(T strategy) {
strategy.post_goroutine(std::declval>());
};
```

- `void post_goroutine(cfn::Goroutine &&)` を実装する必要がある
- この関数は Channelで blockされた GoroutineをSchedulerにpostするために使う
- 複数threadでChannnelを使用する場合は、`post_goroutine` はthread safeで無ければならない