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 に答える