Chiharu の日記

絵描き C/C++ プログラマーの日記です。

X-Window でマルチスレッド 〜X11 API (xlib) プログラミング メモ

『Parallel force - パラレル・フォース』のウィンドウ処理とバックバッファの転送処理を X-Window 環境に移植したのですが、X11 API (xlib) のマルチスレッドでどハマリして、解決に結構時間を使いました。以下、解決メモです。

X11 API (xlib) のスレッド対応

X11 API (xlib) をスレッド セーフに動作させるには、プログラムの冒頭で XInitThreads API を呼び出す必要があります。これは他のすべての X11 API (xlib) 呼び出しの前に実施します。

// X11 API をスレッド セーフに
int main()
{
 // 他の X11 API 呼び出しよりも前に
 XInitThreads();

 // メイン処理
 // ...
}

この処理を行わない場合、同一コンテキストに対して異なるスレッドからの API 呼び出しをするとプログラムが高確率で異常終了します。(実際にしました)

XNextEvent API をスレッド セーフに使う

XInitThreads API を使用しても、XNextEvent API はスレッド セーフに動作しないみたいです。例えば当該 API でイベント待機中に、別スレッドで XPutImage API を連呼すると、高確率で XPutImage API がフリーズする現象に遭遇しました。

// イベント ループ (改善前)
for (;;) {
 XNextEvent(aDisplay, &aEvent); // これがスレッドセーフでないっぽい
 switch (aEvent.type) { 
 // イベント応答
 }
}

この問題は XNextEvent API を XLockDisplay, XUnlockDisplay API でロックすることで対処できました。

// イベント ループ (改善後)
for (;;) {
 while (XPending(aDisplay)) {
  // XNextEvent をマニュアル ロック!
  XLockDisplay(aDisplay);
  XNextEvent(aDisplay, &aEvent); // ロックすれば大丈夫みたい
  XUnlockDisplay(aDisplay);
  switch (aEvent.type) { 
  // イベント応答
  }
 }
 // ここで 10ms 程度のスリープ
}

ここまで、とりあえず対症療法的に対応したのですが、ちゃんと根本対策になっているんだろうか。ちょっと不安です。