how to catch keyboard interrupts?

Hi, all, I am continuing to mess with my little scheme interpreter, and I decided that it would be nice to be able to hit control-C in the middle of a long-running scheme computation to interrupt that and return to the lisp> prompt; hitting control-C and getting back to the shell prompt works, but is a little drastic. So I looked at System.Posix.Signals and after a bit of messing about got the following:
mysighandler = Catch (do hPutStrLn stderr "caught a signal!" fail "Interrupt!")
runREPL :: IO () runREPL = do getProgName >>= writeHdr env <- setupBindings [] True runInit env installHandler sigINT mysighandler Nothing installHandler sigQUIT mysighandler Nothing doREPL env
This compiles just fine, the interpreter runs as usual... but the added code doesn't seem to do anything. You can probably guess already... the print statement in mysighandler is there to see if it actually caught a signal. It does: I see "caught a signal!" just fine, in fact I see dozens of them as I lean on the control-C; but now my scheme calculation doesn't get interrupted at all! I see in the System.Posix.Signals documentation that the signal handler gets invoked in a new thread; is this the source of the problem? If so, what should I do to fix it? I'm afraid that sort of stuff is still beyond my haskell-fu... many thanks! Uwe

Hello Uwe, Saturday, February 23, 2008, 11:35:35 PM, you wrote:
mysighandler = Catch (do hPutStrLn stderr "caught a signal!" fail "Interrupt!")
scheme calculation doesn't get interrupted at all! I see in the System.Posix.Signals documentation that the signal handler gets invoked in a new thread; is this the source of the problem?
yes, fail kills only this thread :) you should store thread id of thread running interpreter and send async exception to it. control.concurrent is probably contains all required functions -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Thanks, Bulat, I'll look into this!
On 2/23/08, Bulat Ziganshin
Hello Uwe,
Saturday, February 23, 2008, 11:35:35 PM, you wrote:
mysighandler = Catch (do hPutStrLn stderr "caught a signal!" fail "Interrupt!")
scheme calculation doesn't get interrupted at all! I see in the System.Posix.Signals documentation that the signal handler gets invoked in a new thread; is this the source of the problem?
yes, fail kills only this thread :)
you should store thread id of thread running interpreter and send async exception to it. control.concurrent is probably contains all required functions
-- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 2/23/08, Bulat Ziganshin
you should store thread id of thread running interpreter and send async exception to it. control.concurrent is probably contains all required functions
Most splendid! Here's what I did
data MyInterrupt = MyInt Int instance Typeable MyInterrupt where typeOf x = typeOf (0 :: Int)
catcher :: MyInterrupt -> IO () catcher e = hPutStrLn stderr "interrupt!"
then later, in the REPL
catchDyn (evalAndPrint env True line) (\e -> catcher e)
and in the initialization
mysighandler tid = Catch (throwDynTo tid (MyInt 0))
and
myTID <- myThreadId installHandler sigINT (mysighandler myTID) Nothing installHandler sigQUIT (mysighandler myTID) Nothing doREPL env
I had to add the "MyInterrupt" stuff because GHC was complaining about ambiguous types, initially I had just "(\e -> hPutStrLn stderr (show e))" as the second arg of the catchDyn. And try it in the self-test... Now test number/string conversions... this'll take a bit longer interrupt! lisp> It works! Cool! This is worth bumping the version number :-) Many thanks! Uwe

This is pretty cool, but I have one warning:
On Sat, Feb 23, 2008 at 4:37 PM, Uwe Hollerbach
data MyInterrupt = MyInt Int instance Typeable MyInterrupt where typeOf x = typeOf (0 :: Int)
I am pretty sure that this makes Dynamic unsound; you could accidentally cast from an Int to a MyInterrupt or vice versa. Try this instead:
data MyException = Interrupt deriving Typeable
then you can safely use throwDyn and catchDyn on this type. -- ryan

Thanks, I'll try that. -- Uwe
On 2/24/08, Ryan Ingram
This is pretty cool, but I have one warning:
On Sat, Feb 23, 2008 at 4:37 PM, Uwe Hollerbach
wrote: data MyInterrupt = MyInt Int instance Typeable MyInterrupt where typeOf x = typeOf (0 :: Int)
I am pretty sure that this makes Dynamic unsound; you could accidentally cast from an Int to a MyInterrupt or vice versa. Try this instead:
data MyException = Interrupt deriving Typeable
then you can safely use throwDyn and catchDyn on this type.
-- ryan
participants (3)
-
Bulat Ziganshin
-
Ryan Ingram
-
Uwe Hollerbach