为什么MVar的弱指针最终确定,即使MVar仍可访问?

时间:2012-05-29 15:38:07

标签: haskell weak-references

我看到混合MVar和Weak的问题:即使MVar仍然可以从函数的范围访问(并且主线程阻塞它),Weak认为它应该最终确定。在启用优化的情况下编译时,似乎只会发生此行为。

平台:64位Linux

转载GHC版本:6.10.4,6.12.3,7.0.4,7.2.2,7.4.1

module Main (main) where

import           Control.Concurrent
import           Control.Monad (forever, forM_)
import           Data.IORef
import           System.Mem
import           System.Mem.Weak

dispatchPendingCalls :: IORef [Weak (MVar ())] -> IO ()
dispatchPendingCalls ref = forever $ do
    threadDelay 100000

    pending <- atomicModifyIORef ref (\p -> ([], p))
    forM_ pending (\weak -> do
        maybeMVar <- deRefWeak weak
        case maybeMVar of
            Just mvar -> putMVar mvar ()
            Nothing -> putStrLn "dispatchPendingCalls: weak mvar is Nothing")

call :: IORef [Weak (MVar ())] -> IO ()
call ref = do
    mvar <- newEmptyMVar
    weak <- mkWeakPtr mvar (Just (putStrLn "call: finalising weak"))

    putStrLn "call: about to insert weak into list"
    atomicModifyIORef ref (\p -> (weak : p, ()))
    putStrLn "call: inserted weak into list"
    performGC
    takeMVar mvar
    putStrLn "call: took from mvar"

main :: IO ()
main = do
    pendingCalls <- newIORef []
    _ <- forkIO (dispatchPendingCalls pendingCalls)
    call pendingCalls

预期产出:

$ ghc --make WeakMvar.hs
$ ./WeakMvar
call: about to insert weak into list
call: inserted weak into list
call: took from mvar
$

实际输出:

$ ghc --make -O2 WeakMvar.hs
$ ./WeakMvar
call: about to insert weak into list
call: inserted weak into list
call: finalizing weak
dispatchPendingCalls: weak mvar is Nothing
(never exits)

为什么会这样?如果我正确地阅读System.Mem.Weak文档,那么takeMVar mvar行应该保持mvar活着,从而保持其弱指针有效。相反,弱指针认为MVar在对takeMVar的调用返回之前已无法访问

2 个答案:

答案 0 :(得分:1)

尝试在BlockedIndefinitelyOnMVar的{​​{1}}中抓取takeMVar(由默认IIR处理,因此您无法看到)。我想使用call会阻止GC注意您对WeakMVar的引用,因此会收集垃圾?

答案 1 :(得分:1)

这几乎可以肯定是因为GHC的优化倾向于删除附加终结器的数据结构,导致终结器运行得太早。也就是说,终结器引用MVar数据构造函数而不是底层MVar#current docs对此有一些警告。如果我使用Control.Concurrent.MVar.mkWeakMVar,我会看到预期的输出(使用ghc-7.6.3)。