Nondeterminism in ‘pure’ function w/ parallel evaluation & memo combinators
The example below uses in-place copies (mostly to fix dependencies
and to make it more self-contained) of parallel
(modified),
unordered-containers
(modified), and hashable
. Its behavior is
nondeterministic, despite using only supposedly pure operations.
Aforementioned aside, it only depends on base
and deepseq
, and there's
no unsafePtrEq
involved at all. The affected fromListWith
uses foldl'
to effect in-place HashMap
updates using unsafe{Thaw,Freeze}
.
I don't see how references to intermediate versions of HashMap
could possibly leak
out, so in-place updates seem valid to me.
Strictifying (or even rnf
ing) the arguments to unsafeInsertWith
doesn't
help. The issue is reproducible with -O0
on HEAD
but not with -O2
; On
- 0.2 and 7.10.1, it can also be reproduced with
-O2
.
sum . map snd = sum . map snd . toList . fromListWith (+)
The above identity fails when the input contains values constructed using both memo combinators and parallel evaluation strategies.
I have identified the in-place-update that needs to be replaced with
copy-and-update to make things work―patch unsafeInsertWith.go
in unordered-containers-0.2.8.0/Data/HashMap/Strict.hs
, under the BitmapIndexed
pattern as follows:
- A.unsafeUpdateM ary i st'
- return t
+ let ary' = A.update ary i st'
+ return $ BitmapIndexed b ary'
Steps to reproduce:
git clone https://github.com/pacak/cuddly-bassoon && cd cuddly-bassoon
cabal new-build
dist-newstyle/build/gamebooksolver-0.1.0.0/build/gamebooksolver-solvebook02/gamebooksolver-solvebook02
This will either finish successfully producing no output, or will die
with an error
from the regroup
function in src/Solver.hs
.
Your machine must have at least 2 CPU cores to reproduce the problem; also
it will not show up with +RTS -N1
.
This issue was first reported here: https://github.com/tibbe/unordered-containers/issues/147