Re: Threads - When?
Le Chaud Lapin wrote:
James Kanze wrote:
Le Chaud Lapin wrote:
I think that it is possible to use C++ as it is effectively in a
multi-threaded environment, provided that good primitives to support
such an environment have been created. In particular, the
synchronization primitives are critical (no pun intended).
You need more than primitives. As long as accessing memory from
two different threads is undefined behavior, you cannot write
multithreaded programs. And the current sequence point model
simply doesn't work in a multithreaded environment.
It's not undefined in my framework. I use old-fashioned
locking/unlocking.
So you don't work under Windows, Linux (at least on a PC) or
Solaris. Or more likely, you're counting on some additional
guarantees from the implementation. Some of those guarantees
are pretty wide spread. Others not. The problem is that even
the widespread ones aren't part of the C++ standard (which
doesn't recognize threads) nor Posix (which doesn't recognize
C++). And not all are well documented, or even really
guaranteed from one release of the compiler to the next.
C++ has a requirement that it be implementable under current
operating systems. Under current operating systems---at least
under Solaris and Linux, and I think under Windows---there is no
way to implement a general solution to these problems; both
require significant collaboration in the manipulated thread. So
it would be extremely surprising if the C++ standard required a
solution to them. And useless---a standard that cannot be
implemented under Windows or Unix isn't of any interest to
anyone.
I have Windows and Linux covered meaning that, if you use only the
interfaces of my synchronization primitives (Mutex, Event, etc.) it
will cross compile, but the implementation is not portable of course.
I'm less concerned about the interface -- I can always wrap
that. I'm more concerned about the actual synchronization
issues. My own experience is that code written for g++ doesn't
work correctly with Sun CC, and vice versa, because the actual
guarantees given by each compiler are different.
But if you compile the _code_ that uses these primitives, that code is
portable in the strictest sense, meaning, it is not possible to look at
the code and tell which OS is for. This hints that there are actually
two problems:
1. Having available the basic primitives of synchronization
2. Constructing a framework in such way that the use of that framework
does not drive the programmer insane.
These are two separate issues.
There is a fundamental problem with 1: knowing what the basic
primitives actually guarantee.
The first problem is the domain of the OS people.
Partially, although not completely. Typically, the OS people
define a C interface, and you have to depend on an
implementation specific mapping to C++. Thus:
std::string s ; // Initialized from argv...
void f()
{
static std::string t( "xxx" ) ; // OK with g++, not with Sun
CC
if ( s[ 0 ] == t[ 0 ] ) { // OK with Sun CC, not with
g++
}
}
(I think that the g++ developpers consider this latter case an
error, and plan to fix it. There are other issues, like the
handling of pthread_cancel, however, where it is less than
obvious.)
For example, if
there were no atomic operations like test-and-set, or even an
ALU-managed XOR to external store, there would be nothing the C++
library writer could do to guarantee safeness.
Actually, test and set or an atomic xor are far from sufficient.
(They're also not typically available without actually writing
inline assembler.)
Luckily, that's not the
case. We not only have that, we have a lot more. It's just that some
OS vendors have not paid attention to those who have already crossed
the finish line.
Once the first part is done, then the second part can be finished, but
not before.
You can't do the first part until you define exactly what you
mean by synchronization. That's about the point where C++ is
now. And it's difficult, because you do want something that is
implementable (with reasonable overhead) on mainline systems.
[...]
Also, to remain honest, I did not do critical sections, which means I
use Mutex's exclusively for locking, with performance hit, but that
will fixed.
I presume here that you are confusing the name of the system
request with the actual functionality it provides. I know of no
system today which provides a pure critical section; what
Windows calls a critical section is in fact a mutex.
My gut feeling is that Microsoft implemented them using
nothing more than test-and-set with failover to a real mutex on
Windows.
My timing measurements on Solaris suggest that
pthread_mutex_wait only calls the system when it has to,
regardless of the threading model being used. Since I don't
think Sun have some miracle technology unavailable to others, I
rather suspect that most systems do this.
I neglected them because it was hard to make something
portable if your C++ class must include in its declaration something
that #include's from Wnidows.h].
At some point, you have to. There are no synchronization
primitives in any of the standard C++ headers. The usual
solution is to create a wrapper class, with system dependant
implementations. (If the wrapper classes use the pimpl idiom,
there is no need for user code to depend on system specific
headers.)
So to summarize: The OS people can make the primitives as raw and
unsightly as they want as long as the primitives are present and
complete. From that, it becomes easy to write a clean, C++ wrapper
framework to support robust multi-threading.
The problem is that there is no portable set of guarantees you
can count on.
--
James Kanze (Gabi Software) email: james.kanze@gmail.com
Conseils en informatique orientie objet/
Beratung in objektorientierter Datenverarbeitung
9 place Simard, 78210 St.-Cyr-l'Icole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]