`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?