Haskell Windows Ctrl-C 動作確認
コード
コードは前回記事と同じです(再掲)。
import Control.Concurrent import Control.Monad import System.Exit import System.IO import System.Win32.Console.CtrlHandler main :: IO () main = do tid <- myThreadId let handler event = do if event == cTRL_C_EVENT then do putStrLn "goodbye!" killThread tid pure True else pure False pHandler <- mkHandler handler success <- c_SetConsoleCtrlHandler pHandler True when (not success) $ do putStrLn "SetConsoleCtrlHandler failed" exitFailure let loop n = do putStr $ show n ++ ", " hFlush stdout threadDelay 1000000 loop (n+1) loop 0
これを次の4通りのオプションでビルドします。
name: windows-interruption dependencies: - base >= 4.7 && < 5 - Win32 executables: app-rtsopts-threaded: main: Main.hs source-dirs: . ghc-options: - -rtsopts - -threaded app-threaded: main: Main.hs source-dirs: . ghc-options: - -threaded app-rtsopts: main: Main.hs source-dirs: . ghc-options: - -rtsopts app: main: Main.hs source-dirs: .
実行
これを実行してみます。
app
タイミングによる。
$ app 0, 1, 2, goodbye! goodbye! app.exe: thread killed (応答がなくなる)
$ app 0, 1, 2, goodbye! goodbye! app.exe: thread killed (終了する)
$ app 0, goodbye! app.exe: thread killed app.exe: warning: too many hs_exit()s
また、GHC 8.6.3 でコンパイルしたときですが、下記のようなエラーが出ることもありました。そもそも GHC 8.6.3 は Windows でのビルドにバグがあるので、他は GHC 8.4.4 を使っています。
$ app 0, 1, 2, goodbye! goodbye! Access violation in generated code when writing 0x20 Attempting to reconstruct a stack trace... app.exe: internal error: scavenge_stack: weird activation record found on stack: 5212000 (GHC version 8.6.3 for x86_64_unknown_mingw32) Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug Frame Code address * 0x484dd00 0x736b99
app-rtsopts
タイミングによる。
$ app-rtsopts 0, 1, 2, goodbye! goodbye! app-rtsopts.exe: thread killed
$ app-rtsopts 0, 1, 2, goodbye! app-rtsopts.exe: thread killed app-rtsopts.exe: warning: too many hs_exit()s
app-rtsopts +RTS --install-signal-handlers=no
--install-signal-handlers はデフォルトでは yes。
タイミングによる。
$ app-rtsopts +RTS --install-signal-handlers=no 0, 1, 2, goodbye!
$ app-rtsopts +RTS --install-signal-handlers=no 0, goodbye! app-rtsopts.exe: thread killed app-rtsopts.exe: warning: too many hs_exit()s
$ app-rtsopts +RTS --install-signal-handlers=no 0, 1, 2, goodbye! goodbye! app-rtsopts.exe: thread killed
app-threaded
想定通りの動作。
$ app-threaded 0, 1, 2, goodbye! app-threaded.exe: thread killed
app-rtsopts-threaded
想定通りの動作。
$ app-rtsopts-threaded 0, 1, 2, 3, goodbye! app-rtsopts-threaded.exe: thread killed
app-rtsopts-threaded +RTS --install-signal-handlers=no
想定通りの動作。
$ app-rtsopts-threaded +RTS --install-signal-handlers=no 0, 1, 2, 3, 4, 5, goodbye! app-rtsopts-threaded.exe: thread killed
まとめ
- 想定通りの動作
- app-threaded
- app-rtsopts-threaded
- app-rtsopts-threaded +RTS --install-signal-handlers=no
- タイミングによる
- app
- app-rtsopts
- app-rtsopts +RTS --install-signal-handlers=no
タイミングによるときの動作。
- ハンドラー → 終了する
- ハンドラー → ハンドラー → thread killed 例外 → 応答がなくなる
- ハンドラー → ハンドラー → thread killed 例外 → 終了する
- ハンドラー → thread killed 例外 → too many hs_exit()s 例外 → 終了する
今後
ハンドラーが2回呼ばれるときは killThread
も2回呼んでいるはずなので、その後がおかしくなることはありそう。また killThread
しているが、それは自分自身ではないのかの確認もしたい。
System.Win32.Console.CtrlHandler
とか GHC RTS を追うしかない気がするので追ってみます。
GHC RTS はこの辺(rts/win32/ConsoleHandler.c)からかなぁ。