Haskell:getProcessStatus 阻塞 SIGINT
Haskell: getProcessStatus blocking SIGINT
我正在尝试在 Haskell 中编写一个简单的 shell,但我无法使信号处理正常工作。如果没有命令是 运行,发送 SIGINT
到 shell 进程会触发信号处理程序。但是当对 getProcessStatus
进行阻塞调用时,该信号将被忽略。立即向子进程发送信号当然会杀死子进程并进行阻塞调用 return。
用 Control.Concurrent.threadDelay
替换阻塞调用不会阻止信号,即一切都按预期工作。将阻塞标志替换为 getProcessStatus
为 False
使函数 return 在子进程完成之前。
参考流程包: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
getProcessStatus
在内部使用 interruptible FFI 调用。但为什么 -threaded
是必要的?
This blog post 关于在 Haskell 中处理 ctrl-c 表明信号处理是在一个单独的线程中完成的,该线程使用异步异常杀死主线程:
When the user hits Ctrl-C, GHC raises an async exception of type
UserInterrupt on the main thread. This happens because GHC installs an
interrupt handler which raises that exception, sending it to the main
thread with throwTo.
但是异步包的 documentation 提到:
Different Haskell implementations have different characteristics with
regard to which operations block all threads.
Using GHC without the -threaded option, all foreign calls will block
all other Haskell threads in the system, although I/O operations will
not. With the -threaded option, only foreign calls with the unsafe
attribute will block all other threads.
所以也许这就是为什么在存在可中断的 ffi 调用时正确处理 SIGINT 需要 -threaded
:否则,抛出异步异常的线程将被阻止 运行。
我正在尝试在 Haskell 中编写一个简单的 shell,但我无法使信号处理正常工作。如果没有命令是 运行,发送 SIGINT
到 shell 进程会触发信号处理程序。但是当对 getProcessStatus
进行阻塞调用时,该信号将被忽略。立即向子进程发送信号当然会杀死子进程并进行阻塞调用 return。
用 Control.Concurrent.threadDelay
替换阻塞调用不会阻止信号,即一切都按预期工作。将阻塞标志替换为 getProcessStatus
为 False
使函数 return 在子进程完成之前。
参考流程包: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
getProcessStatus
在内部使用 interruptible FFI 调用。但为什么 -threaded
是必要的?
This blog post 关于在 Haskell 中处理 ctrl-c 表明信号处理是在一个单独的线程中完成的,该线程使用异步异常杀死主线程:
When the user hits Ctrl-C, GHC raises an async exception of type UserInterrupt on the main thread. This happens because GHC installs an interrupt handler which raises that exception, sending it to the main thread with throwTo.
但是异步包的 documentation 提到:
Different Haskell implementations have different characteristics with regard to which operations block all threads.
Using GHC without the -threaded option, all foreign calls will block all other Haskell threads in the system, although I/O operations will not. With the -threaded option, only foreign calls with the unsafe attribute will block all other threads.
所以也许这就是为什么在存在可中断的 ffi 调用时正确处理 SIGINT 需要 -threaded
:否则,抛出异步异常的线程将被阻止 运行。