『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 程度のスリープ }
ここまで、とりあえず対症療法的に対応したのですが、ちゃんと根本対策になっているんだろうか。ちょっと不安です。