我看到一個混合MVar和Weak的問題:即使MVar仍然可以從函數的作用域訪問(並且主線程阻塞了它),Weak認爲它應該被最終確定。這種行爲只有在啓用優化的情況下編譯時纔會發生。爲什麼指向MVar的弱指針已完成,即使MVar仍可訪問?
平臺: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
的呼叫返回。
供參考:他提交了一個bug報告http://hackage.haskell.org/trac/ghc/ticket/6130 據此,應該在GHC 7.6.1中修復 – Chetan