本実装方法以上にハマったので、きっと他にもハマる人がいるはずと覚書。いないか。
使い方概要
libevとは、こちらの方のlibev まとめがまとめられているように、FD等のイベントを受け取る部分を適切な方法を使うよう隠ぺいしてくれて、かつ高速な凄いライブラリです。
これはぜひ使いたいので、libevによる非同期プログラミングの記事を参考に実装。
- メインloop作成⇒
ev_loop_new
- イベント初期化⇒
ev_io_init
- イベント監視開始/停止⇒
ev_io_start
/ev_io_stop
- メインループの開始/停止⇒
ev_loop
/ev_unloop
なるほど、簡単だ。で、コールバックはというとサンプルを参考にするとこうなるようです。
static void (*cb) (EV_P_ ev_io *w, int revents)
なるほど~。でも引数にはev_ioしかないな、どうやってユーザーデータを設定するんだろう。
ユーザーデータを設定する
ヘッダーを見ても自分の力量ではそれっぽいのがこっちのAPIしか見つからない。
EV_API_DECL void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg) EV_THROW;
でもこれは1回こっきりでstop出来なさそうだしなと思いつつ、本家サイトのメールを見てたらありました!
ev_ioですが、マクロが多重になっていますけど定義としてはこんな感じなんですよね。
/* can be used to add custom fields to all watchers, while losing binary compatibility */
#ifndef EV_COMMON
# define EV_COMMON void *data;
#endif
typedef struct ev_io
{
...
EV_COMMON
...
int fd; /* ro */
int events; /* ro */
} ev_io;
ちゃんとEV_COMMONがカスタム用ですって書いていました。こちらに必要なユーザーデータのポインタを設定してやれば万事OK!
無事ユーザーデータも利用したイベントが発火されるようになりました。
ちなみに、この記事書きながらヘッダー見返してたら、ちゃんとユーザーデータ設定/取得APIもありました。
EV_API_DECL void ev_set_userdata (EV_P_ void *data) EV_THROW;
EV_API_DECL void *ev_userdata (EV_P) EV_THROW;
…なんかすいませんlibev開発者の皆さん。
ハマった本質は何か?
追記: @tenmyo さんからのコメントでドキュメントサポートしているはわかったのですが、たどり着き方をわかっていなくて少しもやっとしていたところOSSに貢献したいのに、まだ貢献していないあなたへ という記事を見かけました。
libevの何に自分が困ったのか?そこから何か貢献できることがないかについて考えてみました。
困っていたこと
情報がうまく探せなかった。。この一点です。
なぜこうなったのか?頭を空っぽにして内容を確認してみました。
- ドキュメントパスはどこか?
- READMEにちゃんとかいてありましたね。
Library Documentation: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod
libev本家ページからもDocumentはREADME参照とあるし、改めて読むと内容の充実度が半端ないです。
-
マルチスレッド対応
- threadで検索。「FUNCTIONS CONTROLLING EVENT LOOPS」にちゃんと
ev_default_loop
に非スレッドセーフ、ev_loop_new がスレッドセーフと書いてあります。
- threadで検索。「FUNCTIONS CONTROLLING EVENT LOOPS」にちゃんと
-
ユーザーデータをどう設定するか?
- 探し方としては「user data」, 「custom」辺りで検索か、「FUNCTIONS CONTROLLING EVENT LOOPS」項を読み込むくらいかな?
- customで検索するとコメントにもあったASSOCIATING CUSTOM DATA WITH A WATCHERがヒット。サンプル付きで分かりやすいです。ここにwatcherに
void *data
があるという旨の記載もあります。 - user dataの使用方法は「FUNCTIONS CONTROLLING EVENT LOOPS」にもev_set_userdata, ev_userdata が載っています。
内部構造には変に触れず、void *
のデータが設定/使用できる旨が伝えてあるのでこれを使えば見やすいコードが書けそうです。
- customで検索するとコメントにもあったASSOCIATING CUSTOM DATA WITH A WATCHERがヒット。サンプル付きで分かりやすいです。ここにwatcherに
- 探し方としては「user data」, 「custom」辺りで検索か、「FUNCTIONS CONTROLLING EVENT LOOPS」項を読み込むくらいかな?
こうやって見ると、サンプルいくつかとFUNCTIONS_CONTROLLING_EVENT_LOOPSを参照するだけでも利用方法がしっかりわかります。
使い方がわかってから読み直すと、綺麗で情報の追えるいい公式ドキュメントじゃないか。
なぜハマってしまったのか?
- 自分の公式ドキュメントからの情報収集が足りなかった。(力量不足)
- 強いて言うなら「FUNCTIONS CONTROLLING EVENT LOOPS」項の有用性が前面に出てきていなかった。個人的には知りたい情報は全部ここに乗っていたので。
ってところですかね。後者の解決策としては、こんな情報がドキュメントに追記されているといいのかな?
- EXAMPLE PROGRAMの最後に「See also the "FUNCTIONS CONTROLLING EVENT LOOPS" section for more defail of functions.」の一文がある。
-
ASSOCIATING CUSTOM DATA WITH A WATCHERの
void *
があるという記載のところに「which can use by "ev_set_userdata" and "ev_userdata"」といった補足がある。
これがあればさらにuserdataが扱いやすくなる気がしました。ということでlibev側への提案してみよう!
追記:
提案してから気付いた。ev_set_userdataってwathcer向けじゃなくてevent_loop向けのユーザー設定だ。APIがあるものと思い込んだせいで空目してました。当時コードも見て確認してなかったっけ、僕。めっちゃ恥ずかしい!(笑)
そうすると余計にデータを見つけにくいので、
「See also the "FUNCTIONS CONTROLLING EVENT LOOPS" section for more detail of functions.
If you want to set userdata in WATHCER, please use void * data in watcher (see "ASSOCIATING CUSTOM DATA WITH A WATCHER"」かな。
日記みたいになってきた。
参考:
本家ドキュメント
libev まとめ
libevによる非同期プログラミング
本家サイトで見つけたメール
every watcher has a "void *data" member that you can use for
this.というそのものずばりの回答でした。