Open4

miniio: POSIX移植

okuokuokuoku

API検討のためにPOSIXに移植する。ちょっと難しいのは、ポータブルで非同期な名前解決手法が存在しないことで、このためだけにワーカースレッドを作成する必要がある。

非同期な名前解決はOS固有の手法が提供されていることが多い。

今回はこれらは考慮しない。というかPOSIXのsocketなんだからpthreadくらい有るだろって事で。。今回はBSD"風"ソケットAPIに対するポータビリティは考慮しないことにする。

okuokuokuoku

POSIX仕様を集める

一旦miniioのコア仕様からプロセスは外そうと思っているので、いわゆるPOSIX準拠組込みOSにも対応できる。

poll の対応状況

常識的なシステムでは poll が共通のwaitとして select の代わりに利用できる。組込みOSの対応状況は:

... 打率低いな。。

okuokuokuoku

Chimeとタイマの実装

Chimeはminiioのwait API miniio_ioctx_process を他のスレッドから起床させるためのAPIで、 miniio_chime_trigger は唯一のスレッドセーフAPIとなる。(他のAPIはI/Oスレッドから呼出す必要がある)

poll の実I/Oに拠らない起床方法には2つあり、1つはシグナルでsyscallを中断させることで、もう1つは単に socketpairpipe で作成したfdに別のスレッドから書き込む(self-pipe)方法がある。

今回は1つのpipeをコンテキストの初期化時に確保しておいて、timerやchimeでは常にそれを使う方向で処理することにした。BSD系のkqueueやLinuxのtimerfd/eventfdではもっと効率的にこれらを実現できる。

複数のスレッドから同時にpipeに書き込むとデータが混ざる可能性がある。このため、self-pipeでpipeに書き込むデータは1バイトでなければならない。timerとchime(とin-flightな名前解決)の最大数は、この1バイトで識別できる数(255個)に制約されることになる。

誤解を招きそうなのでreword。。POSIXでは、pipeに纏まったサイズを書いた場合、アトミックである(メッセージの割り込みが起こらない)ことを求めている。

Write requests of {PIPE_BUF} bytes or less shall not be interleaved with data from other processes doing writes on the same pipe. Writes of greater than {PIPE_BUF} bytes may have data interleaved, on arbitrary boundaries, with writes by other processes, whether or not the O_NONBLOCK flag of the file status flags is set.

I/O is intended to be atomic to ordinary files and pipes and FIFOs. Atomic means that all the bytes from a single operation that started out together end up together, without interleaving from other I/O operations.

ただし、いっぺんに読めることは保証していない:

The standard developers considered adding atomicity requirements to a pipe or FIFO, but recognized that due to the nature of pipes and FIFOs there could be no guarantee of atomicity of reads of {PIPE_BUF} or any other size that would be an aid to applications portability.

ただし、このアトミック性は書き込みが成功したときの要件であって、writeは書き込みが "部分的に" 成功しても良いことになっている。このため、無条件にアトミック性を要求できるメッセージの最大長は1バイトになる。BSDのkeventやLinuxのeventfd、WindowsのIOCP等はポインタ長のメッセージをやりとりできるため、このような制約はself-pipingにユニークとなる。

okuokuokuoku

readとwriteへの対応

... 良いアイデアが無い。一旦ブロッキングI/Oに統一して、その場でreadなりwriteなりする実装にしてしまうか。。ただ、Cygwinのようにブロックすべきでないシチュエーションで read がブロックする https://qiita.com/okuoku/items/7a3a4944745b0424d415#o_nonblock-なpipe2がブロックする バグとか割と普通にあるのでちょっと怖いものはある。

ちなみに write ではダメで、 sendMSG_NOSIGNAL する必要がある。そうしないとTCP接続の対向が不在だった場合に SIGPIPE が来てしまう。