簡體   English   中英

pthread_cond_timedwait在GHC FFI中不返回

[英]pthread_cond_timedwait does not return in GHC FFI

我嘗試實現駐留在共享內存中的Haskell Control.Concurrent.MVar ,並允許使用POSIX功能在多個獨立的進程/程序之間進行通信。 但是我失敗了很多,陷入僵局。

問題在於,有時在GHC FFI中不會調用pthread_cond_timedwait (盡管可interruptibleunsafe )。 經過幾天的拼命嘗試來解決該問題,我決定縮減代碼並要求社區提供幫助。 不幸的是,我無法將問題壓縮為此處可粘貼的幾行代碼。 因此,我將(盡可能小的)代碼與有關如何復制問題的說明一起存儲在github上, 這里是該問題當前狀態的永久鏈接mvar-fail分支)。

本質上,采用和放入mvar的函數如下所示:

int mvar_take(MVar *mvar, ...) {
   pthread_mutex_timedlock(&(mvar->statePtr->mvMut), &timeToWait);
   while ( !(mvar->statePtr->isFull) ) {
     pthread_cond_signal(&(mvar->statePtr->canPutC));
     pthread_cond_timedwait(&(mvar->statePtr->canTakeC), &(mvar->statePtr->mvMut), &timeToWait);
   }
   memcpy(localDataPtr, mvar->dataPtr, mvar->statePtr->dataSize);
   mvar->statePtr->isFull = 0;
   pthread_mutex_unlock(&(mvar->statePtr->mvMut));
}

int mvar_put(MVar *mvar, ...) {
   pthread_mutex_timedlock(&(mvar->statePtr->mvMut), &timeToWait);
   while ( mvar->statePtr->isFull ) {
     pthread_cond_signal(&(mvar->statePtr->canTakeC));
     pthread_cond_timedwait(&(mvar->statePtr->canPutC), &(mvar->statePtr->mvMut), &timeToWait);
   }
   memcpy(mvar->dataPtr, localDataPtr, mvar->statePtr->dataSize);
   mvar->statePtr->isFull = 1;
   pthread_mutex_unlock(&(mvar->statePtr->mvMut));
}

(加上每個命令后的錯誤檢查和printfs)。 mvar_take完整代碼。 初始化發生如下:

pthread_mutexattr_init(&(s.mvMAttr));
pthread_mutexattr_settype(&(s.mvMAttr), PTHREAD_MUTEX_ERRORCHECK);
pthread_mutexattr_setpshared(&(s.mvMAttr), PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&(s.mvMut), &(s.mvMAttr));
pthread_condattr_init(&(s.condAttr));
pthread_condattr_setpshared(&(s.condAttr), PTHREAD_PROCESS_SHARED);
pthread_cond_init(&(s.canPutC), &(s.condAttr));
pthread_cond_init(&(s.canTakeC), &(s.condAttr));

完整代碼。 Haskell部分如下所示:

foreign import ccall interruptible "mvar_take"
  mvar_take :: Ptr StoredMVarT -> Ptr a -> CInt -> IO CInt
foreign import ccall interruptible "mvar_put"
  mvar_put :: Ptr StoredMVarT -> Ptr a -> CInt -> IO CInt

takeMVar :: Storable a => StoredMVar a -> IO a
takeMVar (StoredMVar _ fp) = withForeignPtr fp $ \p -> alloca $ \lp -> do
    r <- mvar_take p lp
    if r == 0
    then peek lp
    else throwErrno $ "takeMVar failed with code " ++ show r

putMVar :: Storable a => StoredMVar a -> a -> IO ()
putMVar (StoredMVar _ fp) x = withForeignPtr fp $ \p -> alloca $ \lp -> do
    poke lp x
    r <- mvar_put p lp
    unless (r == 0)
      $ throwErrno $ "putMVar failed with code " ++ show r

完整代碼。 將FFI從可interruptible更改為unsafe不會防止死鎖。 有時死鎖發生在第二次運行中,有時死機發生在僅運行50次后(其余運行按預期執行)。

我的猜測是,GHC可能會通過某些OS信號處理來干擾POSIX互斥鎖的工作,但是我不知道GHC內部足以對其進行驗證。

是我做錯了什么愚蠢的事情,還是需要添加一些特殊技巧使它在GHC FFI中起作用?

PS:帶有我調查的自述文件的最新版本可在interprocess mvar-fail

更新13.06.2018 :我嘗試通過以下功能代碼來暫時阻止所有OS信號:

sigset_t mask, omask;
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &omask);
...
sigprocmask(SIG_SETMASK, &omask, NULL);

這沒有幫助。

好吧,正如預期的那樣,這是我的錯-一個非常C開頭的錯誤。 從初始化代碼片段可以看出,我將互斥量和條件變量保留在一個結構中。 從這里的片段中看不到,但是通過我給的鏈接(在github上)可以看到的是,我正在將該結構復制到共享內存中。 不僅互斥體不允許這樣做,而且在初始化結構中的所有內容之前,我還愚蠢地復制了它。

也就是說,我只是復制了應該設置指針的C結構

這里最令人驚訝的是該代碼有時仍然可以工作。 這是錯誤代碼的鏈接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM