pthread_cond_timedwait不会在GHC FFI中返回

art*_*tem 8 c haskell posix mutex ghc

我试图实现Control.Concurrent.MVar驻留在共享内存中的Haskell ,并允许使用POSIX功能在多个独立进程/程序之间进行通信.但是我失败了很多僵局.

问题是pthread_cond_timedwait有时候不会在GHC FFI中调用(尽管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));
}
Run Code Online (Sandbox Code Playgroud)

(加上每个命令后的错误检查和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));
Run Code Online (Sandbox Code Playgroud)

完整代码. 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
Run Code Online (Sandbox Code Playgroud)

完整代码. 将FFI更改interruptibleunsafe不会阻止死锁.有时死锁每隔一次运行一次,有时它只在50次运行后发生(其余的按预期执行).

我的猜测是GHC可能会干扰POSIX互斥体的工作与一些OS信号处理,但我不知道GHC内部足以验证它.

是我做了一些愚蠢的错误,还是我需要添加一些特殊的技巧才能让它在GHC FFI中运行?

PS:我的调查的README的最新版本可在以下网站获得interprocess mvar-fail.

更新13.06.2018:我试图通过以下功能代码暂时阻止所有OS信号:

sigset_t mask, omask;
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &omask);
...
sigprocmask(SIG_SETMASK, &omask, NULL);
Run Code Online (Sandbox Code Playgroud)

这没有用.

art*_*tem 2

嗯,正如所料,这是我的错——一个非常 C 初学者的错误。从初始化片段中可以看出,我将互斥体和条件变量保留在一个结构中。从这里的代码片段中看不到,但可以通过我提供的链接(在 github 上)看到,我正在将该结构复制到共享内存中。不仅互斥体不允许这样做,而且在初始化结构中的所有内容之前我还愚蠢地复制了它。

也就是说,我只是复制了一个 C 结构,我应该在其中设置一个指针。

最令人惊讶的是代码有时仍然有效。 这是错误代码的链接。