Haskell:getProcessStatus阻塞SIGINT

时间:2016-01-18 20:02:13

标签: haskell process signals posix

我正在尝试在Haskell中编写一个简单的shell,但是我无法使信号处理工作。如果没有命令正在运行,则将SIGINT发送到shell进程会触发信号处理程序。但是当对getProcessStatus进行阻塞调用时,将忽略该信号。立即向子进程发送信号当然会杀死子进程并使阻塞呼叫返回。

Control.Concurrent.threadDelay替换阻止呼叫并不会阻止信号,即一切都按预期工作。使用getProcessStatus将阻止标志替换为False会使子函数在子进程完成之前返回。

参考流程包:https://hackage.haskell.org/package/unix-2.7.1.0/docs/System-Posix-Process.html#v:getProcessStatus

相关代码如下,请参阅(仅)注释行。

main :: IO ()
main = do
    pidRef <- (newIORef [] :: IO (IORef [ProcessID]))
    setSigHant pidRef
    doPrompt pidRef

printPrompt :: IO ()
printPrompt = fdWrite stdError "λ➔ " >> return ()

doPrompt :: IORef [ProcessID] -> IO ()
doPrompt pidRef = do
    printPrompt
    tryLine <- try getLine :: IO (Either SomeException String)
    case tryLine of 
        Left _ -> do
            putStrLn ""
            exitSuccess
        Right line -> do
            tryCl <- try (parse line) :: IO (Either SomeException [Command])
            case tryCl of 
                Left e -> fdWrite stdError (show e ++ "\n") >> return ()
                Right cl -> 
                    if length cl > 0 && (cmd . head) cl == "cd" then
                        cd (head cl)
                    else do
                        execCommands pidRef cl (stdInput, stdOutput)
                        pids <- readIORef pidRef

                        -- This call to getProcessStatus blocks the signals
                        _ <- sequence $ map (getProcessStatus True False) pids
                        _ <- writeIORef pidRef []
                        return ()
            doPrompt pidRef

setSigHant :: (IORef [ProcessID]) -> IO ()
setSigHant pidRef = do
    let handler = Catch (sigIntHandler pidRef)
    installHandler sigINT handler Nothing
    return ()


sigIntHandler :: (IORef [ProcessID]) -> IO ()
sigIntHandler pidRef = do
    pids <- readIORef pidRef
    sequence_ $ map (signalProcess sigINT) pids
    fdWrite stdError "\n"
    printPrompt

1 个答案:

答案 0 :(得分:1)

getProcessStatus在内部使用interruptible FFI来电。但为什么-threaded必要?

关于在Haskell中处理ctrl-c的

This blog post建议信号处理在一个单独的线程中完成,该线程使用异步异常杀死主线程:

  

当用户按Ctrl-C时,GHC会引发类型的异步异常   主线程上的UserInterrupt。这是因为GHC安装了一个   中断处理程序,它引发该异常,将其发送到main   与throwTo的线程。

但异步包的documentation提到:

  

不同的Haskell实现具有不同的特性   关于哪些操作阻止所有线程。

     

使用没有-threaded选项的GHC,所有外部调用都将被阻止   系统中所有其他Haskell线程,尽管I / O操作会   不。使用-threaded选项,只有外部调用具有不安全性   属性将阻止所有其他线程。

也许这就是为什么在存在可中断的ffi调用时正确处理SIGINT需要-threaded的原因:否则,将阻止抛出异步异常的线程运行。