2010-02-27 12 views
13

में नियंत्रण-सी अपवाद को पकड़ना मैंने हास्केल में एक बहुत ही सरल रीड-इवल-प्रिंट-लूप बनाया जो नियंत्रण-सी (UserInterrupt) को पकड़ता है। हालांकि, जब भी मैं इस प्रोग्राम को संकलित और चलाता हूं, यह हमेशा पहले कंट्रोल-सी को पकड़ता है और हमेशा दूसरे नियंत्रण-सी पर बाहर निकलने वाला कोड 130 के साथ बंद हो जाता है। इससे कोई फर्क नहीं पड़ता कि मैं इनपुट के कितने लाइनों को पहले और बीच में देता हूं नियंत्रण-सीएस, यह हमेशा इस तरह होता है। मुझे पता है कि मुझे कुछ आसान याद आना चाहिए ... कृपया मदद करें, धन्यवाद!जीएचसी (हास्केल)

नोट: यह बेस -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 
+0

यह कोड 'नियंत्रण। अपवाद' और 'आईओ' आयात करने, जीएचसी 6.8 के साथ भी संकलित नहीं होगा। –

+0

@ नॉर्मन, जीएचसी 6.12 * बाहर * है। यह हास्केल प्लेटफ़ॉर्म में शामिल नहीं है, लेकिन यह पहले से ही आर्क और डेबियन अस्थिर के लिए उपलब्ध है। –

+0

क्यों अपना खुद का सिग्नल हैंडलर स्थापित नहीं करें? http://therning.org/magnus/archives/285 –

उत्तर

4

अस्वीकरण: मैं GHC internals से परिचित नहीं हूँ और मेरा उत्तर स्रोत कोड grepping, टिप्पणियाँ पढ़ने, और अनुमान लगाने पर आधारित है।

main समारोह आप को परिभाषित runMainIOGHC.TopHandler में परिभाषित द्वारा लिपटे वास्तव में है (यह आगे TcRnDriver.lhs को देखकर की पुष्टि की है):

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() 
:

-- | '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 के रूप में परिभाषित किया गया है

लिनक्स पर, stg_sig_install एक सी फ़ंक्शन है जो sigaction पर कॉल करता है। पैरामीटर STG_SIG_RST का अनुवाद SA_RESETHAND है। विंडोज़ पर, चीजें अलग-अलग होती हैं, जो शायद जेए के अवलोकन को बताती हैं।

3

मेरे लिए सबसे विश्वसनीय समाधान (कम से कम लिनक्स पर), System.Posix.Signals का उपयोग कर सिग्नल हैंडलर स्थापित करना है। मैं ऐसे समाधान की उम्मीद कर रहा था जिसके लिए इसकी आवश्यकता नहीं होगी, लेकिन वास्तविक कारण मैंने प्रश्न पोस्ट किया था कि मैं जानना चाहता था कि जीएचसी ने जिस तरह से व्यवहार किया था। जैसा कि # हास्केल पर बताया गया है, एक संभावित स्पष्टीकरण यह है कि जीएचसी इस तरह से व्यवहार करता है ताकि उपयोगकर्ता हमेशा लटकने पर एप्लिकेशन को नियंत्रित कर सके। फिर भी, यह अच्छा होगा अगर जीएचसी ने कुछ हद तक निम्न स्तर की विधि के बिना इस व्यवहार को प्रभावित करने का एक तरीका प्रदान किया है जिसका हमने सहारा लिया था :)।

7

वीआई हू सही है; हास्केल रनटाइम सिस्टम जानबूझकर कार्यक्रम को रोकता है जब दूसरा नियंत्रण-सी दबाया जाता है। व्यवहार प्राप्त करने के लिए कोई उम्मीद कर सकता है:

import Control.Exception as E 
import Control.Concurrent 
import System.Posix.Signals 

main = do 
    tid <- myThreadId 
    installHandler keyboardSignal (Catch (throwTo tid UserInterrupt)) Nothing 
    ... -- rest of program 
संबंधित मुद्दे