Login
Immutable PageDiscussionInfoAttachments

Your search query "linkto:"clear/wm_devel/2013-03-02"" didn't return any results. Please change some terms and refer to HelpOnSearching for more information.
(!) Consider performing a full-text search with your search terms.

Clear message
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         if (xcb_connection_has_error(conn)) {
  12             Xサーバとの接続でエラー発生。終了
  13         }
  14         /* 出力バッファをflushしてリクエストをXサーバに送信する
  15            ここでやっておくと、あちこちでxcb_flushしなくても良くなる */
  16         xcb_flush(conn);
  17         /* 何か来るまで待つ */
  18         FD_SET(xfd, &fd);
  19         if (select(xfd + 1, &fd, NULL, NULL, NULL) < 0) {
  20             if (errno == EINTR) {
  21                 /* Linuxのselect(2)のmanによるとエラー発生時のfdの中身は未定義になるらしい
  22                    FreeBSDではfdは変更されないとmanに書いてあった */
  23                 FD_ZERO(&fd);
  24             } else {
  25                 selectに失敗。エラーを出力して終了
  26             }
  27         }
  28     }
  29 }

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

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

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