簡體   English   中英

Control.Concurrent.Chan readChan中的理論死鎖

[英]theoretical deadlock in Control.Concurrent.Chan readChan

瀏覽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_mask_ 相反,它在restore時調用,因此可以啟用異步異常。

這是我的工作。

所以在readMVar中......

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

...盡管使用了mask_ ,運行時可能會在阻塞的takeMVar引發異常。 請注意,在該功能中,不需要實際處理該情況; readMVar工作,在這種情況下我們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

編輯 :雖然readMVar也是mask_ -ed,所以現在我不明白為什么要么選擇modifyMVarMasked或者修改modifyMVar都會產生影響......

注釋似乎暗示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

暫無
暫無

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

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