2012-01-17 17 views
5

Poniżej znajduje się kod Haskell/C FFI, który zgłasza błąd harmonogramu w czasie wykonywania (GHC 7.0.3, Mac OS 10.7, x86_64). Szukałem wyjaśnienia błędu, ale nie znalazłem nic istotnego.Błąd harmonogramu podczas wywoływania wielowątkowego C FFI z funkcją oddzwaniania Haskell

kodu 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); 
    } 
} 

kod Haskell (t.hs) - wywołuje create_threads w mt.c powyżej Storable Vector z FunPtr do funkcji Haskell f (po nałożeniu pierwszych trzech argumentów 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) 

Proszę zignorować niebezpieczne części w kodzie - moim celem jest przetestowanie oddzwonienia za pomocą Haskella FFI z multi-tre Przypisany kod C. Kiedy skompilować i uruchomić go, pojawia się błąd poniżej:

$ 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 

Błąd harmonogram dzieje się tylko wtedy, gdy mam gwinty C oddzwonić funkcyjny Haskell f. Sądzę, że jest bardziej prawdopodobne, że w moim kodzie występuje błąd, niż błąd w jednej z bibliotek lub GHC. Chciałbym tutaj najpierw sprawdzić wskaźniki dotyczące przyczyny błędu.

+1

Skoro znalazłeś rozwiązanie, powinieneś opublikować to jako odpowiedź. –

+0

Dzięki, @john. Właśnie dodałem odpowiedź. – Sal

+0

@hammar, dzięki za edytowanie odzwierciedlają wysłaną odpowiedź. – Sal

Odpowiedz

2

W tym przypadku wystąpił błąd schedule, ponieważ kod haskell został skompilowany bez opcji -threaded.

Kod haskell wywołuje funkcję C create_threads, która spawnuje wiele wątków dla funkcji C threadFunc. threadFunc wraca do funkcji Haskell f. Tak więc, mimo że kod Haskella jest kompilowany bez opcji -threaded, nadal powoduje to powstanie wielu wątków C wykonujących f.

To było dobre złapanie przez GHC Runtime Scheduler do wykrycia tego niedopatrzenia w wykonywaniu wielu wątków bez gwintowanego środowiska wykonawczego i oznaczenia go jako błędu. Jest to o wiele lepsze niż tajemnicza awaria czasu. Zdałem sobie sprawę z niedopatrzenia, gdy sprawdziłem kod rts/schedule.c w bazie kodu GHC i zobaczyłem poniższy komentarz. Dało mi to do zrozumienia, że ​​nieaktywne jest środowisko wykonawcze threaded:

// Check whether we have re-entered the RTS from Haskell without 
// going via suspendThread()/resumeThread (i.e. a 'safe' foreign 
// call). 
Powiązane problemy