Haskell

HaskellでCtrl-Cを制御する

SIGINT等のシグナルの扱い方です。


tl;dr

System.Posix.Signalsを使います


2019/1/25 追記

WindowsでCtrl-Cを制御する方法については @kakkun61 氏の記事を参照してください

Haskell で Ctrl-C を制御する(Windows) - 趣味はデバッグ……


Ctrl-Cが押されたらクロージングの処理を伴って終了するプログラムを書いてみます。

import Control.Concurrent

import System.Posix.Signals

main :: IO ()
main = do
tid <- myThreadId
let handler = do
putStrLn "goodbye!"
killThread tid
installHandler keyboardSignal (Catch handler) Nothing

let loop n = do
putStr $ show n ++ ", "
threadDelay 1000000
loop (n+1)
loop 0

実行してみましょう

$ runhaskell Main.hs

0, 1, 2, 3, 4, 5, ^Cgoodbye!
Main.hs: thread killed
$

ちゃんとgoodbyeと出力されて終了しました!killThreadは例外を伴って終了するのでMain.hs: thread killedというメッセージが出てしまっています。もし気になるなら例外を握りつぶすかMVarを使って終了を監視する仕組みを作るといいでしょう。

肝心のSignalを制御する関数はinstallHandler :: Signal -> Handler -> Maybe SignalSet -> IO Handlerです。Signelには制御するSignal、HandlerにはSignalが投げられた時の処理、Maybe SignalSetには処理時にブロックする他のSignalを指定します。

Signalの値は予め用意されていて

Function
Signal

keyboardSignal
SIGINT

keyboardStop
SIGTSTP

keyboardTermination
SIGQUIT

openEndedPipe
SIGPIPE

と言った感じです。

HandlerDefaultにすれば標準の動作を、Ignoreにすれば何もしなくなり、Catch (IO ())で処理を記述すればSignalが投げられた時の処理を記述することができます。上の例ではhandlerという関数にメッセージの表示とメインスレッドの停止の処理を書いてHandlerとして渡していました。

簡単に解説しましたがまだまだ色々な使い方ができるので是非一度System.Posix.Signalsを読んでみてください(^^)