Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

why are mutexes not declared volatile?

5 views
Skip to first unread message

jshe...@gmail.com

unread,
Oct 8, 2007, 3:17:17 AM10/8/07
to
Hi,

perhaps this is a silly question, but in all the examples I have seen
about multi-threading and the volatile keyword, the mutex itself is
not declared volatile. e.g.

volatile int counter;
Mutex mutex;

Why is that? Is not the mutex itself also an object that is accessed
from multiple threads?

thanks
Jamie


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Thomas Richter

unread,
Oct 8, 2007, 9:34:19 AM10/8/07
to
jshe...@gmail.com schrieb:

> Hi,
>
> perhaps this is a silly question, but in all the examples I have seen
> about multi-threading and the volatile keyword, the mutex itself is
> not declared volatile. e.g.
>
> volatile int counter;
> Mutex mutex;
>
> Why is that? Is not the mutex itself also an object that is accessed
> from multiple threads?

volatile is neither sufficient nor necessary to declare an object
thread-safe. That is, C++ itself (the language standard) says nothing
about threads, and leaves it to the implementation to define the
details. Multithreading in C++ hence might or might not work, depending
on your compiler vendor. If the vendor declares it as o.k. that the
mutex they provide does not require "volatile", then so might it be.

That said, the internals are often that the mutex is just a handle to an
object kept care of by the operating system, and the operating system
then takes all action to ensure that the mutex works as intended. But
again, this is not a question that can be answered here since it doesn't
concern C++. There are no mutex objects in C++ either.

So long,
Thomas

svob...@gmail.com

unread,
Oct 8, 2007, 9:34:47 AM10/8/07
to
> Why is that? Is not the mutex itself also an object that is accessed
> from multiple threads?

i recently found a note on this in the following paper:
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

<quote>
Well, Lock comes from a threading library, so
we can assume it either dictates enough restrictions in its
specification or embeds
enough magic in its implementation to work without needing volatile.
This is the case with all threading libraries that we know of. In
essence, use
of entities (e.g., objects, functions, etc.) from threading libraries
leads to the
imposition of "hard sequence points" in a program-sequence points that
apply
to all threads.
</quote>

mojmir

Gianni Mariani

unread,
Oct 8, 2007, 1:11:45 PM10/8/07
to
jshe...@gmail.com wrote:
> Hi,
>
> perhaps this is a silly question, but in all the examples I have seen
> about multi-threading and the volatile keyword, the mutex itself is
> not declared volatile. e.g.
>
> volatile int counter;
> Mutex mutex;
>
> Why is that? Is not the mutex itself also an object that is accessed
> from multiple threads?

This would be because the elements in a mutex are probably volatile.

Maciej Sobczak

unread,
Oct 8, 2007, 1:12:57 PM10/8/07
to
On 8 Pa , 09:17, jsher...@gmail.com wrote:

> perhaps this is a silly question, but in all the examples I have seen
> about multi-threading and the volatile keyword, the mutex itself is
> not declared volatile. e.g.
>
> volatile int counter;
> Mutex mutex;
>
> Why is that?

Because these examples are broken anyway and this "inconsistency" is
just a proof that this is actually the case.

volatile is not enough to make counter thread-safe (whatever that
means), and one you make it safe by other means (like by using the
mutex), it does not have to be volatile at all.

Please consider reading "Programming with POSIX Threads" by David R.
Butenhof. It is Unix-centric, but at the same time full of general
insight that can be reused elsewhere.

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Brian Palmer

unread,
Oct 12, 2007, 5:27:08 AM10/12/07
to
Maciej Sobczak <see.my....@gmail.com> writes:

> volatile is not enough to make counter thread-safe (whatever that
> means), and one you make it safe by other means (like by using the
> mutex), it does not have to be volatile at all.

Hmm? Consider (for some imaginary mutex implementation)

Mutex mutex;
int counter;

mutex.lock(); // Initialization
counter = 0;
mutex.unlock();

sleep(1000); // Let somebody else do something

mutex.lock(); // Display stats
cout << counter << endl;
mutex.unlock();

A C++ compiler without the volatility constraint is free to optimize
away reading counter in the display_stats block; it knows that counter
is 0, so it can simply hard code a 0 into the function call, without
ever examining memory to see if counter has changed.

Perhaps I'm mistaken, but I would think that volatility of the
resources protected under the mutex is necessary.

--
I'm awfully glad I'm a Beta, because I don't work so hard.

Maciej Sobczak

unread,
Oct 12, 2007, 1:49:04 PM10/12/07
to
On 12 Pa , 11:27, Brian Palmer <bpal...@rescomp.stanford.edu> wrote:

> > volatile is not enough to make counter thread-safe (whatever that
> > means), and one you make it safe by other means (like by using the
> > mutex), it does not have to be volatile at all.
>
> Hmm? Consider (for some imaginary mutex implementation)
>
> Mutex mutex;
> int counter;
>
> mutex.lock(); // Initialization
> counter = 0;
> mutex.unlock();
>
> sleep(1000); // Let somebody else do something
>
> mutex.lock(); // Display stats
> cout << counter << endl;
> mutex.unlock();
>
> A C++ compiler without the volatility constraint is free to optimize
> away reading counter in the display_stats block

Short version: No.

Longer version:
A C++ compiler is free to do whatever it wants with this code, because
C++ does not define what threads are and even less how they behave. In
particular, there is no concept of "Let somebody else do something",
because as far as C++ is concerned, there is nobody else.
C++ alone is not enough to explain the above example and both
programmers and language implementers have to reach outside of the
language to cover this situation. We can imagine anything we want
outside of the language, but if we constrain ourselves to some
established standards like POSIX (forget for the moment that POSIX in
turn does not recognize C++), then having the combined C++&POSIX
environment it is possible to reason about what the code above is
doing - assuming, of course, that the imaginary mutex above is
implemented in terms of relevant POSIX primitives. In this case, as
far as POSIX is concerned, you do *not* have to make counter volatile.
The important thing is that not only we, programmers, have to know it
- also the language (compiler) implementers have to know it. And they
do.

There are two ways for the language implementer to ensure that the
above code works fine: lazy and smart. The lazy way is to not do any
optimizations across function calls. Mutex::lock/unlock is a function
call and the compiler will not optimize accesses to local variable
counter across these calls. The smart way is to analyze what
Mutex::lock/unlock actually do and if they call relevant POSIX
interfaces, the compiler knows that there is potential modification
from other threads and then will not optimize accesses to counter
across these calls.

The lazy way is more common.

> Perhaps I'm mistaken, but I would think that volatility of the
> resources protected under the mutex is necessary.

Not only it is *not* necessary[*], it can also bite you in terms of
performance. Consider the series of accesses within a single critical
section - why losing the opportunity for optimization in this case?

[*] All these concern the example POSIX standard. There might be other
multithreading standards and implementations that have different
guarantees and constraints. Check your vendor's docs.

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com


--

Chris Thomasson

unread,
Oct 12, 2007, 4:18:29 PM10/12/07
to
"Brian Palmer" <bpa...@rescomp.stanford.edu> wrote in message
news:0whlka9...@rescomp.stanford.edu...

> Maciej Sobczak <see.my....@gmail.com> writes:
>
>> volatile is not enough to make counter thread-safe (whatever that
>> means), and one you make it safe by other means (like by using the
>> mutex), it does not have to be volatile at all.
>
> Hmm? Consider (for some imaginary mutex implementation)
>
[...]

> mutex.lock(); // Display stats
> cout << counter << endl;
> mutex.unlock();
>
> A C++ compiler without the volatility constraint is free to optimize
> away reading counter in the display_stats block; it knows that counter
> is 0, so it can simply hard code a 0 into the function call, without
> ever examining memory to see if counter has changed.
[...]

Link-time optimizations aside... If mutex.lock() is located in a foreign
library that the compiler knows nothing about, then it has to be somewhat
pessimistic about what optimizations it can apply. This is why some
threading abstractions are assembled into externally libraries:

http://groups.google.com/group/comp.programming.threads/msg/423df394a0370fa6

Although, there are no real concrete guarantees here unless you are using
PThreads with a POSIX compliant compiler.


--

Kurt Stege

unread,
Oct 12, 2007, 4:18:43 PM10/12/07
to
Brian Palmer wrote:

> Consider (for some imaginary mutex implementation)
>
> Mutex mutex;
> int counter;
>
> mutex.lock(); // Initialization
> counter = 0;
> mutex.unlock();
>
> sleep(1000); // Let somebody else do something
>
> mutex.lock(); // Display stats
> cout << counter << endl;
> mutex.unlock();
>
> A C++ compiler without the volatility constraint is free to optimize
> away reading counter in the display_stats block; it knows that counter
> is 0, so it can simply hard code a 0 into the function call, without
> ever examining memory to see if counter has changed.

No, it isn't. The compiler may do this optimization only,
when the compiler can prove that the function call mutex.unlock()
does not modify the variable counter. And the implementation
of the magically mutex will be done in a way that the compiler
cannot prove this. (Except, when counter is a local variable,
and the address of counter is not published in any way, the compiler
does not know that mutex.unlock() (or mutex.lock()) do not modify
that variable. And in the case that counter is local, and its
address is not published, no other thread will be able to modify
that value.)


> Perhaps I'm mistaken, but I would think that volatility of the
> resources protected under the mutex is necessary.

No, for a usual implementation of the mutex, volatile is
not necessary. For usual compilers, it is enough when the compiler
does not see the implementation of mutex.unlock(). And compilers
that support optimizations about the complete program will
(hopefully) support some magic that allows the mutex implementation
to disable that optimization in this context.

Best regards,
Kurt.

--

Chris Thomasson

unread,
Oct 12, 2007, 4:24:12 PM10/12/07
to
"Maciej Sobczak" <see.my....@gmail.com> wrote in message
news:1192193144.5...@e9g2000prf.googlegroups.com...

> On 12 Pa , 11:27, Brian Palmer <bpal...@rescomp.stanford.edu> wrote:
[...]

> There are two ways for the language implementer to ensure that the
> above code works fine: lazy and smart. The lazy way is to not do any
> optimizations across function calls. Mutex::lock/unlock is a function
> call and the compiler will not optimize accesses to local variable
> counter across these calls. The smart way is to analyze what
> Mutex::lock/unlock actually do and if they call relevant POSIX
> interfaces, the compiler knows that there is potential modification
> from other threads and then will not optimize accesses to counter
> across these calls.

[...]

POSIX aside for a moment... IMHO, link-time optimizations can be potentially
dangerous wrt multi-threading. The compiler could think its doing the right
thing by messing around and/or making assumptions about a sensitive
synchronization algorithm which was assembled externally into an object
file.

Something like this is what I am afraid of:

http://groups.google.com/group/comp.programming.threads/msg/fd3b4b5a5cd7841e

http://groups.google.com/group/comp.programming.threads/msg/0afc1109d18c2991

Ouch!

[...]

Mathias Gaunard

unread,
Oct 14, 2007, 7:42:32 PM10/14/07
to
On Oct 12, 10:24 pm, "Chris Thomasson" <cris...@comcast.net> wrote:

> POSIX aside for a moment... IMHO, link-time optimizations can be potentially
> dangerous wrt multi-threading.

Not if the optimizer recognizes memory barriers and thus doesn't do
stuff it's not allowed to.

0 new messages