以前、Luaのコルーチンを紹介しました。
このコルーチンはマイクロスレッドやファイバーという名前で呼ばれることもあります。
C++がどんなに変態的なプログラムができたとしても、コルーチンは無理!と思ってたのですが、
CreateFiberという関数があるという話を聞いたので使ってみました。
#include "windows.h"
/* サブのファイバ用関数 */
VOID WINAPI SubFiber(void *mainfiber){
/* 処理 */
printf("衝撃のファーストブリット!!\n");
SwitchToFiber(mainfiber);
printf("撃滅のセカンドブリット!!\n");
SwitchToFiber(mainfiber);
printf("抹殺のラストブリット!!\n");
SwitchToFiber(mainfiber);
printf("終わり!\n");
}
/* メインのファイバ用関数 */
DWORD WINAPI MainFiber(void *){
/* このスレッドをファイバに変換 */
LPVOID pMainFiber = ConvertThreadToFiber(NULL);
/* サブのファイバ作成 */
LPVOID subFiber = CreateFiber(0, SubFiber, pMainFiber);
printf("MainFiber:Start\n");
for (;;){
SwitchToFiber(subFiber);
Sleep(1000);
}
printf("MainFiber:End\n");
return(0);
}
/* メイン関数 */
int main(int argc, char *argv[]){
HANDLE hThread;
/* メインのファイバ用スレッド作成 */
hThread = CreateThread(NULL, 0, MainFiber, NULL, 0, NULL);
/* スレッドの終了まで待機 */
WaitForSingleObject(hThread, INFINITE);
/* 後処理 */
CloseHandle(hThread);
return(0);
}
<実行結果> MainFiber:Start 衝撃のファーストブリット!! 撃滅のセカンドブリット!! 抹殺のラストブリット!! 終わり! |
SwitchToFiberがいわゆるyieldなんですが…なんと、コルーチンのアドレスを必要とします。
Switchという名の通り、引数として渡したコルーチンに実行が移動します。
Luaのコルーチンは呼び出した関数に戻るわけですが…。
つまり、言語がスタックに置いて管理しているはずのリターンアドレスを自分で指定しているわけです。
そして、関数が終了すると…関数を受け持つスレッドが終了してしまいます。
ということは、リターンアドレスを使うことは一度もないです。
もしかしたら、リターンアドレスは破壊されていて、使えないのかもしれません。
関数の終了でスレッドそのものが終了、ということは、
もう使わなくなったファイバーはどうすればいいんでしょう。
一応、DestoryFiberという関数があるにはあるにはありますが、
自分自身で壊すわけにはいかないので、誰かに壊してもらう必要があるわけです。
実行結果を見てもらえばわかりますが、MainFiber:Endが出力されていません。
無限ループから脱出する術がないので出るわけがないのですが…。
同時にもうひとつ問題が起きています。
実は、MainFiber関数は処理としては終わらないまま、プログラムそのものが終了しています。
つまり、強制的に中断されているわけで、関数のデストラクタに当たるものが実行されてないのです。
MainFiberにクラスを実体宣言した場合、そのクラスのデストラクタは呼ばれません。
最後に実行され、コルーチンのスレッドが終了する契機になる関数以外では
RAIIを利用したプログラムはかけないことになります。
正直、CreateFiberは面白い関数ではありますが、実用的ではないでしょう。
予想通りというか、残念な結果です。