Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TVar that blocks read until change?

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?

like image 324
The Oddler Avatar asked Sep 01 '25 23:09

The Oddler


1 Answers

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.

like image 146
Daniel Wagner Avatar answered Sep 03 '25 18:09

Daniel Wagner