[Mono-devel-list] Double-locking and thread safety
kornelpal at hotmail.com
Thu Jun 23 13:49:44 EDT 2005
> From: Ben Maurer
> On Thu, 2005-06-23 at 19:03 +0200, Kornél Pál wrote:
>> 1. Declaring "a" as volatile would solve the problem but it results in
>> performance loss because of memory barriers for each read.
> Well, the performance loss here isn't all that bad. On an x86 machine
> there is basically no loss.
You are right but although on x86 machines the original code without
volatile and MemoryBarrier works correctly we care about this problem. As
the problem may occur only on MP IA64 (according to referenced articles) the
solution itself has to be shaped for MP IA64 as well (as long as it does not
cause performance loss on other architectures). And I think using no lock
for read results in better performance than locking or doing memory barriers
>> 2. Removing the check outside the lock would solve the problem but it
>> results in performance loss because of memory barriers for each read.
> The real problem is that the lock can result in contention which is
> orders of magnitude worse than a memory barrier.
You are right, but memory barriers should be done by lock as well so it's
>> I think using a lock is better than constructing a new instance because
>> new instance would be dropped if it's already created as we want to use a
>> single instance so it's better to suspend the thread by doing a wait than
>> creating a new instance because the CPU time can be used for useful
>> operations instead of wating time.
> That issue only happens in the *VERY* rare case that two threads are
You are right but if we know a better solution we should use that. And of
course the problem because of using the original form of the code will occur
only in very rare cases as well and we don't say that it's unimportant.
These rare cases exist anyway and I think it's better to wait and let the
CPU do something useful than doing someting useless just not to lock. And
note that it even may need more time to create a new instance than waiting
for the other thread to create the instance so I don't see any advantages of
creating the multiple instances simultaneously.
>> "Realize that synchronization is expensive. The full fence implied by
>> Interlocked.Increment can be many 100’s of cycles on modern hardware.
>> penalty may continue to grow, in relative terms."
>> I think this applies to Interlocked.CompareExchange as well.
> But that only gets run once per program. Anyways the cost of reading a
> page in from the disk to do the Locale stuff is orders of magnitude more
You are right but if we care about performance we should care about this
rare situations as well. Doing expensive research for these situations is
not recommented but if we know that one solution is better than the other we
should use the better instead of saying it's not important.
>> So I suggest to use this model instead of Interlocked.CompareExchange.
>> Furthermore I think using two Thread.MemoryBarrier() is less effective
>> using lock and and a single Thread.MemoryBarrier() because there will be
>> useless object creation, Thread.MemoryBarrier() allways does a full
>> while lock does acrique at the begining and release at the end and
>> Interlocked.CompareExchange does barriers as well so there are more
>> and thus locks in your "lock-free" code than in the locked code.
> It depends on how commonly this code is used. The code is going to
> become horrible to maintain if we have MemoryBarriers scattered around.
> We should really stick to obvious locking patterns except where
> performance is an issue. Now, given that this code is only used in
> exceptional code paths (and in SWF -- but there, speed is measured in UI
> responsiveness, and none of the solutions are slow enough to make a
> difference there), the simple lock-then-check should suffice for now.
It's sufficient but I can't understand why not to use a more sufficient
solution if we already know that solution.
More information about the Mono-devel-list