Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does lazy evaluation interplay with MVars?

Tags:

haskell

Let's say I have multiple threads that are reading from a file and I want to make sure that only a single thread is reading from the file at any point in time.

One way to implement this is to use an mvar :: MVar () and ensure mutual exclusion as follows:

thread = do
   ...
   _ <- takeMVar mvar
   x <- readFile "somefile"  -- critical section
   putMVar mvar ()
   ...
   -- do something that evaluates x. 

The above should work fine in strict languages, but unless I'm missing something, I might run into problems with this approach in Haskell. In particular, since x is evaluated only after the thread exits the critical section, it seems to me that the file will only be read after the thread has executed putMVar, which defeats the point of using MVars in the first place, as multiple threads may read the file at the same time.

Is the problem that I'm describing real and, if so, how do I get around it?

like image 407
theory_student Avatar asked Aug 07 '20 05:08

theory_student


1 Answers

Yes, it's real. You get around it by avoiding all the base functions that are implemented using unsafeInterleaveIO. I don't have a complete list, but that's at least readFile, getContents, hGetContents. IO actions that don't do lazy IO -- like hGet or hGetLine -- are fine.

If you must use lazy IO, then fully evaluate its results in an IO action inside the critical section, e.g. by combining rnf and evaluate.

Some other commentary on related things, but that aren't directly answers to this question:

  • Laziness and lazy IO are really separate concepts. They happen to share a name because humans are lazy at naming. Most IO actions do not involve lazy IO and do not run into this problem.

  • There is a related problem about stuffing unevaluated pure computations into your MVar and accidentally evaluating it on a different thread than you were expecting, but if you avoid lazy IO then evaluating on the wrong thread is merely a performance bug rather than an actual semantics bug.

like image 191
Daniel Wagner Avatar answered Sep 30 '22 02:09

Daniel Wagner