コルーチン
Boost.Coroutine2は、非対称コルーチンを提供します。
値のシーケンスを生成する実装は、通常、非対称コルーチンを使っています5。
スタックフル
コルーチンの各インスタンスはそれ自身のスタックを持っています。
スタックレスコルーチンに対して、スタックフルコルーチンは任意の部分スタックフレームで停止命令を呼び出すことができ、脱出・再入を再帰命令で行うことが可能になります。
ムーブ・オンリー
コルーチンはムーブだけが可能です。
もしコピー可能なら、スタックもそこで割り当てられたすべてのオブジェクトとともにコピーする必要があります。もしRAIIクラス(RAIIパターンで資源を管理しているクラス)があれば、これは未定義の動作を引き起こすでしょう。コルーチンの1つ目のコピーが終了(そのスタックを巻き戻します)とき、RAIIクラスのデストラクターがそれが管理する資源を開放するでしょう。2つ目のコピーが終了するとき、同じデストラクターが同じ資源を二重開放してしまい、未定義動作へと繋がるのです。
掃除
コルーチンの破棄において、関連付けられたスタックは巻き戻されます。
コルーチンのコンストラクターには、カスタマイズされたスタックアロケーターを渡すことが出来ます。スタックアロケーターは、スタックのメモリー割り当てを解除するも、今後の使用のためにキャッシュする(後に作られるコルーチンのためです)も自由です。
セグメント化スタック(segmented stack)
coroutine<>::push_type
とcoroutine<>::pull_type
は、セグメント化スタック(必要に応じて成長します)をサポートしています。
いつでも必要なスタックサイズを正確に見積もれるわけではありません。多くの場合、あまりも多くのメモリーが割り当てられています(そして、仮想アドレス空間を無駄にします)。
生成時において、コルーチンはデフォルトの(最小の)スタックサイズで始まります。この最小のスタックサイズは、最大のページサイズかつ、シグナルスタックの標準的なサイズです(POSIXにおけるSIGSTKSZマクロ)。
執筆時においてはGCC(4.7)6だけがセグメント化スタックをサポートしていると知られています。Boost.Coroutine2はバージョン1.54で、セグメント化スタックのサポートを提供します。
デストラクターは関連付けられたスタックを開放します。スタックのメモリー割り当てを解除するも、今後の使用のためにキャッシュするも実装者の自由です。
コンテキストスイッチ
コルーチンは、各コンテキストスイッチにおいて、基礎となるABIに従ってレジスタを保存したり復元したりします(Boost.Contextを使っています)。
浮動小数点数を使わないアプリケーションは、性能上の理由により、FPUレジスタの保存を無効化することが出来ます。
注釈
呼出規約に従って、FPUレジスタはデフォルトで保存されます。
POSIXシステムにおいては、性能上の理由により、コルーチンのコンテキストスイッチはシグナルマスクを保存しません。
コンテキストスイッチは、coroutine<>::push_type::operator()
とcoroutine<>::pull_type::operator()
を介して行われます。
警告
coroutine<>::push_type::operator()
とcoroutine<>::pull_type::operator()
の内部からの同じコルーチンの呼び出しは、未定義動作をもたらします。
例として、以下のコードは未定義動作をもたらします。
boost::coroutines2::coroutine<void>::push_type coro(
[&](boost::coroutines2::coroutine<void>::pull_type& yield){
coro();
});
coro();
5. Moura, Ana Lucia De and Ierusalimschy, Roberto. "Revisiting coroutines". ACM Trans. Program. Lang. Syst., Volume 31 Issue 2, February 2009, Article No. 6 ↩
6. Ian Lance Taylor, Split Stacks in GCC ↩