Asynchronous C call to Haskell code via FFI

I'm currently trying to port parts of the Windows API to Haskell. One of the functions considered is SetConsoleCtrlHandler , which registers a callback for a handler that is called whenever Ctrl + C or Ctrl + Break comes from the console input. So, here is a small sample program:

 {-# LANGUAGE CPP #-} {-# LANGUAGE ForeignFunctionInterface #-} module Main where import Control.Monad import GHC.ConsoleHandler import Foreign import System.Win32.Process import System.Win32.Types foreign import ccall "wrapper" makeHandlerPtr :: (DWORD -> IO BOOL) -> IO (FunPtr (DWORD -> IO BOOL)) foreign import stdcall "windows.h SetConsoleCtrlHandler" c_SetConsoleCtrlHandler :: FunPtr (DWORD -> IO BOOL) -> BOOL -> IO BOOL main :: IO () main = unsafeCtrl unsafeCtrl :: IO () unsafeCtrl = do ptrHandler <- makeHandlerPtr $ \d -> do putStrLn $ "received event " ++ show d return True c_SetConsoleCtrlHandler ptrHandler True forM_ [1..50] $ \_ -> sleep 100 freeHaskellFunPtr ptrHandler safeCtrl :: IO () safeCtrl = do installHandler $ Catch $ \e -> do putStrLn $ "received event " ++ show e forM_ [1..50] $ \_ -> sleep 100 

If you compile the above program through ghc --make AsyncCallback.hs and run the program, it will exit with the following error as soon as a control event is received:

 $ AsyncCallback AsyncCallback.exe: schedule: re-entered unsafely. Perhaps a 'foreign import unsafe' should be 'safe'? 

However, compiling with the -threaded option -threaded seems fine:

 $ AsyncCallback received event 0 received event 1 received event 0 

Not sure why this makes it work, since I am not using the thread here explicitly, and I do not see where the GHC will implicitly start a new thread.

When asking about this at #haskell, someone noticed that calling Haskell functions asynchronously from a C callback is inherently unsafe and that the above example works more or less. I also pointed to GHC.ConsoleHandler.installHandler , which looks like a safe wrapper around SetConsoleCtrlHandler , and running the above program with main = safeCtrl really works fine. I tried to understand the implementation of installHandler ( C-side and Haskell-side ), but I don’t quite get this.

The whole problem here is that the callback is issued asynchronously, so the Haskell RTS is not β€œprepared” when the callback tries to run the Haskell code. Therefore i would like to know

  • How does the GHC installHandler implementation installHandler ? What is the key part that makes this implementation work and my version doesn't work?
  • Are there other options / common patterns, how to call Haskell code asynchronously from a C callback? In this particular case, I can switch to the GHC implementation. But I may run into similar C functions that asynchronously go into Haskell code, where I have to write secure binding myself.
+7
asynchronous callback haskell ffi
source share

No one has answered this question yet.

See related questions:

5129
How to return a response from an asynchronous call?
2817
How can I upload files asynchronously?
1204
How to access the correct `this` inside a callback?
1137
How can I get jQuery to execute a synchronous rather than asynchronous Ajax request?
1110
Asynchronous and synchronous execution, what does this mean?
757
Getting started with Haskell
746
How to pass parameter to callbackTimeout ()?
613
What is a callback function?
427
How to return a value from an asynchronous callback function?
2
Using Haskell FFI - mallocForeignPtr

All Articles