First, some background. I want a queue which I would like to operate in one of two different modes. In the first mode, I want to be able retrieve an element if one exists in the queue but not to block if there is no element. In the second mode, I want to be able to block until the queue has an element. (I am aware that I could use a specialized mechanism for each mode, but I'd like to factor out some common code and thus it would be easiest if I could use the same mechanism for both modes of operation.)
I could use Chan
, but according to the documentation I shouldn't use isEmptyChan
because it is deprecated due to potential for deadlock. This leaves me with TChan
. The tryReadTChan
function gives me exactly what I want for the first mode (i.e. I can check whether an element is present without blocking) but I am not sure exactly what readTChan
does. My mental model is that the atomically
block will keep retrying until an element is present in the channel, which means it will busy loop wasting CPU cycles; this is unlike readChan
(i.e., the non-STM version) which (if I understand correctly) will actually block execution of the thread until an element is available, as MVars are understood by the runtime threads scheduler.
So is TChan
like Chan
in that if I use readTChan
the runtime is smart enough to not schedule the calling thread until a value is available? Or will it waste a lot of CPU cycles continually polling into a value arrives?
STM blocking (via retry
) behaves as if it retries the transaction immediately, but the implementation is smarter: Since STM keeps track of what variables you've read as the transaction goes on, it knows that the transaction will behave the same way as long as those variables have the same values. So when a transaction fails, it'll block (won't actually retry) until one of the variables you used has changed. In the case of TChan
s, that means it'll block until someone writes to the TChan
.
I recommend Simon Marlow's slides on concurrent and parallel Haskell for a good introduction to STM (among other things).
Your mental model has the right denotational semantics, but misses out on an operational optimization STM makes. When a transaction retries in STM, it blocks until some TVar
it read from before the retry is changed. (And yes, TChan
is implemented in terms of TVar
.)
So executing a transaction has the same meaning as if the transaction was continually retrying - but the STM system is smart enough to not busy-loop if there's no chance of things going differently within the transaction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With