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