CAS operations do not work when compiled with -fhpc
Summary
Below is a short piece of code that works fine until it is compile with -fhpc
data MutableArray a = MutableArray (MutableArray# RealWorld a)
readArray :: MutableArray a -> Int -> IO a
readArray (MutableArray ma#) (I# i#) = IO (readArray# ma# i#)
casArray :: MutableArray a -> Int -> a -> a -> IO (Bool, a)
casArray (MutableArray ma#) (I# i#) expected new =
IO $ \s ->
case casArray# ma# i# expected new s of
(# s', failed#, actual #) -> (# s', (isTrue# failed#, actual) #)
newArray :: Int -> a -> IO (MutableArray a)
newArray (I# n#) a =
IO $ \s ->
case newArray# n# a s of
(# s', ma# #) -> (# s', MutableArray ma# #)
main :: IO ()
main = do
arr <- newArray 1 99
expected <- readArray arr 0
(hasFailed, actual) <- casArray arr 0 expected 100
current <- readArray arr 0
putStrLn $
unlines
[ "========== Boxed Array ==========="
, "Expected: " ++ show expected
, "Actual: " ++ show actual
, "Has CAS failed: " ++ show hasFailed
, "Current: " ++ show current
]
Regular compilation produces expected output:
========== Boxed Array ===========
Expected: 99
Actual: 100
Has CAS failed: False
Current: 100
But compiling the same thing with -fhpc
, results in a binary that produces different output 100% of the time:
========== Boxed Array ===========
Expected: 99
Actual: 99
Has CAS failed: True
Current: 99
It is clear from the output that CAS can never succeed with hpc
enabled, which makes it impossible to get coverage reports when testing atomic operations. Same behavior is observed for casSmallArray#
and casMutVar#
, but unsurprisingly not for the casByteArray#
.
Steps to reproduce
Attached file cas.hs reproduces the behavior for all affected CAS operations. Same file can be found in this repo Steps to reproduce (-threaded
isn't necessary, added just in case):
Correct behavior:
$ ghc -fforce-recomp -threaded cas.hs && ./cas
[1 of 1] Compiling Main ( cas.hs, cas.o )
Linking cas ...
========== Boxed Array ===========
Expected: 99
Actual: 100
Has CAS failed: False
Current: 100
========== Small Boxed Array ===========
Expected: 99
Actual: 100
Has CAS failed: False
Current: 100
========== MutVar ===========
Expected: 99
Actual: 100
Has CAS failed: False
Current: 100
========== ByteArray ===========
Expected: 99
Actual: 100
Has CAS failed: False
Current: 100
Broken:
$ rm *.tix; ghc -fforce-recomp -threaded -fhpc cas.hs && ./cas
[1 of 1] Compiling Main ( cas.hs, cas.o )
Linking cas ...
========== Boxed Array ===========
Expected: 99
Actual: 99
Has CAS failed: True
Current: 99
========== Small Boxed Array ===========
Expected: 99
Actual: 99
Has CAS failed: True
Current: 99
========== MutVar ===========
Expected: 99
Actual: 99
Has CAS failed: True
Current: 99
========== ByteArray ===========
Expected: 99
Actual: 100
Has CAS failed: False
Current: 100
Expected behavior
Expected behavior is that -fhpc
flag should not change semantics of casArray#
, casSmallArray#
and casMutVar#
Environment
- GHC version used: 7.10.3 - 8.10.1
Optional:
- Operating System: Ubuntu 18.04
- System Architecture: x86_64