[英]pthread_cond_timedwait does not return in GHC FFI
我嘗試實現駐留在共享內存中的Haskell Control.Concurrent.MVar
,並允許使用POSIX功能在多個獨立的進程/程序之間進行通信。 但是我失敗了很多,陷入僵局。
問題在於,有時在GHC FFI中不會調用pthread_cond_timedwait
(盡管可interruptible
或unsafe
)。 經過幾天的拼命嘗試來解決該問題,我決定縮減代碼並要求社區提供幫助。 不幸的是,我無法將問題壓縮為此處可粘貼的幾行代碼。 因此,我將(盡可能小的)代碼與有關如何復制問題的說明一起存儲在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上)可以看到的是,我正在將該結構復制到共享內存中。 不僅互斥體不允許這樣做,而且在初始化結構中的所有內容之前,我還愚蠢地復制了它。
這里最令人驚訝的是該代碼有時仍然可以工作。 這是錯誤代碼的鏈接。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.