Re: Thread safety question
Re: Thread safety question
- Subject: Re: Thread safety question
- From: Rich Seibel <email@hidden>
- Date: Fri, 18 Feb 2005 15:03:34 -0600
On Feb 18, 2005, at 11:27 AM, Shawn Erickson wrote:
On Feb 18, 2005, at 8:33 AM, Ernesto Corvi wrote:
Hi Steve.
The problem you might encounter here, if you have global register
allocation turned on, is that the compiler might cache
'variable_initialized' in a register.
So let's contemplate this scenario:
Thread1 -> Caches variable_initialized into R2, gets swapped out
between the 'if ( !variable_initialized )' and the '[myLock lock]'.
Thread2 -> Executes the entire function and initializes the variable.
Thread1 -> Resumes execution, but since it cached
'variable_initialized' into R2, it still thinks its unitialized, and
initializes again.
A portable solution to this problem is to declare
'variable_initialized' as:
volatile BOOL variable_initialized=NO;
The 'volatile' keyword hints the compiler not to cache the variable
and force direct access to the variable every time it's accessed.
Actually this isn't correct.
Calling lock (in fact any function) will force the compiler to reload
the external variable for all following code since the function call
could have changed it (threads doesn't affect this requirement).
So the use of volatile shouldn't be needed nor is it really that
portable because of the freedoms of implementation that exist for the
keyword volatile (however it is likely going to do exactly what you
are expecting). It can also come with overhead depending on how your
volatile var is used.
See...
<http://groups.google.com/groups?selm=email@hidden>
...and...
<http://lists.apple.com/archives/darwin-development/2004/Mar/
msg00154.html>
Calling lock will put out a memory barrier so it will insure that data
is written/read in order such that "myVariable" will be correct to the
current state while inside of lock bracketing (assuming all other
writes to that variable are also protected by the same lock, other
reads should be wrapped as well but that is not important here). In
other words it is best to use provided locking constructs when having
to do shared access like this from multiple threads (of course
sometimes the checking of a boolean, etc. for say an exit from a
working loop doesn't always require the use of any locking or even
volatile depending on what is needed and how things function).
This is exactly the kind of assurance I was looking for.
The fast check pathway however could result in a bogus pointer getting
returned if the write of a pointer to the variable isn't atomic but on
PPC for a 32b value I believe it will be (won't get a partial pointer
read out of it, all or nothing) but it is a fragile thing to be doing
without consideration for future/different system architectures.
True. This code has been in use for a long time and we haven't run
into that processor yet, but we will have to be watchful on every new
processor to come along.
I question if the use of this fast check is really needed...? Do you
know how often this function is called such that avoiding locking is
actually gaining you anything? Often best to go with simpler code when
possible. If it is a hot function why not move initialization to some
up front function call and then have all later use just assume the
variable has been initialized.
In the case of ACE, this code is used to instantiate singletons, so the
probability that the lock will be taken more than once per singleton is
essentially zero. Since, this is middleware used by thousands of
applications, I can't even guess the number of times it is called, the
range is a few to billions. The code has been around for over ten
years and used on dozens of different platforms, so any change would
certainly require a proven failure.
If on 10.3 or later consider...
static void* mySingletonObject = 0;
void* instance(void)
{
// possibly use self in the following if this is actually going to be
a class or instance method
@synchronized(someObjectOrUniqueValue) {
I am not familiar with this syntax or function. Can it provide
synchronization without a context switch?
if (mySingletonObject == nil) {
mySingletonObject = allocateObject();
}
}
return mySingletonObject;
}
This is good information to have going into the future.
Thank you very much,
Rich
-Shawn
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden