« Big screens | Main | Lissajous sculpture »

Efficient thread safe static initialization

Dave MacLachlan, a Mac programmer at Google, discusses thread safe initialization of statics here and here on the Official Google Mac blog offering an interesting peak under the hood of Objective-C and avoiding the pitfalls of the dreaded DCLP.

Bill Bumgarner offered a clever approach: the Objective-C method which initialzes the static uses an expensive lock to ensure that only one thread performs the initialization, but then calls ReplaceMethodImplementationWithSelector so that later calls to that method call an alternate version of the function without the lock. This provides a general solution to the complications of the DCLP, by using a function pointer which initially calls a function with a lock to guard initialization, then replaces the function pointer. This doesn't actually require a function pointer. One just needs a separate global saying, in essence, don't bother with the expensive lock any more. Modifying the objective C method pointer is quite elegant.

I wonder what subtleties I'm missing behind all this.

In the presence of CPU write-reordering, might another threads see the new method or function pointer but the old uninitialized static variable? A lock acts as a write barrier, but both are set between acquiring and releasing the lock, and the faster version of the function has no lock, so there needs to be an additional memory barrier between the initialization and ReplaceMethodImplementationWithSelector, unless one is implicit in the call to ReplaceMethodImplementationWithSelector.

On reflection, this is just the DCLP in disguise. In essence, DCLP is an attempt to avoid an expensive lock by doing a check first to see if its needed. Using a method or function-pointer for dispatch implements that check without an "if" statement, but doesn't eliminate the problems of DCLP in the presence of relaxed consistency when CPUs can reorder memory reads and writes.

Comments

What about modifying the lock so it becomes a nop after the successful initialization?

How to modify the lock? If the lock contains a flag which disables it, that would be equivalent to DCLP.

One way to modify the lock is to use an Obj-C NSLock object. Then dealloc and set it to nil when done. objc_msgSend() is quite fast at short circuiting calls to nil (and ==nil is even faster)....

I'm not saying it is the fastest or best way to do this, only that it is a way of modifying the lock.

This blog entry has spawned a thread on the objective-c list at lists.apple.com. Everyone interested should check it out.

http://lists.apple.com/archives/Objc-language/2006/Nov/msg00124.html