Is this a concurrency bug in base?
Jean-Marie Gaillourdet
jmg at gaillourdet.net
Sun Oct 9 17:37:14 CEST 2011
Hi,
On 09.10.2011, at 17:27, Daniel Fischer wrote:
> Jean-Marie Gaillourdet:
>> the Eq instance of TypeRep shows the same non-deterministic behavior:
>
> Of course, equality on TypeReps is implemented by comparison of the Keys.
>
> On Sunday 09 October 2011, 16:40:13, Jean-Marie Gaillourdet wrote:
>> Hi Daniel,
>
>> I've been chasing the source of the non-deterministic of my library for
>> quite some time now. And at several points in time I had the impression
>> that modifyMVar would not always be atomic.
>
> It isn't:
>
> "MVars offer more flexibility than IORefs, but less flexibility than STM.
> They are appropriate for building synchronization primitives and performing
> simple interthread communication; however they are very simple and
> susceptible to race conditions, deadlocks or uncaught exceptions. Do not
> use them if you need perform larger atomic operations such as reading from
> multiple variables: use STM instead.
>
> In particular, the bigger functions in this module (readMVar, swapMVar,
> withMVar, modifyMVar_ and modifyMVar) are simply the composition of a
> takeMVar followed by a putMVar with exception safety. These only have
> atomicity guarantees if all other threads perform a takeMVar before a
> putMVar as well; otherwise, they may block."
>
> But I don't think that's the problem here.
>
>> (Of course under the
>> assumption that no other code touches the MVar).
This sentence referred to what you explained above. Although, my reference was quite cryptic.
>> But in that case as
>> well as in the case here it is only reproducible by looping the
>> execution of the binary. Moving the loop into the Haskell program will
>> show the bug in the first iteration or never.
>
> That's what I expect.
> I think what happens is:
>
> -- from Data.Typeable
>
> cache = unsafePerformIO $ ...
>
>
> mkTyConKey :: String -> Key
> mkTyConKey str
> = unsafePerformIO $ do
> let Cache {next_key = kloc, tc_tbl = tbl} = cache
> mb_k <- HT.lookup tbl str
> case mb_k of
> Just k -> return k
> Nothing -> do { k <- newKey kloc ;
> HT.insert tbl str k ;
> return k }
>
> occasionally, the second thread gets to perform the lookup before the first
> has updated the cache, so both threads create a new entry and update the
> cache.
>
> If you loop in the Haskell programme, after the first round each thread
> definitely finds an entry for "()", so the cache isn't updated anymore.
That sounds plausible. Do you see any workaround? Perhaps repeatedly evaluating typeOf?
Jean
More information about the Glasgow-haskell-users
mailing list