Turion
Turion

Reputation: 6156

Why is putStrLn not atomic?

To practice concurrent programming, I wrote the following (suboptimal) program, which repeatedly calculates the first prime bigger than whatever the user inputs:

import Control.Concurrent
import Control.Concurrent.Chan
import Control.Monad (forever)
primeAtLeast n = -- Some pure code that looks up the first prime at least as big as n

outputPrimeAtLeast n = putStrLn $ show $ (n, primeAtLeast n)

main = do
    chan <- newChan
    worker <- forkIO $ forever $ readChan chan >>= outputPrimeAtLeast
    forever $ (readLn :: (IO Int)) >>= (writeChan chan)
    killThread worker

I want to have a worker thread in the background that does the actual calculation and outputs (n, primeAtLeast n) as soon as it's finished.

What it's doing now: As soon as I enter a number n, it immediately outputs (n,, returns the control to the main thread, calculates primeAtLeast n in the background and outputs the second half primeAtLeast n) as soon as it's finished.

So is putStrLn not atomic? Or where is the problem?

Upvotes: 3

Views: 394

Answers (1)

chi
chi

Reputation: 116174

Try this:

outputPrimeAtLeast n = let p = primeAtLeast n in p `seq` putStrLn $ show (n, p)

The above forces the computation of the prime before the putStrLn is run.

Further, you may use print instead of putStrLn . show:

outputPrimeAtLeast n = let p = primeAtLeast n in p `seq` print (n, p)

Alternatively, you may use a putStrLn function which forces every single character before starting printing anything.

strictPutStrLn :: Show a => a -> IO ()
strictPutStrLn x = let str = show x in str `listSeq` putStrLn str

listSeq :: [a] -> b -> b
listSeq []     w = w
listSeq (x:xs) w = x `seq` listSeq xs w

Upvotes: 3

Related Questions