Double-checked locking: is this alternative OK?

J

JohnQ

SomeObject* instance() // flawed way
{
static SomeObject* instance = NULL;
LockThing(); // locking even when not required
if(instance == NULL) // instance may still be NULL when another thread
checks
instance = new SomeObject();
UnlockThing(); // oh dang, more unnecessary function calls after first
pass
return instance;
}

SomeObject* instance() // OK?
{
static bool been_there_done_that = LockThing();
static volatile instance = new SomeObject(); // no problemo (on X86
hardware)
static bool been_there_done_that_B = UnlockThing();
return instance; // fast is nice! No overhead after first pass through
function
}

John
 
V

Victor Bazarov

JohnQ said:
SomeObject* instance() // flawed way
{
static SomeObject* instance = NULL;
LockThing(); // locking even when not required
if(instance == NULL) // instance may still be NULL when another
thread checks
instance = new SomeObject();
UnlockThing(); // oh dang, more unnecessary function calls after
first pass
return instance;
}

Could you explain why you don't do it this way:

if (instance == NULL) {
LockThing();
instance = new SomeObject();
UnlockThing();
}
return instance;

? That should avoid the unnecessary locking you mention, no?
SomeObject* instance() // OK?
{
static bool been_there_done_that = LockThing();
static volatile instance = new SomeObject(); // no problemo (on X86
hardware)
static bool been_there_done_that_B = UnlockThing();
return instance; // fast is nice! No overhead after first pass
through function
}

John


The problem with having to double-check is not solved by your static
initialisation, however. The language at this point makes no attempt
to resolve the problem of calling the function "for the first time"
from two different threads. And AIUI the sequence of events in that
case can be

Thread 1 Thread 2
LockThing() - succeeds
LockThing() - fails, waits
new SomeObject() . // waiting
UnlockThing() . // waiting
return instance
new SomeObject() // overrides instance
UnlockThing()
return instance // different instance

The Committee is working on thread support, however. Static
initialisations would have to be resolved before the thread support
can be claimed, that's for sure.

V
 
D

david.baird

if (instance == NULL) {
LockThing();
instance = new SomeObject();
UnlockThing();
}
return instance;

I think this can be improved to:

if (instance == NULL) {
LockThing();
if (instance == NULL) {
instance = new SomeObject();
}
UnlockThing();
}
return instance;

This is mentioned in [1] as "The Double-Checked Locking Pattern."
Thread 1 Thread 2
LockThing() - succeeds
LockThing() - fails, waits
new SomeObject() . // waiting
UnlockThing() . // waiting
return instance
new SomeObject() // overrides instance

The above code fixes this, instead of calling "new SomeObject()":
Thread 1 Thread 2
Checks if instance is NULL
(Since instance is not NULL,
do *not* call "new SomeObject()")
UnlockThing()
return instance // different instance

[1] "Modern C++ Design" Andrei Alexandrescu
 
D

david.baird

I think this can be improved to:

if (instance == NULL) {
LockThing();
if (instance == NULL) {
instance = new SomeObject();
}
UnlockThing();
}
return instance;

Okay, I read my book a little bit more and found that actually even
this double-checked solution can have problems in SMP systems where
memory writes are committed in non-chronological bursts. Using the
"volatile" keyword might help and there might also be special
concurrency features offered by your platform.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top