Atomicity of `readChan`?

yes because the issue is not within readChan but with race!

Sorry, I’m reading and replying in order. I see your solution, which I’m going through now

Thanks for linking this. This is what we use at work, but I’m trying to write the simplest possible Servant cookbook recipe, so I’m trying to not introduce new dependencies

This is interesting. I’m learning a lot about masking.

Unfortunately, I think the read can never happen faster than 5 milliseconds here, due to the threadDelay in the loop. That’s not doing quite what I set out to do with race, whereby I was hoping to print a value at least once per 5 milliseconds, but faster if the writer thread write more values.

My answer was mainly to demonstrate masking behaviour, it’s possible that it doesn’t faithfully reproduce the intended behaviour for your original program.

1 Like

Your original program also does not print anything if the queue is empty. What do you want it to print in that case?

Maybe something like this is what you want:

{-# LANGUAGE NumericUnderscores #-}
import Control.Concurrent (forkIO, threadDelay, killThread)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Monad (forM_, forever)
import System.IO (hSetBuffering, BufferMode (LineBuffering), stdout)

main :: IO ()
main = do
  chan <- newChan
  -- Writer thread writes a number every 10ms to a channel
  _ <-
    forkIO
      ( forM_
          [(1 :: Int) ..]
          (\i -> threadDelay 10_000 *> writeChan chan i)
      )
  hSetBuffering stdout LineBuffering -- otherwise threads get killed mid-print
  let
    tick = forkIO $ forever $ threadDelay 5_000 *> putStrLn "tick"
    go t = do
      x <- readChan chan
      killThread t
      print x
      t' <- tick
      go t'
  t <- tick
  go t
2 Likes

This is great! In practice I’ll be writing to a Chan rather than printing, and this is easy to extend.

Thank you to everyone who replied here. I learned a lot about readChan footguns and masking exceptions.

3 Likes

How about having two threads writing to the Chan, one of them being being the 5_000-spaced ticks? :thinking: The Chan could emit Either Tick SomeOtherStuff.

Instead of race, you could use waitEither on both the delay and the channel read. Then you would know which one finished, and the other would still be running. That might be cleaner overall.

You’d need some logic about what is actually meant by “at least once every 5 ms”, and a little bit to handle the initial wait before you have a value to repeat.

3 Likes