I have misunderstandingn on sec. 3.6.3/1 N3797. When I considered initialization rule I come across with copy-initialization concept.
If the completion of the constructor
or dynamic initialization of an object with static storage duration is
sequenced before that of another, the completion of the destructor of
the second is sequenced before the initiation of the destructor of the
first. [ Note: This definition permits concurrent destruction.— end
note ]
Is it possible that completion destructors of the second and of the first are in different threads?
Note: I edited and corrected the statement about unsequenced initialization (thanks to #dyp). If your program doesn't start any threads, then all initializations happen in some (possibly indeterminate) sequence.
There are no constraints beyond the constraints described in the Standard1 on which threads perform initialization and destruction of objects with static storage duration. In particular, the initialization of objects in different translation units are indeterminately sequenced with respect to one another (cf. [basic.start.init]/2), and unsequenced if the program starts threads.
It is therefore possible for initializations to occur concurrently in multiple threads if the first function call within a TU occurs within another thread other than the main thread. What the standard says is that those objects whose initialization is sequenced (e.g. those in the same TU) are also destroyed in a deterministic, compatible order.
______________
1) "beyond what is said, nothing is said" — a bit it of a tautology, but worth calling out
Related
In cppref, the following holds until C++17:
code such as f(std::shared_ptr<int>(new int(42)), g()) can cause a
memory leak if g gets called after new int(42) and throws an
exception, while f(std::make_shared<int>(42), g()) is safe, since
two function calls are never interleaved.
I'm wondering which change introduced in C++17 renders this no longer applicable.
The evaluation order of function arguments are changed by P0400R0.
Before the change, evaluation of function arguments are unsequenced relative to one another. This means evaluation of g() may be inserted into the evaluation of std::shared_ptr<int>(new int(42)), which causes the situation described in your quoted context.
After the change, evaluation of function arguments are indeterminately sequenced with no interleaving, which means all side effects of std::shared_ptr<int>(new int(42)) take place either before or after those of g(). Now consider the case where g() may throw.
If all side effects of std::shared_ptr<int>(new int(42)) take place before those of g(), the memory allocated will be deallocated by the destructor of std::shared_ptr<int>.
If all side effects of std::shared_ptr<int>(new int(42)) take place after those of g(), there is even no memory allocation.
In either case, there is no memory leak again anyway.
The P0145R3 paper (which was accepted into C++17) refines the order of evaluation of several C++ constructs, including
Postfix expressions are evaluated from left to right. This includes functions calls and member
selection expressions
Specifically, the paper adds the following text to 5.2.2/4 paragraph of the standard:
The postfix-expression is sequenced before each expression in the
expression-list and any default argument. Every value computation and
side effect associated with the initialization of a parameter, and the
initialization itself, is sequenced before every value computation and
side effect associated with the initialization of any subsequent
parameter.
Could you explain the point of difference between variable initialization sequence if a program starts and does not start a thread?
sec. 3.6.2/3 N3797 c++14 working draft:
If a program starts a thread (30.3), the subsequent initialization of
a variable is unsequenced with respect to the initialization of a
variable defined in a different translation unit. Otherwise, the
initialization of a variable is indeterminately sequenced with respect
to the initialization of a variable defined in a different translation
unit.
Please, give an example, explaining that rule, if it possible.
Unsequenced means there is no sequence - the variables could be initialised concurrently, on different threads.
Indeterminately sequenced means that one is sequenced before the other - the variables are initialised sequentially, on the same thread - but it's not specified which is initialised first.
I have recently read Andrei Alexandrescu's Modern C++ Design. After reading 6. chapter, I begin to worry about our singletons at company. Since our experienced team leader writes core helper libraries like singletons etc... . I asked him if the way he handles singleton takes care of on dead reference problem ? If he used at_exit function call which is given by C core language?
He told me C++11 has singleton support and will execute CTORs and DTORs in a row that they will not be any dead reference problem. User will not have to cope with synchronization.
Even it sounds awesome I couldn't find any information which confirms him on internet. So please tell me if C++11 takes care of Dead Reference Problem for singletons and if so please explain a little what dark magic going behind ?
Presumably your team leader is talking about singletons implemented as follows:
T &get_value() {
static T val;
return val;
}
In this case, the standard gives two guarantees. The first is that the val objects will be constructed exactly once, the first time that the flow of program execution passes the declaration of the local static variable, even if that happens simultaneously on several threads 6.7/4:
An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
Static initialisation is only allowed in the case of constants, so as long as T does not have a constexpr constructor you shouldn't have to worry (but read 3.6.2 for the full rules, in case there is some edge case that is relevant to your code).
The second guarantee is that all variables with static storage duration will be destructed in the reverse order of their construction 3.6.3/1:
Destructors (12.4) for initialized objects (that is, objects whose lifetime (3.8) has begun) with static storage duration are called as a result of returning from main and as a result of calling std::exit (18.5). Destructors for initialized objects with thread storage duration within a given thread are called as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The completions of the destructors for all initialized objects with thread storage duration within that thread are sequenced before the initiation of the destructors of any object with static storage duration. If the completion of the constructor or dynamic initialization of an object with thread storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. If the completion of the constructor or dynamic initialization of an object with static storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. [Note: This definition permits concurrent destruction. — end note ] If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized. For an object of array or class type, all subobjects of that object are destroyed before any block-scope object with static storage duration initialized during the construction of the subobjects is destroyed. If the destruction of an object with static or thread storage duration exits via an exception, std::terminate is called (15.5.1).
While this paragraph gives a lot of scope for concurrent destruction of static objects when their construction was concurrent, the main thing to take away from it is that destruction happens in the reverse order of construction.
Together, these mean means that if T val depends on some U val in another of these singleton functions, the U val will always be constructed before T val and destructed after the T val, so overall, this is a safe way of implementing singletons (unless you're doing something very crazy).
I am very much trilled with the how a scoped_lock works and was wondering weather a similar implementation can be done as to time a particular code of execution
If say I implement a simple class scoped_timer which on construction initiates a timer and on deletion it stops and report the time elapsed, then would this sample code be timed correctly
func()
{
//some code
{
scoped_timer a;
//some code that does not include a
}
//some code
}
In practice am I guaranteed that scoped_time a is constructed at the beginning and destructed exactly when it is out of scope. Can the compiler decide to reorder the code in such a way as not to destruct it exactly at the end of scope or construct it at the beginning since there is no dependence on the object a? Are there guarantees from C++ standard?
Thanks
Daniel
The code is guaranteed to behave as you would like.
This guarantee is important in C++, because C++ is not a functional programming language, due to the fact that almost any function in C++ can have side effects (either from the flow of execution of the current thread, or from other threads or even other processes, whether or not the data is declared as volatile). Because of this, the language specification makes guarantees about the sequencing of full expressions.
To piece this together from the C++11 standard, there are a number of clauses that must be considered together.
The most important clause is §1.9:
§1.9 Program execution [intro.execution]
1 The semantic descriptions in this International Standard define a
parameterized nondeterministic abstract machine. This International
Standard places no requirement on the structure of conforming
implementations. In particular, they need not copy or emulate the
structure of the abstract machine. Rather, conforming implementations
are required to emulate (only) the observable behavior of the abstract
machine as explained below. * (<-- the footnote is in the standard itself)
* This provision is sometimes called the “as-if” rule, because an implementation is free to disregard any requirement of this
International Standard as long as the result is as if the requirement
had been obeyed, as far as can be determined from the observable
behavior of the program. For instance, an actual implementation need
not evaluate part of an expression if it can deduce that its value is
not used and that no side effects affecting the observable behavior of
the program are produced.
(The bolding of the text is mine.)
This clause imposes two important requirements that are relevant for this question.
If an expression may have side effects, it will be evaluated. In your case, the expression scoped_timer a; may have side effects, so it will be evaluated.
"...conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.", where "below" includes Clauses 13 and 14 of the same section:
§1.9.13 Sequenced before is an asymmetric, transitive, pair-wise
relation between evaluations executed by a single thread (1.10), which
induces a partial order among those evaluations. Given any two
evaluations A and B, if A is sequenced before B, then the execution of
A shall precede the execution of B. If A is not sequenced before B and
B is not sequenced before A, then A and B are unsequenced. [ Note: The
execution of unsequenced evaluations can overlap. —end note ]
Evaluations A and B are indeterminately sequenced when either A is
sequenced before B or B is sequenced before A, but it is unspecified
which. [ Note: Indeterminately sequenced evaluations cannot overlap,
but either could be executed first. —end note ]
§1.9.14 Every value computation and side effect associated with a
full-expression is sequenced before every value computation and side
effect associated with the next full-expression to be evaluated. * (<-- the footnote here is not relevant)
Therefore, your expression scoped_timer a; (which is a full expression) may have side effects and will be evaluated; so the computation of the value of a will be sequenced before any of the following statements in the block.
Regarding destruction of the object a, that is simpler.
§3.7.3.3 If a variable with automatic storage duration has initialization or a destructor with side effects, it shall not be
destroyed before the end of its block, nor shall it be eliminated as
an optimization even if it appears to be unused, except that a class
object or its copy/move may be eliminated as specified in 12.8.
This makes clear that the destructor will not be called until the block exits.
ADDENDUM And to confirm that all block-level variables are destroyed (and their destructor called) at the end of block scope, here it is in the C++11 standard:
§3.7.3.1 Block-scope variables explicitly declared register or not explicitly declared static or extern have automatic storage duration.
The storage for these entities lasts until the block in which they are
created exits.
§3.7.3.2 [ Note: These variables are initialized and destroyed as described in 6.7. —end note ]
... and the above-mentioned §6.7:
§6.7.2 Variables with automatic storage duration (3.7.3) are initialized each time their declaration-statement is executed.
Variables with automatic storage duration declared in the block are
destroyed on exit from the block (6.6).
The block is defined as all code between a pair of curly braces {} here:
§6.3.1 So that several statements can be used where one is expected, the compound statement (also, and equivalently, called “block”) is provided.
compound-statement:
{ statement-seq }
statement-seq:
statement
statement-seq statement
A compound statement defines a block scope (3.3).
Note: The compount-statement (etc) section takes a while to get used to, but the important point is that here, the open curly brace { and close curly brace } actually mean a literal open curly brace and close curly brace in the code. This is the exact place in the C++11 standard where block scope is defined as the sequence of statements between curly braces.
Putting the pieces together: Because the standard, as quoted above, says The storage for these entities lasts until the block in which they are created exits and that Variables with automatic storage duration declared in the block are destroyed on exit from the block, you are assured that the object a in your question (and ANY block-level object) will last until the end of the block, and will be destroyed and have its destructor called when the block exits.
Is a C++11-compliant compiler allowed to optimize/transform this code from:
bool x = true; // *not* an atomic type, but suppose bool can be read/written atomically
/*...*/
{
while (x); // spins until another thread changes the value of x
}
to anything equivalent to an infinite loop:
{
while (true); // infinite loop
}
The above conversion is certainly valid from the point of view of a single-thread program, but this is not the general case.
Also, was that optimization allowed in pre-C++11?
Absolutely.
Since x is not marked as volatile and appears to be a local object with automatic storage duration and internal linkage, and the program does not modify it, the two programs are equivalent.
In both C++03 and C++11 this is by the as-if rule, since accessing a non-volatile object is not considered to be a "side effect" of the program:
[C++11: 1.9/12]: Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes
both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.
C++11 does make room for a global object to have its value changed in one thread then that new value read in another:
[C++11: 1.10/3]: The value of an object visible to a thread T at a particular point is the initial value of the object, a value assigned to the object by T, or a value assigned to the object by another thread, according to the rules below.
However, if you're doing this, since your object is not atomic:
[C++11: 1.10/21]: The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.
And, when undefined behaviour is invoked, anything can happen.
Bootnote
[C++11: 1.10/25]: An implementation should ensure that the last value (in modification order) assigned by an atomic or synchronization operation will become visible to all other threads in a finite period of time.
Again, note that the object would have to be atomic (say, std::atomic<bool>) to obtain this guarantee.
The compiler is allowed go do anything to those two loops. Including terminating the program. Because infinite loops have undefined behaviour if they do not perform a synchronization-like operation (do something that requires synchronization with another thread or I/O), according to the C++ memory model:
Note that it means that a program with endless recursion or endless loop (whether implemented as a for-statement or by looping goto or otherwise) has undefined behavior.