[GHC] #11760: runST with lazy blackholing breaks referential transparency
GHC
ghc-devs at haskell.org
Mon Mar 28 08:26:44 UTC 2016
#11760: runST with lazy blackholing breaks referential transparency
-------------------------------------+-------------------------------------
Reporter: Yuras | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.10.3
Keywords: | Operating System: Unknown/Multiple
Architecture: | Type of failure: None/Unknown
Unknown/Multiple |
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
A thunk created with `runST` can be evaluated twice by different threads
producing different results. An example (taken from
https://twitter.com/obadzz/status/714081240475951105):
{{{#!hs
{-# LANGUAGE RecordWildCards #-}
import qualified Data.STRef.Lazy as S
import Control.Monad
import Control.Monad.ST.Lazy
import Control.Concurrent
data ListRef s a = ListRef
{ element :: a
, readCounter :: Int
, rest :: Maybe (S.STRef s (ListRef s a))
}
toList :: S.STRef s (ListRef s a) -> ST s [(a, Int)]
toList r = do
ListRef{..} <- S.readSTRef r
S.modifySTRef r $ \e -> e
{ readCounter = readCounter + 1
}
xs <- maybe (return []) toList rest
return $ (element, readCounter) : xs
circularList :: ST s (S.STRef s (ListRef s Char))
circularList = do
x3 <- S.newSTRef (ListRef 'c' 0 Nothing)
x2 <- S.newSTRef (ListRef 'b' 0 (Just x3))
x1 <- S.newSTRef (ListRef 'b' 0 (Just x2))
S.modifySTRef x3 $ \e -> e
{ rest = Just x1
}
return x1
l :: [(Char, Int)]
l = take 15 $ runST $ circularList >>= toList
main :: IO ()
main = do
void $ forkIO $ print l
void $ forkIO $ print l
void getLine
print l
}}}
The output (run multiple times to reproduce):
{{{
$ ghc --make -O -threaded -outputdir=.build test.hs
[1 of 1] Compiling Main ( test.hs, .build/Main.o )
Linking test ...
$ ./test +RTS -N2
[('b',0),('b',0),('c',0),('b',1),('b',1),('c',1),('b',2),('b',2),('c',2),('b',3),('b',3),('c',3),('b',5),('b',4),('c',4)]
[('b',0),('b',0),('c',0),('b',1),('b',1),('c',1),('b',2),('b',2),('c',2),('b',3),('b',3),('c',3),('b',4),('b',4),('c',4)]
[('b',0),('b',0),('c',0),('b',1),('b',1),('c',1),('b',2),('b',2),('c',2),('b',3),('b',3),('c',3),('b',4),('b',4),('c',4)]
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.10.1
$
}}}
Note that the last 3 elements are `('b',5),('b',4),('c',4)` or
`('b',4),('b',4),('c',4)`.
I was able to reproduce it with few weeks old HEAD. With `-feager-
blackholing` it works as expected.
`unsafePerformIO` uses `noDuplicate` to prevent such kind of issue. Should
`runST` do something similar?
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/11760>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list