`stToIO` allows ST computations to run in multiple threads
Summary
The existence of the safe stToIO
function enables one to use ST
computations in multiple threads. This forces the use of atomic operations in ST
computations which can have a 4x performance impact.
Context at #22764 (comment 473050)
Steps to reproduce
We might think ST
computations are single-threaded an thus do not need to use atomic operations, so we might write a programST
function as follows:
programST :: STRef s Integer -> ST s ()
programST ref = do
n <- readSTRef ref
if n <= 0
then pure ()
else do
unsafeIOToST yield
writeSTRef ref $! n - 1
programST ref
(The unsafeIOToST yield
is only to make it more likely that weird concurrent interleavings occur)
But we can actually use this function in an unsafe way:
countdownST :: Integer -> IO Integer
countdownST n = do
ref <- stToIO (newSTRef n)
forkIO $ stToIO (programST ref)
stToIO (programST ref)
s <- stToIO (readSTRef ref)
pure s
main :: IO ()
main = do
putStrLn . show =<< countdownST 1000000
Compiling with ghc -O2 T.hs -threaded
and running with ./T +RTS -N2
sometimes gives me unexpected results:
$ ./T +RTS -N2
129
$ ./T +RTS -N2
100
Click to expand full repro source code
import Control.Monad.ST
import Control.Monad.ST.Unsafe
import Control.Concurrent
import Data.STRef
programST :: STRef s Integer -> ST s ()
programST ref = do
n <- readSTRef ref
if n <= 0
then pure ()
else do
unsafeIOToST yield
writeSTRef ref $! n - 1
programST ref
countdownST :: Integer -> IO Integer
countdownST n = do
ref <- stToIO (newSTRef n)
forkIO $ stToIO (programST ref)
stToIO (programST ref)
s <- stToIO (readSTRef ref)
pure s
main :: IO ()
main = do
putStrLn . show =<< countdownST 1000000
Question
The paper "Lazy Functional State Threads" mentions multiple times that ST
computations are supposed to be single-threaded, however the possibility of using RealWorld
makes it possible to run "entangled" ST
computations in multiple threads at the same time.
Is this intended to be possible?
Are the costs of requiring atomic operations worth the utility of the stToIO
function? Or should the stToIO
function be removed?