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?
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 resultingEither SomeException ()
to a format you can process from C (you can use aStablePtr
for theSomeException
, but the point is that you need to handle theEither
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.