On Dec 7, 2012, at 3:08 PM, Greg Parker wrote:

> On Dec 7, 2012, at 7:03 AM, Jeremy Pereira <jere...@jeremyp.net> wrote:
>> On 12 Nov 2012, at 20:45, Greg Parker <gpar...@apple.com> wrote:
>>> There is something special about statically-allocated memory. 
>>> Statically-allocated memory has always been zero for the life of the 
>>> process. Dynamically-allocated memory may have been non-zero at some point 
>>> in the past (i.e. if it was previously part of a now-freed allocation).
>>> 
>>> The problem is your condition #2. If the memory was previously non-zero and 
>>> you set it to zero, you need appropriate memory barriers on some 
>>> architectures to prevent a race where the caller of dispatch_once() sees 
>>> the old non-zero value. Neither dispatch_once() nor the malloc system nor 
>>> the Objective-C runtime promise to provide the correct barriers.
>>> 
>>> In some cases you might be able to add an appropriate memory barrier to 
>>> your -init... method, assuming that no calls to dispatch_once() occur 
>>> before then. 
>>> 
>>> In practice this is a difficult race to hit, but it's not impossible.
>> 
>> Sorry, I'm a bit late to the party here but I've just read this and I don't 
>> understand it.  
>> 
>> If this race condition really exists, you couldn't assume that *any* 
>> instance variables of a newly initialised object have been zeroed out.
>> 
>> What am I missing?
> 
> Your statement is missing the additional requirements to hit the race:
> 
> You can't assume that any instance variables of a newly initialized object 
> have been zeroed out when
> * you are reading them from threads other than the one that allocated the 
> object and 
> * there is no synchronization between the allocating thread and the reading 
> thread.
> 
> In ordinary code there is only one thread involved, or there is already some 
> thread synchronization somewhere. 
> 
> For example, the allocating thread acquires a lock before writing the new 
> object's pointer somewhere that the reading thread can see it, and the 
> reading thread takes the same lock before reading the object pointer. That 
> lock is sufficient synchronization to make it work.
> 
> You'll only run into trouble if you are trying to use lock-free 
> multiprocessing techniques and you don't use enough memory barriers.

But this means that dispatch_once() is not uniquely unsafe.  It can fail only 
in the exact same scenarios where a thread other than the one which allocated 
an object might see uninitialized values for generic instance variables.  Such 
scenarios are, of course, unacceptable and so proper synchronization is 
necessary when passing object pointers between threads, full stop.  There's no 
special care necessary to make dispatch_once() safe, there's just the ordinary 
care necessary to make passing of object pointers between threads "sane".

You said that dispatch_once() doesn't promise to provide memory barriers, but, 
since the use of GCD hasn't brought catastrophe down on all of our heads, I 
assume that dispatch_async() and the like do provide such memory barriers.  
Likewise, NSOperationQueue, -performSelector:onThread:..., and the like must 
also.  Is that right?

If you wanted to be paranoid about using an instance variable as a once 
predicate, is it sufficient to use OSMemoryBarrier() in the -init method of the 
object?  Or perhaps there needs to be another call to OSMemoryBarrier() 
immediate before the dispatch_once() call, too.

Regards,
Ken


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to