Boost logo

Boost :

From: Mattias Flodin (flodin_at_[hidden])
Date: 2001-11-16 20:56:13


On Fri, 16 Nov 2001, Peter Dimov wrote:
> There is no interpretation that can make sense for more than one thread. The
> abstract machine as currently specified is inherently single-threaded AFAIK.

This discussion makes me wonder about the LIFO 'stack' necessary for
proper destruction order of function-statics. Up until now, the issue of
thread safety has mostly concerned the prevention of double initialization
of the same object, not the locking of that stack. Consider the following:

A& f()
{
    static A a;
    return a;
}

B& g()
{
    static B b;
    return b;
}

Assume f() and g() are called simultaneously, each on a separate CPU. If A
and B's ctors do not interfere with eachother, one might first be lead to
believe there are no race conditions here. However, since the standard
requires that a and b are destroyed in the reverse order of creation, both
would need to access the same resource for keeping track of construction
order. To be safe from undefined behavior here, one would need to create a
single global mutex for _all_ function statics.

To generalize this, it could be said that any attempt to (in a
multithreaded program) simultaneously call two different functions that
contain static objects, is undefined.

Another way to interpret what has been said is that, since each thread is
valid 'within itself', destruction order needs only to be guaranteed for
objects created within the same thread. In other words, each thread could
be assigned its own destruction stack. This raises the question whether
destruction of function statics should occur at thread termination or
program termination. The only sensible point would be at program
termination - otherwise additional calls to f() after termination of the
thread that first called it, would need to call the constructor a second
time. Thus we arrive once more at the need for a synchronized global list,
that the last exiting thread can access to call destructors.

Taking a specific implementation, MSVC seems content with just calling
atexit() to register the function for destroying an object, once it is
created. I would be surprised if MSVC's implementation of atexit() isn't
designed to be thread-safe.

The question is whether such thread-safe behavior can be assumed in the
general case. Since we are operating outside the scope of the C++
standard, what more choice is there than to look at what today's
multithread-enabled compilers do? Are there any such compilers out there,
that do not ensure thread-safe registration of destruction order? And if
there are, should we or can we do anything about it?

The argument 'we can't do this because it is undefined by the standard'
appears to have been somewhat weakened the day Boost.Threads was
introduced. And if the LWG is ever to accept inclusion of Boost.Threads,
there will probably need to be support from the language in the form of
defined semantics of multithreaded execution. It becomes not just a
library issue, but a language issue.

Regards,
  Mattias

--
Mattias Flodin <flodin_at_[hidden]>   "A good thing about C++ is that only
Room D418                           friends can access your private parts"
Department of Computing Science
Umeå University
S-901 87 Umeå, Sweden
Note: Any opinions expressed in this mail are personal, and do not
necessarily reflect an official standpoint of Umeå University.

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