Boost logo

Boost :

From: Wil Evers (Wil_Evers_at_[hidden])
Date: 2002-01-15 08:06:55


Hi,

A few weeks ago, a posting by Beman Dawes to comp.lang.c++.moderated
caught my attention. The posting said that Boost.threads had been
submitted to the Committee for their upcoming C++ Standard Library
Technical Report, and that the initial response from the Committee had
been favorable.

Since I'm always looking for ways to do portable multi-threading in C++,
I checked the library's documentation to see how some of the more
difficult C++/MT issues had been addressed, only to find out that
Boost.threads doesn't address them at all. I expressed my
disappointment in a few postings to comp.std.c++ and
comp.lang.c++.moderated, which triggered an email by David Abrahams,
who wrote:

> Have you told us (boost) what you need that threads doesn't provide?
> A post to the boost list would be appreciated.

Before I go on, please keep in mind that I haven't been following the
discussions on the Boost mailing list; what I'm about to say must have
been discussed before here. On other hand, I guess my reaction is
fairly typical for a programmer who's simply looking for a way to write
reasonably substantial, portable multi-threaded C++ programs. In my
opinion, a standard C++ multi-threading library should allow me to do
that without having to deal too much with platform-specific APIs and
other implementation details.

So here are the main points on my wish list, along with some
suggestions for a possible approach:

(*) Cancellation support

This must be the most frequently requested feature. Without support for
cancellation, it is very awkward to implement a proper program
shutdown. In the past, I've had to implement an entire layer on top of
POSIX's condition variables (and their home-made Win32 substitute) just
to get to the point where a cancellation request is translated into a
C++ exception, and believe me, this is something you only want to do
once.

There are some tough design decisions to take: should cancellation be
asynchronous (no, the exception should be thrown from a set of known
cancellation points), should an attempt to lock a mutex be a
cancellation point (no, but waiting on a condition variable, or for I/O,
should), what happens if a thread completely handles a cancellation
request without dying (unappreciated behavior, i.e. another thread
attempting to join it will remain blocked).

Come to think of it: a cancellation request is just one of the things
that can interrupt an unsuspecting thread. Certain types of signals,
such as SIGINT, SIGTERM and SIGHUP, could also be mapped to a C++
exception thrown from a cancellation point.

(*) Inter-thread problem notification

What should happen when one of two threads with a producer/consumer
relation dies unexpectedly because of an exception (say, a bad_alloc)?
The peer must be notified, or it will wait forever.
There are, of course, more complex scenarios where certain threads
dynamically enter and leave a state that only makes sense when other
threads are still alive and behaving as expected.

So far, the approach I'm using is to totally hide the use of condition
variables and replace them with reference-counted I/O channels that
behave much like UNIX pipes: a thread attempting to read from a channel
without any living writers will get some end-of-file notification, and a
thread attempting write to a channel without any living readers gets the
C++-exception equivalent of a SIGPIPE.

(*) Event multiplexing

This issue relates both to inter-thread communication (semaphores,
condition variables, etc,.) as well as inter-process communication
(shared memory, local pipes and networking).

Some programs spawn a separate thread (or even a process) for each peer
entity they communicate with, while other programs use only a single
thread that iteratively handles all peer communication. The optimal
distribution of a number of peer conversations (which could be other
threads in the same process, other processes on the same host, or other
hosts on the network) over a certain number of threads, processes and
hosts is highly application-, hardware- and OS-dependent,
hard to design up front, and can often only be determined through
careful experimentation. In addition, it is sometimes appropriate to
'move' a certain service from some distinct host or process into a
thread which is part of the same process as its peers, or vice versa.

For maximum flexibility, we need (1) a portable library facility that
allows us to wait for the first of a number of events to happen and (2)
an interface that unifies the exchange of messages between peers,
wherever they live. This requires at least some support from a standard
multi-threading library, because it must be able to represent a blocking
point (a condition variable or semaphore) as something that can be
waited on while also monitoring network events and timer alarms.

As I said in comp.std.c++, Hoare's Communicating Sequential Processes
(CSP) contains a good example of a high-level syntax to express such
selection constructs. So far, my low-level approach is to use event
handles on Win32; for Unix, I use a pipe (which has a file descriptor,
and can thus be selected on) to express the state of something that
resembles a condition variable.

- Wil

Wil Evers, DOOSYS R&D, Utrecht, Holland
[Wil underscore Evers at doosys dot com]


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk