miniio: POSIX移植^2
... write APIの設計を間違っていることに今さら気付いた。。まぁ後で直すことにしよう。。
今の設計だとwrite queueが必要
忘れないようにメモっておくと、今のAPI設計だとsocketにwriteできるまでデータ保持する必要があり、一旦どこかにwrite対象のデータをコピーする必要がある。
read側はライブラリ側で勝手にバッファを確保するのでこの問題は無いが、受信側が処理しきれない勢いでデータが来るとメモリが喰いつくされてしまうのでそれはそれで問題ということになる。
非fdイベント
POSIX版miniioにおける非fdイベントとは、fd以外の "waitable" オブジェクトを指す。ここでは
- タイマ(... 廃止したい)
- chime (いわゆるイベント)
- DNS lookup結果 -- POSIXのDNS参照はブロッキングしかないので
が該当する。... OS固有のインターフェースを使って良いなら、例えばLinuxはtimerfdやeventfdがあり、BSDであればkqueueの方にタイマやイベント機能がある。どちらにせよ、標準のPOSIXにはどちらもfdなインターフェースが無いため、何らかの方法でエミュレートしてやる必要がある -- そうしないと、標準のselect() APIで待ち合わせることができない。selectはfd専用なので。
前回書いた https://zenn.dev/link/comments/d1d37827e6f0bc ように、非fdイベントの処理はself-pipeによって実現する。今回は簡単のため、self-pipe内に構造体を直接流す実装とした。
If the readfds, writefds, and errorfds arguments are all null pointers and the timeout argument is a null pointer, the pselect() or select() function shall block until interrupted by a signal.
何も指定しなかった時は無限待ちになる。というかPOSIXの nfds
パラメタの記述わかりづらいな。。
The nfds argument specifies the range of descriptors to be tested. The first nfds descriptors shall be checked in each set; that is, the descriptors from zero through nfds-1 in the descriptor sets shall be examined.
nfds
には評価対象のfdの値の最大値 + 1 をセットする。どっちにせよ FD_SETSIZE
未満であることを評価する必要がある。
イベント構造体
struct pos_evobj_s {
struct pos_evobj_s* prev;
struct pos_evobj_s* next;
enum pos_evobj_type_e type;
void* userdata;
int destroyed;
};
enum pos_evnote_cmd_e {
EVNOTE_CMD_TRIGGER = 1,
EVNOTE_CMD_DESTROY
};
struct pos_evnote_s {
struct pos_evobj_s* obj;
enum pos_evnote_cmd_e cmd;
};
struct pos_evnote_s
がイベント構造体、self-pipeに書き込まれるデータとなる。イベントはトリガの他にDESTROYがある。キューにtriggerが溜まっているときに安全にdestroyするにはこの方式しかない。
オブジェクトの方にdestroyedフラグを用意するならイベントには要らない気もするが、そうするとトリガイベントが失われる可能性があり危い。
有効なイベントオブジェクトは双方向リストで連結される。上記の理由により、destroyを要求しても直ちには解放されない。