Theoretical impasse in Control.Concurrent.Chan readChan

When viewing the readChan source, you will find the following implementation and comment, starting with version 4.6 of the database:

-- |Read the next value from the 'Chan'.
readChan :: Chan a -> IO a
readChan (Chan readVar _) = do
  modifyMVarMasked readVar $ \read_end -> do -- Note [modifyMVarMasked]
    (ChItem val new_read_end) <- readMVar read_end
        -- Use readMVar here, not takeMVar,
        -- else dupChan doesn't work
    return (new_read_end, val)

-- Note [modifyMVarMasked]
-- This prevents a theoretical deadlock if an asynchronous exception
-- happens during the readMVar while the MVar is empty.  In that case
-- the read_end MVar will be left empty, and subsequent readers will
-- deadlock.  Using modifyMVarMasked prevents this.  The deadlock can
-- be reproduced, but only by expanding readMVar and inserting an
-- artificial yield between its takeMVar and putMVar operations.

Prior to the basic version 4.6, the MVar modifier was used instead of the MVarMasked modification.

I do not understand why the theoretical problem has been solved here. The last sentence says that there is a problem if the stream gives between takeMVar and putMVar, which contain readMVar. But since readMVar runs under mask_, how can an async exception prevent put after successful execution?

Any help in understanding the problem is welcome.

+3
source share
3 answers

modifyMVar modifyMVarMasked, , :

modifyMVar m io =
  mask $ \restore -> do
    a      <- takeMVar m
    (a',b) <- restore (io a) `onException` putMVar m a
    putMVar m a'
    return b

modifyMVarMasked m io =
  mask_ $ do
    a      <- takeMVar m
    (a',b) <- io a `onException` putMVar m a
    putMVar m a'
    return b

, modifyMVar restore , modifyMVarMasked . readMVar mask_ , ! restore, .

+1

.

, readMVar...

readMVar :: MVar a -> IO a
readMVar m =
  mask_ $ do
    a <- takeMVar m
    putMVar m a
    return a

... mask_, takeMVar. , ; readMVar, , takeMVar ; MVar, . ( ? , .)

modifyMVar modifyMVarMasked:

modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar m io =
  mask $ \restore -> do
    a      <- takeMVar m
    (a',b) <- restore (io a) `onException` putMVar m a
    putMVar m a'
    return b

modifyMVarMasked :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVarMasked m io =
  mask_ $ do
    a      <- takeMVar m
    (a',b) <- io a `onException` putMVar m a
    putMVar m a'
    return b

... modifyMVar, (.. async, , ) io a, readMVar.

EDIT. readMVar mask_ -ed, , modifyMVarMasked modifyMVar ...

, , , yield ( readMVar) ( ), async, readVar ( , 4.6), .

0

All Articles