同期中にデッドロックするという報告をもらったのでソースをつらつらと眺めて考えてみたところ、どうも怪しそうなところがあったので修正しました。
怪しそうというのはひどく単純にするとこんな感じです。
CRITICAL_SECTION cs;
// この関数はUIスレッドでないスレッドから呼ばれる
void foo() {
::EnterCriticalSection(&cs);
// 変数を更新
::InvalidateRect(hwnd, 0);
::LeaveCriticalSection(&cs);
}
LRESULT windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT:
// いろいろ準備
::EnterCriticalSection(&cs);
// 変数を使って描画
::LeaveCriticalSection(&cs);
return 0;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
つまり、InvalidateRectとWM_PAINTを呼び出す側(OS)が何らかのロックを共有しているとすると、foo()内ではユーザ定義のクリティカルセクション->何らかのロックという順序でロックし、WM_PAINTの処理内では何らかのロック->ユーザ定義のクリティカルセクションの順序でロックするのでデッドロックする可能性があるということです。
今回は、InvalidateRectをクリティカルセクションの外側で呼ぶようにして対処してみました。
InvalidateRectはUIスレッド以外から呼び出しても構わない*1ようですが、更新リージョンの更新をしているはずなので何らかのロックは使っていてもおかしくないですね。
*1 Window関係の関数はUIスレッド以外からは呼び出してはいけないものが多い