volatile and barriers (especially on PPC)

Peter A. Buhr pabuhr at plg.uwaterloo.ca
Fri Mar 11 18:22:37 GMT 2005


Hi, I'm in catch-up mode because my address got dropped from the CCed list
after Feb 26. I've managed to obtain and read through the bulk of the messages
between then and now. Since some of the material has been well discussed, I'll
try to keep my comments at a high-level and then work my way back into the
detailed stuff in the new discussion.

(Personal comment) I've never liked "volatile" because it borders on
witch-craft. If I don't use it, my program might work. And if I have to use it,
the factors to make that decision are often so arcane that I'm never sure if
I've done it correctly. Try explaining to a class of 2nd or 3rd year students
the reasons for when it is necessary to qualify a variable with "volatile". You
are not going to be very successful.

I agree with Maged that "volatile" already has special meaning in C/C++. My
understanding of current practice is that volatile is used to make
setjmp/longjmp work and to handle special memory-mapping situations (like
memory-mapped I/O devices).  Notice, neither of these cases exist in Java;
hence, it is possible for Java to co-opt this qualifier for use with
concurrency because it had no prior meaning. To strength the semantics of
volatile in C++ without the ability for the compiler to implicitly distinguish
the two different usages is going to cause problems. We may be forced to
introduce a new qualifier, like "shared", resulting in 4 possible meanings for
all 4 combinations of volatile and shared. I'm unhappy with this suggestion
because it just increases the level of witch-craft, but it may be thrust upon
us.

With respect to high-level concurrency capabilities, I'll make the following
sweeping generalizations: Hans Boehm thinks it's hopeless, Doug Lea thinks it is
semi-hopeless so he is encouraging discussion at the middle-level, Jim Rogers
and myself think it is far from hopeless if we do it right. (I know I have
missed others, but I just wanted to get across the broad spectrum of opinion.)
The rest of my comments are on high-level concurrency capabilities, and yes
Doug Lea, I'm going to make concrete suggestions.

We have to think about the nature of the underlying language in the design
process so that new ideas meld with or are orthogonal to existing features in
the language; otherwise it will look like a poorly constructed add on.  I think
Hans pointed out, it's problematic to talk about how to start a thread without
simultaneously thinking about synchronization and mutual exclusion (SME) of
threads. So I see this as the starting point for discussion.

I want to base both thread creation and SME on the notion of a class because
the class is the central unit for program construction in C++ (and Java). For
example, in Java the monitor provides the majority of SME and is based on the
notion of a class. As Doug suggested, the popularity of concurrency in Java
results from the fact that programmers can easily transition from the notion of
class into a monitor, leveraging all the programming concepts they already know
about classes. I can't emphasis enough how important this leveraging is and how
the monitor continues to work and play with all the other features of the
language (modulo a very few exceptional situations). So I'm going to make the
concrete suggestion that the "high-level" SME in C++ should be provided via a
monitor that is based on the notion of a class. I think any discussion on the
exact semantics of such a monitor would be premature without first establishing
a general consensus that a monitor is a good approach for "high-level" SME.
Note, the monitor notion is well understood and appears in many current
languages, e.g., Java, C#, Ada (protected objects), so there is a reasonable
chance that this approach might be accepted by the C++ standards committee.

That brings me to the notion of thread creation. Like the monitor, I want to
leverage the class for thread creation, which is similar to the "task type" in
Ada, e.g.:

  task T {
      void main() {...} // thread starts here
    public:
      // constructors/destructor and other member routines
  }

A task has all the properties of a class, it has its own stack, a thread, and
its public members have mutual exclusion so they can be used to construct
SR/Ada style rendezvous allowing tasks to directly communicate with one another
rather than having to communicate through a monitor. Note, a task declaration
can appear in any declaration context where a class can be declared, which
means in the global (external) scope, local (automatic) scope, and heap. Other
features, such as inheritance, overloading and templates are applicable, with
possibly some minor restrictions. Because the restrictions on the task can be
very few, it is orthogonal with all the concepts in the language and leverages
all of these existing concepts.

The thread-creation approaches suggested so far all start a thread in a
function/functor, which looks, smells and tastes like pthreads to me and does
not attempt to integrate with the fundamental notion of class in C++. This
dichotomy can be seen a little in Java where the monitor arguably blends into
the language as a whole, while thread creation looks like an add-on from some
library. This library approach for thread creation works in Java because all
objects are declared on the heap. In C++, it should be possible to create
threads in all the declaration contexts using exactly the same syntax and
having virtually the same semantics as a normal class declaration.  As well, in
the thread approaches suggested so far and in Java, there is little or no
attempt to make use of the thread object after it is created, other than
join. However, if a thread object is based on the notion of class, then the
thread object can have public members that may be called by other threads,
which provides a powerful mechanism to implement inter-task communication and
active objects.  This capability can be made to work, and there is 20+ years of
experience for it in Ada.  So I'm going to make the concrete suggestion that
the "high-level" thread creation in C++ should be provided via a task type that
is based on the notion of a class.  Note, this can remove the need for explicit
"start" and "join" members.  I think any discussion on the exact semantics of
such a task type would be premature without first establishing a general
consensus that a task type is a good approach for thread creation.

uC++ takes the above approach, and has a coroutine type as well. If you want to
know about uC++ please see:

   ftp://plg.uwaterloo.ca/pub/uSystem/uC++.ps.gz

if you want to take it out for a test drive see:

   ftp://plg.uwaterloo.ca/pub/uSystem/Announcement

This is enough for now. I'll talk about exception handling and threads in
another message.






More information about the cpp-threads mailing list