以下是在運行時拋出計劃錯誤(GHC 7.0.3,Mac OS 10.7,x86_64)的Haskell/C FFI代碼。我尋找錯誤的解釋,但沒有找到任何相關的。使用Haskell回調函數調用多線程C FFI時的計劃錯誤
C代碼(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代碼(t.hs
) - 它在mt.c
上述調用create_threads
與FunPtr
Storable Vector
到Haskell的功能f
(施加前三個參數後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()
f m v l x = do
!i <- takeMVar m
case (i< fromIntegral l) of
True -> MSV.unsafeWrite v i x >> 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 (f x y (fromIntegral l))) $ zip m v
-- 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)
請忽略代碼中的不安全部分 - 我的目標是使用Haskell FFI測試多線程回調附帶C代碼。當我編譯它,並運行它,我得到下面的錯誤:
$ ghc -O2 t.hs mt.c -lpthread
[1 of 1] Compiling Main (t.hs, t.o)
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
如果我的C線程調用回哈斯克爾函數f時間表錯誤僅發生。我想這很可能是我的代碼中存在一個錯誤,比其中一個庫或GHC中存在一個錯誤。所以,我想首先在這裏查看關於錯誤原因的指針。
由於您找到了解決方案,您應該將其作爲答案發布。 – 2012-01-17 13:23:48
謝謝,@john。剛剛添加了答案。 – Sal 2012-01-17 15:07:46
@hammar,感謝編輯,以反映發佈的答案。 – Sal 2012-01-17 15:12:03