控制中的理论僵局.当前.Chan读Chan



浏览readChan的源代码,可以找到以下实现和注释,从版本4.6的基础开始:

-- |Read the next value from the 'Chan'.
readChan :: Chan a -> IO a
readChan (Chan readVar _) = do
modifyMVarMasked readVar $ read_end -> do -- Note [modifyMVarMasked]
(ChItem val new_read_end) <- readMVar read_end
-- Use readMVar here, not takeMVar,
-- else dupChan doesn't work
return (new_read_end, val)
-- Note [modifyMVarMasked]
-- This prevents a theoretical deadlock if an asynchronous exception
-- happens during the readMVar while the MVar is empty.  In that case
-- the read_end MVar will be left empty, and subsequent readers will
-- deadlock.  Using modifyMVarMasked prevents this.  The deadlock can
-- be reproduced, but only by expanding readMVar and inserting an
-- artificial yield between its takeMVar and putMVar operations.

在基础版本4.6之前,使用的是modifyMVar,而不是modifyMVarMasked。

我不明白这里要解决什么理论问题。最后一句话指出,如果线程在包含readMVar的takeMVar和putMVar之间让步,则存在问题。但是,当readMVar在mask_下执行时,异步异常如何阻止成功获取后的put?

感谢您对理解此处问题的任何帮助。

让我们比较modifyMVarmodifyMVarMasked的来源,因为代码从使用一个变成了使用另一个:

modifyMVar m io =
mask $ restore -> do
a      <- takeMVar m
(a',b) <- restore (io a) `onException` putMVar m a
putMVar m a'
return b
modifyMVarMasked m io =
mask_ $ do
a      <- takeMVar m
(a',b) <- io a `onException` putMVar m a
putMVar m a'
return b

这里的关键是modifyMVar在执行第二个参数之前调用restore,而modifyMVarMasked则不调用。因此,在旧版本的代码中,readMVar不是在mask_下调用的,正如您在问题中所说的那样!相反,它是在restore下调用的,因此异步异常毕竟可以启用。

这是我的工作。

所以在readMVar中。。。

readMVar :: MVar a -> IO a
readMVar m =
mask_ $ do
a <- takeMVar m
putMVar m a
return a

尽管CCD_ 9,运行时可能在被阻塞的CCD_。请注意,在该函数中,实际上不需要处理这种情况;要么readMVar工作了,在这种情况下,我们不会出现异步异常,要么takeMVar永远不会成功;无论哪种方式,我们都不会让MVar空着来破坏它。(这是正确的吗?这是我从自己的相关问题的答案中得到的。)

modifyMVarmodifyMVarMasked分别为:

modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar m io =
mask $ restore -> do
a      <- takeMVar m
(a',b) <- restore (io a) `onException` putMVar m a
putMVar m a'
return b
modifyMVarMasked :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVarMasked m io =
mask_ $ do
a      <- takeMVar m
(a',b) <- io a `onException` putMVar m a
putMVar m a'
return b

其中差异在modifyMVar中,在io a中恢复屏蔽状态(即异步异常可能变为未屏蔽),在我们的情况下,它或多或少是readMVar

EDIT:虽然readMVar也是mask_,但现在我不明白为什么选择modifyMVarMaskedmodifyMVar会有什么不同。。。

该注释似乎暗示yield(插入到readMVar中)是可中断的(我在任何地方都找不到这方面的文档),因此可能会引发异步异常,在这种情况下,readVar将被还原(在当前和4.6之前的版本中),但在非空队列中,读卡器将看到一个空队列和块。

您可能有兴趣阅读此提交中的GHC trac,它有一个示例程序,当Control.Concurrent.Chan和测试程序都编译为-O0时,该程序可以始终如一地再现此错误

https://ghc.haskell.org/trac/ghc/ticket/6153

同理:

https://ghc.haskell.org/trac/ghc/ticket/5870

最新更新