非同期 I/O
nmoshの非同期I/O APIにModern IO APIという名前を振った。非同期 I/Oでaioにすると検索性が悪いのと、非同期 I/Oを基本とするけど同期I/Oも混ぜて書けるので。(アイデアとしてはGround Central Dispatchからパクっている)
プロセスやI/Oルーチンの起動は非常にパラメタが多いので、Rubyのspawnと同様にキーワードを取って動作をカスタマイズできるプリミティブと、それらをwrapする便利なイディオム手続きの組み合わせという形にする。
(launch! (exec "ls" "-al") (env-add '("LANG" . "C")) (chdir "/home/oku") (stdout/bin (^[buf offset count] ...)) (finish (^[status] ...))
execやenv-addがキーワードに相当する。launch!はcaseのような構文で、これらキーワードはquote不要。(^ はlambdaの別名。これはGauche由来。)
残念ながらstdoutやstdinにはモードがある(テキストモード/バイナリモード)。バイナリの場合、↑のように3引数の手続きを与えると、プロセスがなにかを出力するたびにcallbackされる。
結果をまとめて(同期的に)うけとるには、finishに指定する。
(launch! (exec "ls" "-al") (env-add '("LANG" . "C")) (chdir "/home/oku") ((finish stdout/bin) (^[status out] ...))
launch!構文でのキーワードは、identifier的にはマッチされない(= renameできない)。キーワード部に変数を書くことは出来ないので依然launch!構文はhygenicではある。
launch!構文はprocess以外にtcp-serverのようなI/Oノードも用意する予定。
(listen-tcp! serv ;; ← 1つめの引数で、制御用のハンドル名を指定できる (port "http") (bind "0.0.0.0" "::") (accept (^[socket name] ...)))
制御用ハンドルは、動作中のI/Oノードを内部から制御するときに使用する。これは、↓の糖衣構文。
(letrec ((serv (listen-tcp! (port ...) ...))))
Default Queueと同期I/O
Modern I/O APIでは、スレッド1つにつきひとつのDefault Queueを自動的に確保する。このアイデアもGCD由来で、通常のシチュエーションでは複数のQueueを使うことは殆どないことを利用している。
同期、非同期にかかわらず、ModernIOによって発行されるI/OリクエストはすべてこのDefault Queueにsubmitされる。よって、同期I/Oは、発行したI/Oオペレーションそのものを待つのではなく、Default Queueを待ちつつ当該オペレーションが終わるまで単にループすれば良いことになる。
将来的には、Schemeのportもここの同期I/Oに統合したいと考えている。