What happens when a constructor function calls itself in VS2013? - c++

class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();
class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}
IA* GetA()
{
static A a;
return &a;
}
int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}
Obviously, class A's constructor function calls itself.
"static A a" will get stuck in a loop.
On VS2013, this code can get out from the loop and print "End" on the console.
On VS2017, this code gets stuck in a loop.
**What does VS2013 do for this code?

Nothing in particular has to happen. According to the C++ standard:
[stmt.dcl] (emphasis mine)
4 Dynamic initialization of a block-scope variable with static storage
duration or thread storage duration is performed 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. If control re-enters the
declaration recursively while the variable is being initialized, the
behavior is undefined. [ Example:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
 — end example ]
The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).
Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.
But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.

The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.
Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.
Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.

Related

std::atexit ordering when called from a global object's constructor

cppreference says about std::atexit :
The functions may be called concurrently with the destruction of the objects with static storage duration and with each other, maintaining the guarantee that if registration of A was sequenced-before the registration of B, then the call to B is sequenced-before the call to A, same applies to the sequencing between static object constructors and calls to atexit
I understand that passage to mean that, if std::atexit is called during static initialization, the registered function will be called during the destruction of static objects just before the destruction of the static object which was last initialized when the std::atexit that registered the function was called. I also interpret "may be called concurrently" to mean the calls can occur between static object destructions as opposed to the multi-threaded interpretation of the word.
What I'm wondering is whether a object is considered as initialized (in the context of this ordering) when it's initialization begins or when it completes. I wrote a short test to test this :
#include <cstdlib>
#include <iostream>
struct foo
{
foo()
{
std::cout << "ctor\n";
std::atexit([]() { std::cout << "atexit\n"; });
}
~foo()
{
std::cout << "dtor\n";
}
};
foo my_foo;
int main()
{
return 0;
}
The output I get is (http://cpp.sh/3bllu) :
ctor
dtor
atexit
This leads me to believe that my_foo is not considered to be initialized in this context until it's construction finishes. In other words, the function is considered to have been registered before my_foo was initialized so the registered function executes after my_foo's destruction.
I can't seem to find anything that would guarantee this behavior and I'm not even entirely sure my initial interpretation of the cited passage is correct. Is the behavior I've described something that I can rely on or is it implementation defined or even undefined behavior?
The call to the destructor will happen before the call to the function passed to atexit. From [basic.start.term], p5:
If a call to std::atexit strongly happens before the completion of
the initialization of an object with static storage duration, the call to the destructor for the object is sequenced
before the call to the function passed to std::atexit.

Calling setter once on function static variable

I'm using a function static variable that I want to initialize once by calling a setter:
void foo() {
static Obj obj;
obj.setName("name"); // this should be called once
// use obj
}
I don't want the setter to be called multiple times, also due to multi threading issues and I can't add a constructor to Obj since I don't own the code. So is this reasonable and thread safe:
void foo() {
static Obj obj = []() {
Obj o;
o.setName("name");
return o;
}();
// use obj
}
Yes, it's thread safe. Quoting n3337 - [stmt.dcl]/4, emphasis mine:
...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.
You perform copy initialization from a lambda's return value, but it's immaterial. The paragraph above doesn't constrain the initialization of obj to be value or direct initialization. All forms of initialization are applicable.
And as a side note, if you must contend with such poorly written types, I'd argue your solution is very idiomatic. It doesn't artificially introduce a new named function to perform the initialization. Instead it keeps initialization code localized. That is a good thing in my book.

Is it safe to wait for asynchronous work by joining in the destructor?

Suppose I have a class that may run some code asynchronously, and that asynchronous code uses that class instance to do things like call member functions, read data members, etc. Obviously the class instance must outlive the background thread in order for those accesses to be safe. It is sufficient to ensure this by joining the background thread in the destructor? For example:
#include <iostream>
#include <thread>
class foo final
{
public:
foo() = default;
void bar() {
std::cout << "Hopefully there's nothing wrong with using " << this << "\n";
}
void bar_async() {
if (!m_thread.joinable()) {
m_thread = std::thread{&foo::bar, this};
}
}
~foo() {
if (m_thread.joinable()) {
std::cout << "Waiting for " << m_thread.get_id() << "\n";
m_thread.join();
}
}
private:
std::thread m_thread;
};
int main() {
foo f;
f.bar_async();
}
Specifically, I'm worried about object lifetime rules:
For any object of class types whose destructor is not trivial, lifetime ends when the execution of the destructor begins.
... after the lifetime of an object has ended and before the storage which the object occupied is reused or released, the following uses of the glvalue expression that identifies that object are undefined: ...
Access to a non-static data member or a call to a non-static member function.
But to me, a strict reading of the above would also imply that calling this->bar() from inside ~foo() directly is undefined, which is "obviously" not the case.
cppreference is right but it is talking about accessing the members from the object, not from inside the destructor. If we look at [class.cdtor]/1 we see that
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
emphasis mine
So, as long as we are in the destructor we can still work with the member objects as those are not destroyed until the scope of the destructor ends.
So, calling join on the thread is fine here. If you think about it if it wasn't then things like lock guards would be useless if accessing the mutex they refer to was undefined behavior.
My intuition is no. This is because thread::join can throw an exception and you don't want exceptions escaping your destructor. It may be okay if you wrap it in a try catch and handle the exception properly though.

Do magic statics guarantee that right side is executed only once?

If I have
atomic<int> cnt=0;
int get_int() noexcept
{
cnt++;
return rand();
}
and then:
void func()
{
static const auto value = get_int();
}
I know that there will be no race condition on initialization of value, but I don't know if
get_int() will be called once, or in my example will cnt be 1 (and not 2, 3, 4, or 5).
Assume multiple threads enter func() and get_int has only 1 callsite in func().
C++11 guarantees that there will be no race condition N3797 - §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.92 If control re-enters the declaration recursively while the variable is being
initialized, the behavior is undefined. [ Example:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
- end example ]
It's not re-entrant but thread-safe. Make sure there will not be other parts of the code that will call get_int() before func() anyway.
get_int() will be called only once from that line , but given your code, get_int() could be called beforehand from different locations in the code.

What's a "recursive_init_error" exception?

I decided to do a test with computed gotos and local statics
void g() { std::cout << "init "; }
void f() {
int z = 0;
y: z++;
static int x =
(g(), z == 1 ? ({ goto *&&y; 0; }) : 0);
}
int main() { f(); std::cout << "!"; f(); }
I wanted to see whether the output would be "init init !". But to my surprise I didn't get that output, but instead GCC handled it gracefully, outputting at runtime:
init terminated by recursive_init_error: exception
What's that exception? Is it a Standard exception? C++03 or C++0x? Thanks for any explanation.
It's caused by what is stated in C++03 §6.7/4:
... Otherwise such an object is initialized the first time control passes through its declaration; such an object 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 re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined. [Example:
int foo(int i)
{
static int s = foo(2*i); // recursive call – undefined
return i+1;
}
--end example]
GCC throws an exception in that case. Here's some documentation about it.
C++11 update: The following wording was added in C++11, just before the text about the recursive case:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.88
88 The implementation must not introduce any deadlock around execution of the initializer.
Doesn't change the problem here, but does make this construct thread-safe when there is no recursion.