Ловля исключения Ctrl-C в GHC (Haskell)

Я создал действительно простой read-eval-print-loop в Haskell, который ловит Ctrl-C (UserInterrupt). Однако каждый раз, когда я компилирую и запускаю эту программу, она всегда ловит первую Ctrl-C и всегда прерывается на второй Ctrl-C с кодом выхода 130. Не имеет значения, сколько строк входа я даю ему прежде и между этими двумя Управлением-Cs, это всегда происходит этот путь. Я знаю, что должен пропускать что-то простое..., помогите, Спасибо!

Примечание: это - с основой 4 исключения, так Управление. Исключение и не Управление. OldException.

import Control.Exception as E
import System.IO

main :: IO ()
main = do hSetBuffering stdout NoBuffering
          hSetBuffering stdin NoBuffering
          repLoop

repLoop :: IO ()
repLoop
  = do putStr "> "
       line <- interruptible "<interrupted>" getLine
       if line == "exit"
          then putStrLn "goodbye"
          else do putStrLn $ "input was: " ++ line
                  repLoop

interruptible :: a -> IO a -> IO a
interruptible a m
  = E.handleJust f return m
  where
    f UserInterrupt
      = Just a
    f _
      = Nothing
14
задан pheaver 1 March 2010 в 11:12
поделиться

2 ответа

Отказ от ответственности: я не знаком с внутренним устройством GHC и своим ответом основан на поиске исходного кода, чтении комментариев и предположениях.

Определенная вами функция main фактически обернута в runMainIO , определенную в GHC.TopHandler (это дополнительно подтверждается просмотром TcRnDriver.lhs):

-- | 'runMainIO' is wrapped around 'Main.main' (or whatever main is
-- called in the program).  It catches otherwise uncaught exceptions,
-- and also flushes stdout\/stderr before exiting.
runMainIO :: IO a -> IO a
runMainIO main = 
    do 
      main_thread_id <- myThreadId
      weak_tid <- mkWeakThreadId main_thread_id
      install_interrupt_handler $ do
           m <- deRefWeak weak_tid 
           case m of
               Nothing  -> return ()
               Just tid -> throwTo tid (toException UserInterrupt)
      a <- main
      cleanUp
      return a
    `catch`
      topHandler

А install_interrupt_handler определяется как:

install_interrupt_handler :: IO () -> IO ()
#ifdef mingw32_HOST_OS
install_interrupt_handler handler = do
  _ <- GHC.ConsoleHandler.installHandler $
     Catch $ \event -> 
        case event of
           ControlC -> handler
           Break    -> handler
           Close    -> handler
           _ -> return ()
  return ()
#else
#include "rts/Signals.h"
-- specialised version of System.Posix.Signals.installHandler, which
-- isn't available here.
install_interrupt_handler handler = do
   let sig = CONST_SIGINT :: CInt
   _ <- setHandler sig (Just (const handler, toDyn handler))
   _ <- stg_sig_install sig STG_SIG_RST nullPtr
     -- STG_SIG_RST: the second ^C kills us for real, just in case the
     -- RTS or program is unresponsive.
   return ()

В Linux, stg_sig_install - это функция C, которая вызывает sigaction . Параметр STG_SIG_RST преобразуется в SA_RESETHAND . В Windows все устроено иначе, что, вероятно, объясняет наблюдение ja.

5
ответ дан 1 December 2019 в 13:47
поделиться

Самым надежным решением для меня (по крайней мере, в Linux) была установка обработчика сигналов с помощью System.Posix.Signals. Я надеялся на решение, которое не потребует этого, но настоящая причина, по которой я разместил вопрос, заключалась в том, что я хотел знать, почему GHC ведет себя именно так. Как объясняется на #haskell, вероятным объяснением является то, что GHC ведет себя таким образом, поэтому пользователь всегда может нажать Control-C на приложение, если оно зависнет. Тем не менее, было бы неплохо, если бы GHC предоставил способ повлиять на это поведение без несколько более низкоуровневого метода, к которому мы прибегли :).

3
ответ дан 1 December 2019 в 13:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: