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