2.6 – コルーチン

Luaは、協調的マルチスレッディングとも呼ばれるコルーチンをサポートする。Luaのコルーチンは、独立した実行スレッドを表す。ただし、マルチスレッドシステムのスレッドとは異なり、コルーチンはyield関数を明示的に呼び出した場合にだけ実行を中断する。

コルーチンは、coroutine.createを呼び出して作成する。唯一の引数は、コルーチンのメイン関数となる関数である。create関数は新しいコルーチンを作成し、そのハンドル(thread型のオブジェクト)を返すだけで、コルーチンを開始しない。

コルーチンは、coroutine.resumeを呼び出して実行する。coroutine.createが返したスレッドを最初の引数としてcoroutine.resumeを初めて呼び出すと、コルーチンはそのメイン関数を呼び出して実行を開始する。coroutine.resumeへ渡した追加の引数は、その関数の引数として渡される。コルーチンは、実行開始後、終了するかyieldするまで動作する。

コルーチンの実行は2つの方法で終了する。メイン関数が(明示的に、または最後の命令の後に暗黙に)戻る正常終了と、保護されていないエラーによる異常終了である。正常終了の場合、coroutine.resumetrueに続けて、コルーチンのメイン関数が返したすべての値を返す。エラーの場合、coroutine.resumefalseに続けてエラーオブジェクトを返す。この場合、コルーチンはスタックを巻き戻さないため、エラー発生後にデバッグAPIでスタックを調べられる。

コルーチンは、coroutine.yieldを呼び出すことでyieldする。コルーチンがyieldすると、対応するcoroutine.resumeは直ちに戻る。これは、ネストした関数呼び出しの内部(すなわちメイン関数ではなく、メイン関数が直接または間接的に呼び出した関数)でyieldした場合も同じである。yieldの場合、coroutine.resumetrueに続けて、coroutine.yieldへ渡されたすべての値も返す。同じコルーチンを次に再開すると、yieldした位置から実行を続行し、coroutine.yieldの呼び出しはcoroutine.resumeへ渡された追加の引数をすべて返す。

coroutine.createと同様に、coroutine.wrap関数もコルーチンを作成する。ただし、コルーチン自体を返す代わりに、呼び出すとそのコルーチンを再開する関数を返す。この関数へ渡した引数は、coroutine.resumeへの追加引数となる。coroutine.wrapは、coroutine.resumeが返すすべての値から、最初の値(ブール値のエラーコード)を除いて返す。coroutine.resumeとは異なり、coroutine.wrapが作成した関数は、すべてのエラーを呼び出し元へ伝播する。この場合、その関数はコルーチンも閉じる(coroutine.closeを参照)。

コルーチンの動作例として、次のコードを考える。

     function foo (a)
       print("foo", a)
       return coroutine.yield(2*a)
     end
     
     co = coroutine.create(function (a,b)
           print("co-body", a, b)
           local r = foo(a+1)
           print("co-body", r)
           local r, s = coroutine.yield(a+b, a-b)
           print("co-body", r, s)
           return b, "end"
     end)
     
     print("main", coroutine.resume(co, 1, 10))
     print("main", coroutine.resume(co, "r"))
     print("main", coroutine.resume(co, "x", "y"))
     print("main", coroutine.resume(co, "x", "y"))

これを実行すると、次の出力が生成される。

     co-body 1       10
     foo     2
     main    true    4
     co-body r
     main    true    11      -9
     co-body x       y
     main    true    10      end
     main    false   cannot resume dead coroutine

C APIからコルーチンを作成し操作することもできる。lua_newthreadlua_resumelua_yieldの各関数を参照すること。