Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

無限ループ発生時にKGPが正常に終了しない #392

Closed
shinsuke-mat opened this issue Nov 3, 2018 · 15 comments · Fixed by #507
Closed

無限ループ発生時にKGPが正常に終了しない #392

shinsuke-mat opened this issue Nov 3, 2018 · 15 comments · Fixed by #507
Assignees
Labels
bug Something isn't working hard Hard to fix

Comments

@shinsuke-mat
Copy link
Member

CUI実行で発生.

$ r=example/real-bugs/Math73; pushd .; cd $r; java -jar $kgp/kGenProg.jar; popd;
...
[main] [INFO]  jp.kusumotolab.kgenprog.KGenProgMain - in the era of the 9th generation (54 seconds)
[main] [INFO]  jp.kusumotolab.kgenprog.ga.VariantStore - exec selection. 10th variants: (85, 12) => 97
[main] [INFO]  jp.kusumotolab.kgenprog.KGenProgMain - in the era of the 10th generation (58 seconds)
[main] [INFO]  jp.kusumotolab.kgenprog.KGenProgMain - reached the time limit

(ここで停止)

CPU回ってるので無限ループしているっぽい?

@shinsuke-mat
Copy link
Member Author

1つ以上のVariantでテストタイムアウト(無限ループによるものかどうかは不明)
が発生すると起こるっぽい.
Thread周りのバグ.

KGPMain自体は正しく最後まで到達しているが,謎のスレッドが残ってる?

JUnitCoreが怪しい.

@shinsuke-mat
Copy link
Member Author

shinsuke-mat commented Nov 13, 2018

いろいろ足掻いてみたが対処がわからん.
JUnitを使わない素のJavaプロジェクトでは普通に期待通りの振る舞いをするので,
やはりJUnitCoreが原因っぽい.

前向きな修正

@Timeout アノテーションを差し込んで,
JUnitのタイムアウトをJUnit自体に任す(プロセスをぶった切りしない)

後ろ向きな修正

Thread頑張る

@YoshikiHigo
Copy link
Member

まとはずれかもしれませんが,TestExecutor.javaのL35,executor.shutdown() を executor.shutdownNow() に変更してみるとか.

@shinsuke-mat
Copy link
Member Author

↑試してみましたがダメっぽいです.

@shinsuke-mat
Copy link
Member Author

Eclipse上では再現しない.なので現状のテストが正しく動いている.
しかしCUIから実行すると発生する.よってテストで見つけられないバグっぽい

@shinsuke-mat
Copy link
Member Author

shinsuke-mat commented Nov 14, 2018

テストできないので,CUIでの再現手順をメモ.
GCDが都合よく無限ループしがちなので題材に.

5世代目辺りで正解バリアントを見つけるが,そのまま停止する.

$ gradle assemble; java -jar build/libs/kGenProg.jar \
-r example/GCD01/ \
-s example/GCD01/src/example/GreatestCommonDivider.java \
-t example/GCD01/src/example/GreatestCommonDividerTest.java \
--random-seed 2 \
--test-time-limit 1

@a3636tako
Copy link
Contributor

ExecutorServiceのドキュメントによると

  • shutdown メソッド:新規タスクの受け入れを中止する。既存タスクはそのまま実行する
  • shutdownNow メソッド:既存タスクの停止を 試みる

この試みるというのが問題で、Thread.interruptが呼び出されるだけのようです。
そのため、Thread.sleepで停止しているようなスレッドの場合正常に停止しますが、今回の目的のように処理を中断することはできないようです。

スレッドを強制的に停止される方法として、Thread.stopがあるようですが、現在は非推奨になっています。

JUnitのTimeoutでどう実装されているか見てみました。

https://github.com/junit-team/junit4/blob/10a863eee43c9e13d1647675016b086f527a0697/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java#L139-L143

ThreadGroup.destroyが呼び出されていますが、このメソッドはグループ内にあるすべてのスレッドが既に終了していなければdestroyされないようです。
そのため、無限ループを起こしているスレッドは終了されず放置されると考えられます。

実際に、#408 のブランチでrequired-solutionsを大きめで実行すると、以下の画像のように大量のスレッドが生成されていました。

thread

JavaのThreadを安全に強制終了させることは難しく、Processとして切り出す必要があるのかもしれません

@YoshikiHigo
Copy link
Member

以下のWebページを見つけました.

「Javaで無限ループで暴走したスレッドを外部から強制停止できないか実験したメモ」https://qiita.com/msakamoto_sf/items/919c6ee38f0c8d260566

@YoshikiHigo
Copy link
Member

以下のページに一応の解決策は載っています.

「THI05-J. スレッドの強制終了にThread.stop()メソッドを使用しない」
https://www.jpcert.or.jp/java-rules/thi05-j.html

@YoshikiHigo
Copy link
Member

同じような解決策,JDK1.5のページにも載っていますね.
https://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

@shinsuke-mat shinsuke-mat changed the title APR失敗時にKGPが正常に終了しない 無限ループ発生時にKGPが正常に終了しない Nov 15, 2018
@shinsuke-mat
Copy link
Member Author

先程の議論のメモ
3つ方針

A. プロセス化

スレッド化するから止めれないので,プロセス化する.
テスト実行のプロセス切り出しは簡単.昔やったので.

より前向きなやり方:
Variant実行をプロセスに切り出して,プロセス間通信を真面目にやる.
そうしておくとクラスタ化に繋がる.
実装はしんどい.

B. JDB監視員を用意する

KGP起動時に別プロセスで監視員を作る.
制限時間を超えたらJDB使って無限ループスレッドをぶっ殺す.

C. 題材側を書き換える

無限ループを発生しうる箇所全てにinterrupted時の処理を加える
if (Thread.isInterrupted()) break;

@shinsuke-mat shinsuke-mat added the hard Hard to fix label Nov 15, 2018
@shinsuke-mat
Copy link
Member Author

D. JUnit.jarを修正する

https://github.com/junit-team/junit4/blob/10a863eee43c9e13d1647675016b086f527a0697/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java#L139-L143
ここでthread.stop()すれば行ける?

結果:Dで解決
独自junit.jarという点は気持ち悪いがひとまず実験はできる.

@shinsuke-mat shinsuke-mat added the bug Something isn't working label Nov 16, 2018
@shinsuke-mat
Copy link
Member Author

JUnit4のアノテーションベースの書き方なら上記Dで解決している.

しかしJUnit3の継承ベースの書き方だと上記Dでは解決しない.
書き換えた FailOnTimeout がルールベースの実行に依存するクラスで,
継承ベースは全く別.

MathはJUnit3なのでやっかい.

@shinsuke-mat
Copy link
Member Author

JUnitのTestCaseクラスを書き換えて解決.

asis
- リフレクションでテストメソッドを抜き出す
- 実行

tobe
- リフレクションでテストメソッドを抜き出す
- タイムアウト付きスレッドで実行

@shinsuke-mat
Copy link
Member Author

上記では停止しない問題が再発したので,
CUILauncherで System.exit(1); で解決.

ひとまずこれで実験は回るようになった.

まともな修正方法は要検討.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working hard Hard to fix
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants