サイズ: 2652
コメント:
|
サイズ: 2750
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 43: | 行 43: |
/* select(2)によるとエラー発生時のfdの中身は未定義になるらしい */ | /* Linuxのselect(2)のmanによるとエラー発生時のfdの中身は未定義になるらしい FreeBSDではfdは変更されないとmanに書いてあった */ |
イベントループの改良
これまで以下のようにイベントループを書いていた。
1 volatile sig_atomic_t running = 1; /* 実行継続フラグ */
2 xcb_connection_t *conn; /* Xサーバとの接続 */
3
4 /* ... */
5
6 /* SIGTERMハンドラ */
7 void handle_sigterm(int sig)
8 {
9 running = 0;
10 }
11
12 /* ... */
13
14 /* イベントループ */
15 xcb_generic_event_t *event;
16 while (running && (event = xcb_wait_for_event(conn))) {
17 /* イベント処理(とエラーハンドリング)を行う */
18 }
xcb_wait_for_eventは、イベントが無い場合は来るまでブロックし続ける。そのため、WMがkillされて継続フラグが0になった場合でも、何かイベントが来ない限りそこで止まってしまい、WMがすぐに終了しないという問題がある。
そこで、xcb_poll_for_eventを用いてイベントループを次のように書き直した。
1 /* イベントループ */
2 xcb_generic_event_t *event;
3 int xfd = xcb_get_file_descriptor(conn);
4 fd_set fd;
5 FD_ZERO(&fd);
6 while (running) {
7 if ((event = xcb_poll_for_event(conn))) {
8 イベント処理(とエラーハンドリング)を行う
9 } else {
10 /* 何か来るまで待つ */
11 FD_SET(xfd, &fd);
12 if (select(xfd + 1, &fd, NULL, NULL, NULL) < 0) {
13 if (errno == EINTR) {
14 /* Linuxのselect(2)のmanによるとエラー発生時のfdの中身は未定義になるらしい
15 FreeBSDではfdは変更されないとmanに書いてあった */
16 FD_ZERO(&fd);
17 } else {
18 selectに失敗。エラーを出力して終了
19 }
20 }
21 }
22 }
xcb_poll_for_eventは、イベントが無い場合即座にNULLを返すので、killされた場合もループがすぐ回って即座に終了するようになる。しかし、これだけだとイベントが届かない限り全力でループが回り続けるので、CPU資源を食いつぶしてしまう。そこで、イベントが無かった場合はselectでXサーバから何かがやってくるのを待つ。select中に何らかのシグナルを受け取った場合(killされたときはSIGTERM)はselectが-1を返し、errnoがEINTRとなるので、イベントループの実行を続ける1。そうでない場合は本当にselectが失敗しているのでエラーを吐いて終了する。
awesomeやi3(共にXCBで書かれている)は、libevを使ってイベントループを実装している。
シグナルはSIGTERMだけとは限らない (1)