10

我在Haskell中爲Scheme解釋器實現REPL,我想處理一些像UserInterrupt,StackOverflow,HeapOverflow等異步事件...基本上,我想停止電流運算時發生UserInterrupt和時的StackOverflow和HeapOverflow發生打印合適的消息等我實現這個如下:在Haskell中處理UserInterrupt異常

repl evaluator = forever $ (do 
     putStr ">>> " >> hFlush stdout 
     out <- getLine >>= evaluator 
     if null out 
      then return() 
      else putStrLn out) 
     `catch` 
     onUserInterrupt 

    onUserInterrupt UserInterrupt = putStrLn "\nUserInterruption" 
    onUserInterrupt e = throw e 

    main = do 
     interpreter <- getMyLispInterpreter 
     handle onAbort (repl $ interpreter "stdin") 
     putStrLn "Exiting..." 

    onAbort e = do 
     let x = show (e :: SomeException) 
     putStrLn $ "\nAborted: " ++ x 

它可以作爲一個例外預期。如果我開始解釋,然後按Ctrl-Z + Enter鍵,我得到:

>>> ^Z 

    Aborted: <stdin>: hGetLine: end of file 
    Exiting... 

這是正確的。但是,如果我開始解釋,然後按Ctrl-C,然後按Ctrl-Z + Enter鍵,我得到:

>>> 
    UserInterruption 
    >>> ^Z 

它掛,我不能再使用的解釋。但是,如果我再次按下Ctrl-C,則REPL將取消阻止。我搜查了很多,我無法弄清楚它的原因。任何人都可以解釋我?

非常感謝!

+0

中所述我從來沒有看到Ctrl-Z被抓住了。第一個Ctrl-C被捕獲,但第二個不是。這可能是同樣的問題。 你可以在一個完整的工作測試用例中更改你的代碼嗎? F.E. 'return'而不​​是'interpreter'stdin''並添加了正確的導入。 –

回答

10

控制-C處理不catch工作:可能與GHC#2301: Proper handling of SIGINT/SIGQUIT

這裏是一個工作測試用例,與evaluator刪除:

module Main where 

import Prelude hiding (catch) 

import Control.Exception (SomeException(..), 
          AsyncException(..) 
         , catch, handle, throw) 
import Control.Monad (forever) 
import System.IO 

repl :: IO() 
repl = forever $ (do 
    putStr ">>> " >> hFlush stdout 
    out <- getLine 
    if null out 
     then return() 
     else putStrLn out) 
    `catch` 
    onUserInterrupt 

onUserInterrupt UserInterrupt = putStrLn "\nUserInterruption" 
onUserInterrupt e = throw e 

main = do 
    handle onAbort repl 
    putStrLn "Exiting..." 

onAbort e = do 
    let x = show (e :: SomeException) 
    putStrLn $ "\nAborted: " ++ x 

在Linux上,控制-Z是不像Sjoerd提到的那樣。也許你正在使用Control-Z用於EOF的Windows。我們可以在Linux上使用Control-d,它複製你看到的行爲信號EOF:

>>> ^D 
Aborted: <stdin>: hGetLine: end of file 
Exiting... 

EOF是由您handle/onAbort功能處理和控制-C是由catch/onUserInterrupt處理。這裏的問題是,你的repl函數將只捕獲第一個Control-C - 通過刪除handle/onAbort函數可以簡化測試用例。如上所述,該Control-C處理不起作用,catch可能與GHC#2301: Proper handling of SIGINT/SIGQUIT有關。

以下版本而不是使用POSIX API來安裝用於控制-C持續信號處理程序:

module Main where 

import Prelude hiding (catch) 

import Control.Exception (SomeException(..), 
          AsyncException(..) 
         , catch, handle, throw) 
import Control.Monad (forever) 
import System.IO 
import System.Posix.Signals 

repl :: IO() 
repl = forever $ do 
    putStr ">>> " >> hFlush stdout 
    out <- getLine 
    if null out 
     then return() 
     else putStrLn out 

reportSignal :: IO() 
reportSignal = putStrLn "\nkeyboardSignal" 

main = do 
    _ <- installHandler keyboardSignal (Catch reportSignal) Nothing 
    handle onAbort repl 
    putStrLn "Exiting..." 

onAbort e = do 
    let x = show (e :: SomeException) 
    putStrLn $ "\nAborted: " ++ x 

,它可以處理被按壓控制-CS多次:

>>> ^C 
keyboardSignal 

>>> ^C 
keyboardSignal 

>>> ^C 
keyboardSignal 

如果沒有使用Posix API,在Windows上安裝持久性信號處理程序需要在每次捕獲時重新提升異常,如http://suacommunity.com/dictionary/signals.php