I'm trying to wrap my head around how to correctly communicate between threads in Haskell.
I have multiple threads that read some state, and whenever it changes they need to do some update. Then I have multiple threads that can change that state.
I first looked at MVar, but that won't do because it blocks when writing and only one thread can read.
So best thing I found is a TVar, I can write to it and read the current state from it. So I could just have the threads that read poll the state for changes every second or so (that small delay doesn't really matter, nor does it matter that it's possible a thread will miss in-between states). That would work.
However, I was wondering, is there a way that doesn't need to polling? Some other primitive I could use? Or a way I can "listen" to a TVar?
Simply read and retry
when the value is the same as before.
onChanges :: Eq a => (a -> IO ()) -> TVar a -> IO b
onChanges f tvar = readTVarIO tvar >>= go where
go a = do
a' <- atomically do
a' <- readTVar tvar
when (a == a') retry
pure a'
f a'
go a'
Don't worry -- this isn't expensive. Each retry
will block until the TVar
is written to again. If the equality check is expensive compared to running f
, or if you want to run f
on every write even if the value hasn't changed, you can additionally store an Int
or similar in your TVar
saying how many times it's been written, and check that to decide whether to retry
or not.