Broadcast error when calling multithreaded C FFI with Haskell callback function

Below is the FFI Haskell / C code that throws a schedule error at runtime (GHC 7.0.3, Mac OS 10.7, x86_64). I was looking for an explanation of the error, but did not find anything.

C Code ( mt.c ):

 #include <pthread.h> #include <stdio.h> typedef void(*FunctionPtr)(int); /* This is our thread function. It is like main(), but for a thread*/ void *threadFunc(void *arg) { FunctionPtr fn; fn = (FunctionPtr) arg; fn(1); //call haskell function with a CInt argument to see if it works } void create_threads(FunctionPtr* fp, int numThreads ) { pthread_t pth[numThreads]; // array of pthreads int t; for (t=0; t < numThreads;){ pthread_create(&pth[t],NULL,threadFunc,*(fp + t)); t++; } printf("main waiting for all threads to terminate...\n"); for (t=0; t < numThreads;t++){ pthread_join(pth[t],NULL); } } 

Haskell code ( t.hs ) - it calls create_threads in mt.c above with Storable Vector of FunPtr for the Haskell f function (after applying the first three arguments to f ):

 {-# LANGUAGE BangPatterns #-} import Control.Concurrent (forkIO, threadDelay, MVar, newEmptyMVar, putMVar, takeMVar) import qualified Data.Vector.Storable.Mutable as MSV import qualified Data.Vector.Storable as SV import Control.Monad.Primitive (PrimState) import Control.Monad (mapM, forM_) import Foreign.Ptr (Ptr, FunPtr) import Foreign.C.Types (CInt) type Length = CInt -- | f is a function that is called back by create_threads in mt.c f :: MVar Int -> MSV.MVector (PrimState IO) CInt -> Length -> CInt -> IO () fmvlx = do !i <- takeMVar m case (i< fromIntegral l) of True -> MSV.unsafeWrite vix >> print x >> putMVar m (i+1) False -> return () -- overflow -- a "wrapper" import gives us a converter for converting a Haskell function to a foreign function pointer foreign import ccall "wrapper" wrap :: (CInt -> IO()) -> IO (FunPtr (CInt -> IO())) foreign import ccall safe "create_threads" createThreads :: Ptr (FunPtr (CInt -> IO())) -> CInt -> IO() main = do let threads = [1..4] m <- mapM (\x -> newEmptyMVar) $ threads -- intialize mvars with 0 forM_ m $ \x -> putMVar x 0 let l = 10 -- intialize vectors of length 10 that will be filled by function f v <- mapM (\x -> MSV.new l) threads -- create a list of function pointers to partial function - the partial function is obtained by applying first three arguments to function f lf <- mapM (\(x,y) -> wrap (fxy (fromIntegral l))) $ zip mv -- convert above function list to a storable vector of function pointers let fv = SV.fromList lf -- call createThreads with storable vector of function pointers, and number of threads - createThreads will spawn threads which will use function pointers for callback SV.unsafeWith fv $ \x -> createThreads x (fromIntegral $ length threads) 

Please ignore the unsafe parts of the code - my goal here is to test the callback using Haskell FFI with multi-threaded C code. When I compile it and run it, I get the following error:

 $ ghc -O2 t.hs mt.c -lpthread [1 of 1] Compiling Main ( t.hs, to ) Linking t ... $ ./t main waiting for all threads to terminate... t: schedule: re-entered unsafely. Perhaps a 'foreign import unsafe' should be 'safe'? $ uname -a Darwin desktop.local 11.2.0 Darwin Kernel Version 11.2.0: Tue Aug 9 20:54:00 PDT 2011; root:xnu-1699.24.8~1/RELEASE_X86_64 x86_64 $ ghc --version The Glorious Glasgow Haskell Compilation System, version 7.0.3 

A schedule error occurs only if I have C threads that return the haskell f function. I assume that most likely there is an error in my code than an error in one of the libraries or GHC. So, I first want to check here for pointers to the cause of the error.

+6
source share
1 answer

In this case, the schedule error occurred because the haskell code was compiled without the -threaded option.

The haskell code calls the C create_threads function, which spawns multiple threads for the threadFunc function threadFunc C. threadFunc returns the Haskell f function. So, although the Haskell code was compiled without the -threaded option, it still leads to multiple C threads executing f .

It was a good catch for the GHC Runtime Scheduler to detect this control over multiple threads without a streaming runtime and flag it as an error. This is much better than a critical error at runtime. I realized the oversight when I checked the rts / schedule.c code in the GHC code base and saw a comment below. This tested me that threaded were not included:

 // Check whether we have re-entered the RTS from Haskell without // going via suspendThread()/resumeThread (ie a 'safe' foreign // call). 
+3
source

Source: https://habr.com/ru/post/906170/


All Articles