Понимание BlockedIndefinitelyOnMVar в параллельном коде

Я задал этот вопрос пользователям ghc список рассылки и получил несколько полезных ответов, но до сих пор не понимаю, что происходит в этом коде.

По сути, я пытаюсь понять, как я могу поймать исключение BlockedIndefinitelyOnMVar, чтобы восстановить блокировку, которая, возможно, не была возвращена, и понять это исключение в целом.

Вот некоторый однопоточный код, который делает именно это:

-- This raises the exception only once and the lock is successfully restored:
main1 = do
    lock <- newMVar ()
    lockPrint "good1" lock
    takeMVar lock 
    putStrLn "main: took lock but didn't return it!"
    -- exception is raised and lock is restored here:
    lockPrint "good2" lock
    -- no exception raised:
    lockPrint "good3" lock
    readMVar lock
    putStrLn "great success"

lockPrint :: String -> MVar () -> IO ()
lockPrint name v =  takePrint `finally` put 
    where put = putMVar v () >> putStrLn (name++": replaced lock")
          takePrint = do
               e <- try $ takeMVar v :: IO (Either BlockedIndefinitelyOnMVar ())
               let printExc = putStrLn . ((name++": ")++) . show
                   printSuccess = const $ putStrLn (name++": success")
               either printExc printSuccess e

А вот версия main, которая демонстрирует поведение, которое я не понимаю. В частности, я не совсем уверен, почему в основном возникает исключение, хотя я вижу, что потоки на самом деле не запланированы, как я себе представляю.

main0 = do
    lock <- newMVar ()
    forkIO $ lockPrint "good1" lock

    threadDelay 100000
    takeMVar lock
    putStrLn "main: took lock but didn't return it!"

    -- raises blocked indefinitely exception
    forkIO $ lockPrint "good2" lock

    -- this should raise no exception if we were successful above:
    putStrLn "main: long pause..."
    threadDelay 2000000
    readMVar lock
    putStrLn "great success"

Извините, у меня возникли проблемы с придумыванием более простого примера. Вышеприведенное было скомпилировано с помощью: ghc --make -threaded -fforce-recomp experiments.hs


РЕДАКТИРОВАТЬ: Эдвард З. Ян написал действительно ясный блог пост об этом сегодня здесь. В результате на это исключение нельзя полагаться для выполнения чего-либо необычного.


person jberryman    schedule 27.07.2011    source источник


Ответы (1)


Во-первых, попытка справиться с BlockedIndefinitelyOnMVar не кажется хорошей идеей. Проще использовать withMVar чтобы всегда возвращалось содержимое MVar. При этом вы не должны получить это исключение в первую очередь, если у вас нет взаимоблокировки (что следует считать ошибкой и должно быть исправлено в коде).

person hammar    schedule 27.07.2011
comment
Разве это не та проблема, для решения которой был разработан STM? Зачем возиться с withMVar и вручную избегать взаимоблокировок, когда компьютер может сделать это за вас? - person jrockway; 28.07.2011