ログイン
編集不可のページディスカッション情報添付ファイル

2013-03-02 17:01:55時点のリビジョン1

メッセージを消す
clear/wm_devel/2013-03-02

MMA

イベントループの改良

関連: clear/wm_devel/2012-11-12

これまで以下のようにイベントループを書いていた。

   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                 selectに失敗。エラーを出力して終了
  15             } else {
  16                 /* select(2)によるとエラー発生時のfdの中身は未定義になるらしい */
  17                 FD_ZERO(&fd);
  18             }
  19         }
  20     }
  21 }

xcb_poll_for_eventは、イベントが無い場合即座にNULLを返すので、killされた場合もループがすぐ回って即座に終了するようになる。しかし、これだけだとイベントが届かない限り全力でループが回り続けるので、CPU資源を食いつぶしてしまう。そこで、イベントが無かった場合はselectでXサーバから何かがやってくるのを待つ。select中に何らかのシグナルを受け取った場合(killされたときはSIGTERM)はselectが-1を返し、errnoEINTRとなるので、イベントループの実行を続ける1。そうでない場合は本当にselectが失敗しているのでエラーを吐いて終了する。

awesomeやi3(共にXCBで書かれている)は、libevを使ってイベントループを実装している。

  1. シグナルはSIGTERMだけとは限らない (1)