C++ scoped variables re-ordering and timers - c++

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.

Related

Function interleaving in pre C++17

Look at this simple function call:
f(a(), b());
According to the standard, call order of a() and b() is unspecified. C++17 has the additional rule which doesn't allow a() and b() to be interleaved. Before C++17, there was no such rule, as far as I know.
Now, look at this simple code:
int v = 0;
int fn() {
int t = v+1;
v = t;
return 0;
}
void foo(int, int) { }
int main() {
foo(fn(), fn());
}
With C++17 rules, v surely will have the value of 2 after the call of foo. But, it makes me wonder, with pre-C++17, is the same guaranteed? Or could it be that v ends up 1? Does it make a difference, if instead of int t = v+1; v = t;, we just have v++?
Functions calls were not allowed to interleave in previous versions as well.
Quoting from C++11 final draft (n3337)
1.9 Program execution [intro.execution]
...
15. ...
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. —end note ]
Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function9.
9) In other words, function executions do not interleave with each other.
Similar wording can be found in the final draft of the C++14 version as well.
v must be 2 in all versions of C++ (and C). The function fn() must be executed twice, and clearly each time it executes it will increment v. There is no multithreading here, no data race, and no possibility for fn() to be executed only partially and then interrupted while the other invocation of fn() proceeds.
C++17 has the additional rule which doesn't allow a() and b() to be interleaved. Before C++17, there was no such rule, as far as I know.
There were rules that applied here, though the wording and some details have grown more exact.
C++03 [intro.execution]/8:
Once the execution of a function begins, no expressions from the calling function are evaluated until execution of the called function has completed. [footnote 8]
[footnote 8]: In other words, function executions do not interleave with each other.
Though you could argue this doesn't actually say anything about other functions called from the calling function in the text, and footnotes are officially not normative.
C++11 changed the wording, largely because it introduced multithreading semantics. C++11 and C++14 [intro.execution]/15, emphasis mine:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [Note: Value computations and side effects associated with different argument expressions are unsequenced. - end note] Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function. [footnote 9]
[footnote 9] In other words, function executions do not interleave with each other.
This wording, I think, leaves no doubt, at least in most cases.
C++17 [intro.execution]/18:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. For each function invocation F, for every evaluation A that occurs within F and every evaluation B that does not occur within F but is evaluated on the same thread and as part of the same signal handler (if any), either A is sequenced before B or B is sequenced before A. [footnote 10] [Note: If A and B would not otherwise be sequenced then they are indeterminately sequenced. - end note]
[footnote 10] In other words, function executions do not interleave with each other.
This is a much more general statement about all evaluations in separate functions, not just arguments in a function call. But as far as I know, this more precise wording just clarifies some possibly ambiguous cases, but doesn't really change semantic behavior much.
I suspect we are confusing two concepts. Call order was only fixed within expressions in c++17. Interleaving of the statements in a function between 2 calls of functions has always been disallowed, though when the optimiser gets hold of your code, only the effect is guaranteed.
If you write a() ; b() then a() will be fully executed before b is then fully executed. Same if you write a(), b() or a() && b()
If you write a() + b() then there is no guarantee prior to c++17 that a() will be executed before b(), but there is a guarantee that whichever is executed first will be fully completed before the other is executed.
If you write c(a(), b()) it [is my understanding] that again there is no guarantee that a() will be executed before b(), but there /is/ the guarantee that the effects of the first executed function (whichever that is) will complete before the second is started, and of course there is the guarantee that c() will not be executed until both a() and b() have completed.
The optimiser should honour the intent of that guarantee, though it may take the statements of a() and b(), and even c(), and intermingle them, the effect of running the code should be the same as if they were run in sequence. Could the -O0 code do a() then b() and the -O3 code do b() then a()? Perhaps, though that would make the debugging quite difficult, so I would hope not.
The compiler only has to give the effect in the final result, and it is possible in multithreading for another thread to observe the effects of consecutive lines of code occur out of order unless specific thread-aware constructs are used.
It is my understanding that the optimiser can't allow the specific effects of a(), b(), and c() to appear to occur out of order between each function, though prior to c++17 the order of a() and b() is not well defined - all effects of b() might occur before all effects of a().

std::make_shared() change in C++17

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.

How can we predict the output of the following C++ Program [duplicate]

This question already has answers here:
Undefined behavior and sequence points
(5 answers)
Closed 6 years ago.
I am confused about the output of the code.
It depends on what compiler i run the code. Why is it so?
#include <iostream>
using namespace std;
int f(int &n)
{
n--;
return n;
}
int main()
{
int n=10;
n=n-f(n);
cout<<n;
return 0;
}
Running it on the Ubuntu terminal with g++, the output is 1 whereas running it on Turbo C++ ( the compiler we used in school) gives output as 0.
In C++03, modifying a variable and also using its value in the same expression, without an intervening C++03 sequence point, was Undefined Behavior.
C++03 §5/4:
” Between the previous
and next sequence point a scalar object shall have its stored value modified at most once by the evaluation
of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full
expression; otherwise the behavior is undefined.
Undefined Behavior, UB, provides the compiler with an opportunity to optimize, because it can assume that UB does not occur in a valid program.
However, with all the myriad UB rules of C++ it's difficult to reason about source code.
In C++11 sequence points were replaced with sequenced before, indeterminately sequenced and unsequenced relations:
C++11 §1.9/3
” 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.
And with the new C++11 sequence relationship rules the modification in the function in the code in question is indeterminately sequenced with respect to the use of the variable, and so the code has unspecified behavior rather than Undefined Behavior, as noted by Eric M Schmidt in a comment to (the first version of) this answer. Essentially that means that there is no danger of nasal daemons or other possible UB effects, and that the behavior is a reasonable one. The two possible behaviors here are that the modification via the function call is done before the use of the value, or that it's done after the use of the value.
Why it's unspecified behavior:
C++11 §1.9/15:
” Every evaluation in the calling function (including other function calls) that is not otherwise specifically
sequenced before or after the execution of the body of the called function is indeterminately sequenced with
respect to the execution of the called function.
What “unspecified behavior” means:
C++11 §1.3.25:
” unspecified behavior
Behavior, for a well-formed program construct and correct data, that depends on the implementation
[Note: The implementation is not required to document which behavior occurs. The range of possible
behaviors is usually delineated by this International Standard. —end note ]
Why the modification effected by the assignment is not problematic:
C++11 §5.17/1
” In all cases, the assignment is sequenced after the value
computation of the right and left operands, and before the value computation of the assignment expression.
This is also quite different from C++03.
As the rather drastic edit of this answer shows, following Eric's comment, this kind of issue is not simple! The main advice I can give is to as much as possible just Say No™ to effects governed by subtle or very complex rules, the corners of the language. Simple code has a better chance of being correct, while so called clever code does not have a good chance of being significantly faster.

Concurrent object destruction

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

Allowed compiler optimizations on loops in C++11

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.