操作
リファクタ #183
完了RuntimeTaskManagerTest.shutdownInterruptsAndWaitsForTrackedTask が flaky
リファクタ #183:
RuntimeTaskManagerTest.shutdownInterruptsAndWaitsForTrackedTask が flaky
説明
概要¶
RuntimeTaskManagerTest.shutdownInterruptsAndWaitsForTrackedTask() が CI 上でまれに失敗する(flaky)。プロダクションコードの不具合ではなく、テストコードのレースが原因。
失敗内容¶
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
at RuntimeTaskManagerTest.shutdownInterruptsAndWaitsForTrackedTask(RuntimeTaskManagerTest.java:38)
38 行目の assertTrue(interrupted.await(1, TimeUnit.SECONDS)) が false を返す。
原因¶
ワーカースレッドの処理が以下の形になっている:
started.countDown();
try {
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
interrupted.countDown();
}
started.countDown() の後・Thread.sleep(1000) に入る前に manager.shutdown() が thread.interrupt() を撃つと、while (!isInterrupted()) が即 false になりループ本体に入らないため Thread.sleep() が一度も呼ばれず、InterruptedException が発生しない。結果 catch 節が走らず interrupted ラッチが down せず await が失敗する。CI のように CPU が逼迫した環境で発生しやすい。
修正方針¶
ループ条件を外して必ず Thread.sleep を通す。Thread.sleep は割り込みフラグが立った状態で呼ばれると即座に InterruptedException を投げるため、レースが解消する:
try {
while (true) {
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
interrupted.countDown();
}
対象¶
src/test/java/bms/player/beatoraja/runtime/RuntimeTaskManagerTest.java
プロダクションコード(RuntimeTaskManager)の変更は不要。もう一方の startVirtualTracksAndInterruptsVirtualThread は直接 Thread.sleep を呼ぶため影響を受けない。
操作