> Also note linked from the global variables page on the wiki is:
> http://www.haskell.org/haskellwiki/Top_level_mutable_state
>
> Important for the `unsafePerformIO` option is the NOINLINE pragma to ensure
> that only one global variable exists. In the STM case it is also important
> to use `newTVarIO` rather then `unsafePerformIO $ atomically newTVar` which
> does not work.
>
> Ryan
Hi
I think it isn't correct even with NOINLINE.
Read the paper "Haskell on a Shared-Memory Multiprocessor"
http://research.microsoft.com/pubs/67424/2005-haskell.pdf
Among other things, the paper presents these ideas:
(section 3.1) When multiple threads start to evaluate the same thunk
concurrently, we could use interlocked compare-and-swap instruction to
make sure that only one thread starts to evaluate it. However, the
compare-and-swap instruction is slow and executing it at every thunk
evaluation decreases performance. So, the designers avoid the
compare-and-swap instruction (section 3.2) and consequently there is a
race condition - there is a small window where two or more threads may
evaluate the same thunk concurrently.
Normally, this race condition doesn't matter, because Haskell is purely
functional language, so when multiple threads evaluate the same thunk they
end up with the same result - but in this particular example, where one is
using unsafePerformIO to construct a unique variable, it can hurt, because
you end up executing "unsafePerformIO $ newMVar 0" concurrently (section
3.5 of the paper).
So - code like this isn't correct, the numbers returned by "uniq" may not
be unique if we hit the race condition when evaluating "global":
global :: MVar Integer
global = unsafePerformIO $ newMVar 0
{-# NOINLINE global #-}
uniq = modifyMVar global (\i -> return (i+1,i))
Has anything changed since that time, so that this construct is safe?
Mikulas