2012-05-29 47 views
3

我看到一個混合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的呼叫返回。

+0

供參考:他提交了一個bug報告http://hackage.haskell.org/trac/ghc/ticket/6130 據此,應該在GHC 7.6.1中修復 – Chetan

回答

1

嘗試在takeMVarcall中捕獲BlockedIndefinitelyOnMVar(這是由默認IIR處理的,所以你不會看到它)。我猜想使用Weak可以讓GC注意到你的參考dispatchPendingCalls中的MVar,所以它會被垃圾收集?

+0

使用catch(takeMVar mvar)( \ BlockedIndefinitelyOnMVar - > putStrLn「blocked」)'不會改變行爲。此外,GC不應該垃圾收集mvar,因爲'takeMVar'應該保持它活着。 –

+0

不知道發生了什麼,但是運行時肯定會收集一個線程,當所有其他引用消失或被阻止時,該線程將無限期地封鎖在MVar上,並帶有'takeMVar'。 – jberryman

1

這幾乎可以肯定是因爲GHC的優化傾向於刪除附加了終結器的數據結構,導致終結器運行得太早。也就是說,終結器引用的是MVar數據構造函數,而不是底層的MVar#current docs對此有一些警告。如果我使用Control.Concurrent.MVar.mkWeakMVar,我會看到預期的輸出(使用ghc-7.6.3)。