Skip to content

Commit bb9edfa

Browse files
authored
Merge pull request #88 from katahiromz/copilot/fix-crossword-generation-race-condition
Fix race conditions in crossword generation completion/cancellation
2 parents 8c82a5b + d4505f2 commit bb9edfa

7 files changed

Lines changed: 47 additions & 17 deletions

XG_CancelFromWordsDialog.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ class XG_CancelFromWordsDialog : public XG_Dialog
2828
// タイマーを解除する。
2929
::KillTimer(hwnd, uTimerID);
3030
// キャンセルしてスレッドを待つ。
31-
crossword_generation::s_canceled = true;
31+
// 生成完了していなければキャンセル
32+
if (!crossword_generation::s_generated) {
33+
crossword_generation::s_canceled = true;
34+
}
3235
::Sleep(SLEEP);
3336
}
3437

XG_CancelGenBlacksDialog.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ class XG_CancelGenBlacksDialog : public XG_Dialog
2222
::KillTimer(hwnd, uTimerID);
2323
// キャンセルしてスレッドを待つ。
2424
::EnterCriticalSection(&xg_csLock);
25-
xg_bCancelled = true;
25+
// 生成完了していなければキャンセル
26+
if (!xg_bSolved) {
27+
xg_bCancelled = true;
28+
}
2629
m_cancellation_manager.SetCompleted();
2730
::LeaveCriticalSection(&xg_csLock);
2831
// スレッドを待つ(タイムアウト付き)。

XG_CancelSmartSolveDialog.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ class XG_CancelSmartSolveDialog : public XG_Dialog
3838
::KillTimer(hwnd, uTimerID);
3939
// キャンセルしてスレッドを待つ。
4040
::EnterCriticalSection(&xg_csLock);
41-
xg_bCancelled = true;
41+
// 生成完了していなければキャンセル
42+
if (!xg_bSolved) {
43+
xg_bCancelled = true;
44+
}
4245
m_cancellation_manager.SetCompleted();
4346
::LeaveCriticalSection(&xg_csLock);
4447
// スレッドを待つ(タイムアウト付き)。
@@ -60,7 +63,10 @@ class XG_CancelSmartSolveDialog : public XG_Dialog
6063
::KillTimer(hwnd, uTimerID);
6164
// キャンセルしてスレッドを待つ。
6265
::EnterCriticalSection(&xg_csLock);
63-
xg_bCancelled = true;
66+
// 生成完了していなければキャンセル
67+
if (!xg_bSolved) {
68+
xg_bCancelled = true;
69+
}
6470
m_cancellation_manager.SetCompleted();
6571
::LeaveCriticalSection(&xg_csLock);
6672
// スレッドを待つ(タイムアウト付き)。

XG_CancelSolveDialog.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ class XG_CancelSolveDialog : public XG_Dialog
3737
::KillTimer(hwnd, uTimerID);
3838
// キャンセルしてスレッドを待つ。
3939
::EnterCriticalSection(&xg_csLock);
40-
xg_bCancelled = true;
40+
// 生成完了していなければキャンセル
41+
if (!xg_bSolved) {
42+
xg_bCancelled = true;
43+
}
4144
m_cancellation_manager.SetCompleted();
4245
::LeaveCriticalSection(&xg_csLock);
4346
// スレッドを待つ(タイムアウト付き)。
@@ -60,7 +63,10 @@ class XG_CancelSolveDialog : public XG_Dialog
6063

6164
// キャンセルしてスレッドを待つ。
6265
::EnterCriticalSection(&xg_csLock);
63-
xg_bCancelled = true;
66+
// 生成完了していなければキャンセル
67+
if (!xg_bSolved) {
68+
xg_bCancelled = true;
69+
}
6470
m_cancellation_manager.SetCompleted();
6571
::LeaveCriticalSection(&xg_csLock);
6672
// スレッドを待つ(タイムアウト付き)。

XG_CancelSolveNoAddBlackDialog.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ class XG_CancelSolveNoAddBlackDialog : public XG_Dialog
3737
::KillTimer(hwnd, uTimerID);
3838
// キャンセルしてスレッドを待つ。
3939
::EnterCriticalSection(&xg_csLock);
40-
xg_bCancelled = true;
40+
// 生成完了していなければキャンセル
41+
if (!xg_bSolved) {
42+
xg_bCancelled = true;
43+
}
4144
m_cancellation_manager.SetCompleted();
4245
::LeaveCriticalSection(&xg_csLock);
4346
// スレッドを待つ(タイムアウト付き)。

XG_CancellationManager.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ class XG_CancellationManager
4848
// Notify completion (on successful processing)
4949
void SetCompleted() noexcept
5050
{
51-
if (m_hCompletionEvent != nullptr) {
51+
::EnterCriticalSection(&xg_csLock);
52+
// キャンセル済みでなければ完了を通知
53+
if (!xg_bCancelled && m_hCompletionEvent != nullptr) {
5254
::SetEvent(m_hCompletionEvent);
5355
}
56+
::LeaveCriticalSection(&xg_csLock);
5457
}
5558

5659
// 完了またはキャンセルを待機(タイムアウト付き)

crossword_generation.hpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <queue> // 待ち行列(std::queue)
1515
#include <thread> // std::thread
1616
#include <mutex> // std::mutex
17+
#include <atomic> // std::atomic
1718
#include <algorithm> // std::shuffle
1819
#include <utility> // ????
1920
#include <random> // 新しい乱数生成。
@@ -60,8 +61,8 @@ namespace std {
6061
// クロスワード生成用の名前空間。現状は「単語群から自動生成」のみ実装。
6162
// 将来的にはその他の生成方法もこのようなモダンな方向に移行する。
6263
namespace crossword_generation {
63-
inline static bool s_generated = false; // 生成済みか?
64-
inline static bool s_canceled = false; // キャンセルされたか?
64+
inline static std::atomic<bool> s_generated{false}; // 生成済みか?
65+
inline static std::atomic<bool> s_canceled{false}; // キャンセルされたか?
6566
inline static std::mutex s_mutex; // ミューテックス(排他処理用)。
6667

6768
// ルールを表すフラグ群。
@@ -1546,10 +1547,12 @@ struct from_words_t {
15461547
board0.replace('?', '#');
15471548
if (is_solution(board0)) { // 盤面が解ならば
15481549
std::lock_guard<std::mutex> lock(s_mutex); // 排他制御しながら
1549-
// 解をセットして
1550-
s_generated = true;
1551-
s_solution = board0;
1552-
return true; // 成功。
1550+
// キャンセルされていなければ解をセット
1551+
if (!s_canceled) {
1552+
s_generated = true;
1553+
s_solution = board0;
1554+
}
1555+
return !s_canceled; // キャンセルされていなければ成功。
15531556
}
15541557
}
15551558
return s_generated; // 生成済みなら成功。
@@ -1930,9 +1933,12 @@ struct non_add_block_t {
19301933
// 盤面が解ならば成功。
19311934
if (is_solution(m_board)) {
19321935
std::lock_guard<std::mutex> lock(s_mutex);
1933-
s_generated = true;
1934-
s_solution = m_board;
1935-
return true;
1936+
// キャンセルされていなければ解をセット
1937+
if (!s_canceled) {
1938+
s_generated = true;
1939+
s_solution = m_board;
1940+
}
1941+
return !s_canceled; // キャンセルされていなければ成功。
19361942
}
19371943

19381944
return s_generated; // 生成済みなら成功。

0 commit comments

Comments
 (0)