Ticket #4154 (closed bug: fixed)

Opened 3 years ago

Last modified 14 months ago

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 Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:

Description

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

Changed 3 years ago by simonmar

  • priority changed from normal to high
  • os changed from Windows to Unknown/Multiple
  • milestone set to 6.14.1

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

Changed 3 years ago by SamAnklesaria

  • owner set to SamAnklesaria

Changed 3 years ago by SamAnklesaria

  • owner SamAnklesaria deleted

Changed 3 years ago by simonmar

  • status changed from new to closed
  • resolution set to fixed

Fixed by deprecating isEmptyChan and unGetChan (see above).

Changed 3 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?

Changed 14 months 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.