3

我看到混合 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

4

2 回答 2

1

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

于 2013-08-09T03:58:38.490 回答
1

尝试在in 中捕获BlockedIndefinitelyOnMVar(默认情况下会处理 IIR,因此您不会看到它)。我猜 using会让 GC 不会注意到你对in的引用,所以它会被垃圾收集?takeMVarcallWeakMVardispatchPendingCalls

于 2012-05-29T16:08:54.533 回答