Opened 7 years ago

Closed 7 years ago

Last modified 5 years ago

#4154 closed bug (fixed)

Deadlock in Chan module

Reported by: NeilMitchell Owned by:
Priority: high Milestone: 7.0.1
Component: libraries/base Version: 6.12.3
Keywords: Cc: ndmitchell@…, mmitar@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Incorrect result at runtime Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


The following program:

module Main where

import Control.Concurrent

main :: IO ()
main = do
    todo <- newChan
    forkIO $ readChan todo
    putStrLn "Before isEmptyChan"
    b <- isEmptyChan todo
    putStrLn "After isEmptyChan"
    writeChan todo ()

Gives the output:

$ ghc --make Main.hs -threaded && ./Main.exe
Before isEmptyChan
Main.exe: thread blocked indefinitely in an MVar operation

I think that's a bug. Note that if the putStrLn statements are removed then it works, but I think that's because the printing introduces a delay that lets the other thread run.

Change History (6)

comment:1 Changed 7 years ago by simonmar

Milestone: 6.14.1
Operating System: WindowsUnknown/Multiple
Priority: normalhigh

I can't see a way to fix this in the implementation of Chan while retaining the properties and the other operations it has. The problem is that readChan holds empty the read end of the Chan, but isEmptyChan and unGetChan (see #3527) also need to take the read end, so they deadlock if there is a blocked reader on an empty Chan.

My suggestion is to deprecate both isEmptyChan and unGetChan, with a message explaining the problem and directing people to TChan instead. TChan works, but lacks the fairness property of Chan, and is probably only suitable when you have a small number of readers. We could make an MVar version with similar properties, but it wouldn't perform any better than TChan and wouldn't be composable, so this seems the best compromise:

  • Chan has fairness, single-wakeup (good for multiple readers)
  • TChan has isEmptyChan and unGetChan

comment:2 Changed 7 years ago by SamAnklesaria

Owner: set to SamAnklesaria

comment:3 Changed 7 years ago by SamAnklesaria

Owner: SamAnklesaria deleted

comment:4 Changed 7 years ago by simonmar

Resolution: fixed
Status: newclosed

Fixed by deprecating isEmptyChan and unGetChan (see above).

comment:5 Changed 6 years ago by mitar

Cc: mmitar@… added

The only case where I have been using isEmptyChan was in my version of non-blocking readChan, returning Maybe. Is it possible to define instead of isEmptyChan some non-blocking version of readChan with tryTakeMVar and tryPutMVar?

comment:6 Changed 5 years ago by singpolyma

I would also like a non-blocking readChan, and while tryTakeMVar seems like the right solution for that, from reading this report it seems that isEmptyChan will not cause a deadlock in this case, because no one is reading the Chan (unless you have multiple readers).

Note: See TracTickets for help on using tickets.