12

Is there any good way to catch a haskell exception, which is thrown in a haskell callback function called by a c function?

For example, let me have a simple c function which just calls a given callback,

void callmeback ( void (*callback) () ) {
  callback ();
}

and a haskell code which uses this function via ffi.

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Main (main) where

import Foreign
import Control.Exception
import Data.Typeable

foreign import ccall safe "wrapper"
        mkCallback :: IO () -> IO (FunPtr (IO ()))

foreign import ccall safe "callmeback"
        callmeback :: FunPtr (IO ()) -> IO ()

data AnError = AnError String deriving (Show, Eq, Typeable)
instance Exception AnError             

callback :: IO ()
callback = throwIO $ AnError "Catch me."

callMeBack :: IO () -> IO ()
callMeBack f = do fp <- mkCallback f
                  callmeback fp
main = do callMeBack callback `catch`
             (\ e -> do putStrLn $ show (e :: AnError)
                        putStrLn "I caught you." )
          putStrLn "-- Happy end."

The exection result (compiling with GHC 7.8.2) is as follows:

% ./Catch
Catch: AnError "Catch me."

So it seems like the exception thrown in callback can not be caught in main. How can I make this kind code work well?

jub0bs
  • 60,866
  • 25
  • 183
  • 186
tmizmd
  • 123
  • 4
  • 1
    I'm asking a semi-related question at the moment here: http://stackoverflow.com/questions/29725324/catching-exceptions-in-eithert-and-preserving-underlying-monad-transformers - this won't work for me, but I think it might work for you... look at the `enclosed-exceptions` package and see if that brings you any luck: https://hackage.haskell.org/package/enclosed-exceptions – TheCriticalImperitive Apr 19 '15 at 08:42
  • 1
    This blog post is where I read about it, might provide some more useful information: https://www.fpcomplete.com/user/snoyberg/general-haskell/exceptions/catching-all-exceptions – TheCriticalImperitive Apr 19 '15 at 08:44

1 Answers1

4

You have to do this manually, which would look like this:

  • Wrap your callback function in Haskell code that calls try, and then serializes the resulting Either SomeException () to a format you can process from C (you can use a StablePtr for the SomeException, but the point is that you need to handle the Either somehow).

  • Where your C code calls the callback, check whether the result was a Left exn and if so, propagate the error to the top level of your C code, freeing resources as appropriate along the way. This step is non-mechanical, since C does not have exceptions.

  • At the top level of your C code, re-serialize the exception or result and read it in a Haskell function that wraps your call to the C code, raising an exception or returning the result as appropriate.

I'm not aware of any example of a program that does this.

Reid Barton
  • 14,951
  • 3
  • 39
  • 49