5 ms·
Or you could use atomic swaps on the initialized bit. That's both cheaper and simpler. psuedocode: member atomic32 init member mutex lock member o
by jamesaguilar 14y ago
Or you could use atomic swaps on the initialized bit. That's both cheaper and simpler.
psuedocode:
member atomic32 init
member mutex lock
member obj ptr
membarrier()
if (atomic_load(&init)) {
return ptr
} else {
locked_init()
atomic_save(&init, 1)
membarrier()
}
Not an expert, use this at your own risk.
- vy8vWJlco 14y agoIf you have atomic swaps (hardware "lock-free"), the global shared instance pointer should be enough - since it either holds a valid pointer or it doesn't.
- limmeau 14y agoWhat do you swap into the pointer? A new instance?
- vy8vWJlco 14y agoIf the pointer is 0 through a normal test, you would allocate the resource and do a compare-and-swap to record the allocation if the pointer is still 0. As noted beside this comment, you might wind up allocating several times in parallel (which may be undesirable too), but you would never clobber an existing allocation or change the instance that is dispensed.
- jamesaguilar 14y agoNo, you don't want two threads trying to allocate the object at once. You still need a locked init.
- vy8vWJlco 14y agoYes, there would be an allocation race, so if you cannot accept allocating unnecessarily in a discardable/recoverable way (but also one that preserves the semantics of the singleton: one and only one instance returned over its life), you would still need a flag. Though you might instead use an "incomplete"/place-holder reference value to simulate the flag and prevent duplication of effort/wasting resources while the allocation happens - all in the space of the pointer.
- rbehrends 14y agoThat's not sufficient on its own (leaving aside the problem of multiple initialization). While compare and swap will likely have the necessary write barrier built in, you need a matching read barrier when accessing the object. I.e. reads could otherwise get re-ordered so that you see the pointer and the not-yet-initialized object at the same time.
- vy8vWJlco 14y agoRight. I was still thinking in terms of my thread-local-less model posted before when I wrote the above, which took for granted those barriers once past init.
- rbehrends 14y agoIt's well-known that you can do double-checked locking right with memory barriers, but memory barriers aren't always cheaper. On TSO architectures (x86, most SPARCs), read barriers are no-ops, but with weaker memory models (e.g., ARM), they aren't. Edit: Also, the memory barriers are in the wrong place in the code. You need a read barrier between the init check and returning ptr, and a write barrier between the actual initialization of the object and writing the init variable.
- jamesaguilar 14y agoInteresting. I didn't know that. Sorry for the error, I'm not an expert in this subtle stuff. Barely even conversant to be honest. On those architectures with weaker models the real simpler thing to do is to make the singleton thread-unsafe for initialization.
- anonymous 14y agoYou force a memory barrier in all cases, while the original code only passes through a barrier once - if the instantiated_ flag is false.