Sometimes it would be useful if a joinable std::thread had the hability to execute thread::join() on its destructor. See the examples below.
Example 1 (error):
The object std::thread has been destroyed after the throw of the exception. Once the the flow exits the scope, the destructor is called BEFORE the join happens. It makes STL show an error message of 'abort'.
int main( int argc, const char * argv[] )
{
try
{
thread t( [] ()
{
this_thread::sleep_for( chrono::seconds( 1 ) );
cout << "thread done" << endl;
} );
throw exception( "some exception" );
t.join();
}
catch ( const exception & )
{
cout << "exception caught!" << endl;
}
cout << "main done" << endl;
return 0;
}
Example 2 (correct way):
The object t is created before my try-catch block and the join() is put on both try and catch blocks. So it guarantees that the join() happens.
int main( int argc, const char * argv[] )
{
thread t;
try
{
t = thread( [] ()
{
this_thread::sleep_for( chrono::seconds( 1 ) );
cout << "thread done" << endl;
} );
throw exception( "some exception" );
t.join( );
}
catch ( const exception & )
{
t.join();
cout << "exception caught!" << endl;
}
cout << "main done" << endl;
return 0;
}
...AND THE QUESTION IS:
What is the reason for a joinable std::thread not join automatically on its destructor?
It would be much easier if it happened automatically. The way it's done today requires one must be careful when using threads inside try-catch blocks, for example... but I am sure someone THOUGHT when designed std::thread this way. So there must be a reason for that... what is that reason?
PS: I know we can envolve std::thread in a class and put the join() on the destructor of this new class... so it becomes automatic. But this is not the point. My question is really about std::thread itself.
The reason is simply so that you are forced to think about it. If a std::thread object is destroyed due to an exception escaping the scope then a join may cause a blocking wait during stack unwinding, which is often undesirable, and can lead to deadlock if the thread that is being waited for is in turn waiting for some action on the part of the thread doing the waiting.
By having the application terminate in this situation you as a programmer are forced to actively think about the conditions that would cause the object to be destroyed, and ensure that the thread is joined correctly.
Related
I'm working on a project that requires to execute some processes inside a docker container. I want to handle the case when the process doesn't terminate on time (let's say within 10 s).
I'm using this DockerClientpp library for managing the containers that basically just makes HTTP reqs to the Docker socket. Everything is fine up to this point.
To stop a container that is taking too long I'm using a separate thread. The problems is that I was able to implement it using ptheads but I cannot find a way using std::thread and lambas
Here is my working implementation with pthread
void *ContainerManager::spawnKiller(void *ref) {
ContainerManager *self = (ContainerManager *)ref;
std::unique_ptr<DockerClientpp::DockerClient> dc(new DockerClientpp::DockerClient());
std::cout << "[slave]forceStop(): Waiting " << self->timeOut << " before stopping " << self->activeId << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(self->timeOut));
try {
dc->stopContainer(self->activeId);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
pthread_exit(0);
}
void ContainerManager::execute() {
pthread_t killerId;
pthread_create(&killerId, nullptr, &(ContainerManager::spawnKiller), (void *)this);
pthread_detach(killerId);
}
And here is my std::thread and lambda implementation that fails with SEGFAULT as soon as I try to detach the thread.
void ContainerManager::execute() {
std::thread([this]() {
std::this_thread::sleep_for(std::chrono::seconds(timeOut));
try {
dc->stopContainer(activeId);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
}).detach();
}
And this is what gdb shows
Thread 1 "test" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x00000000007c6801 in std::thread::detach() ()
#2 0x0000000000410785 in ContainerManager::execute (this=0x7fffffffe2a0, processName=...)
at ../container_manager.cpp:223
#3 0x0000000000412c99 in ContainerManager::executeNew (this=0x7fffffffe2a0, processName=...,
replace=false, language=#0x7fffffffe020: ContainerManager::GO) at ../container_manager.cpp:336
#4 0x00000000004094a9 in main () at test.cpp:36
I tried with a regular function instead of a lamba, I tried capturing the parameters, I also tried passing the parameters as arguments but I'm stuck.
I haven't tried allocating the thread dynamically with new thread(...) but from my understanding even if the std::thread variable goes out of scope, the thread is still alive.
Do you have any suggestion on what I'm doing wrong? I feel like I'm really missing something about std::thread and lambda.
The execute method is a method of the class ContainerManager that it's guaranteed not to go out of scope before the spawned thread has terminated, also the variables that I use (timeOut and activeId are fields of the object)
EDIT:
It really seems there is something wrong with detach()
If I run this
void ContainerManager::execute() {
int *t = new int;
*t = timeOut;
std::string *s = new std::string;
*s = activeId;
std::thread x([&t, &s]() {
std::cout << "LOL" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(*t));
std::unique_ptr<DockerClientpp::DockerClient> _dc(new DockerClientpp::DockerClient());
try {
_dc->stopContainer(*s);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
});
std::cout << "Detaching" << std::endl;
if(x.joinable()) {
std::cout << ".. in a moment" << std::endl;
x.detach();
}
}
I get this output
Detaching
.. in a moment
Segmentation fault (core dumped)
EDIT 2
I tried running this code on my laptop and everything works fine
void ContainerManager::execute() {
// activeId and timeOut are fields of the ContainerManager object
std::thread([this]() {
std::this_thread::sleep_for(std::chrono::seconds(timeOut));
std::unique_ptr<DockerClientpp::DockerClient> dc(new DockerClientpp::DockerClient());
try {
dc->stopContainer(activeId);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
}).detach();
}
In the thread, you are accessing references to variables int *t and std::string *s which are local to the ContainerManager::execute() method. As soon as ContainerManager::execute() finishes, accesses to the two variables cause undefined behaviour and in your case the SEGFAULT. Instead pass the two pointers per value to the lamdba (and even better: don't use new at all):
void ContainerManager::execute() {
int *t = new int;
*t = timeOut;
std::string *s = new std::string;
*s = activeId;
std::thread x([t, s]() { // <<--- Pass by value
std::cout << "LOL" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(*t));
std::unique_ptr<DockerClientpp::DockerClient> _dc(new DockerClientpp::DockerClient());
try {
_dc->stopContainer(*s);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
});
std::cout << "Detaching" << std::endl;
if(x.joinable()) {
std::cout << ".. in a moment" << std::endl;
x.detach();
}
}
The segfault suggests, to me, that the class is going out of scope, even though you expect it not to. Another possibility is that you're getting a race condition on the variables you are accessing.
Rather than capturing this in the lambda, try passing all variables by copy to the lambda. This will remove any race conditions having to do with scope, and solve any potential lifetime issues as the lambda will be completely decoupled from any other threads. Of course, this means no pointers or references to data elsewhere, make sure you are really doing a full copy of timeOut and activeId.
Alternatively, rather than detach, I would recommend storing the thread as a data member of the class. Then, join in the destructor. If the thread finishes earlier, the join will basically be a no-op. If the thread is not finished, that will prevent the resources the thread is using from going out of scope until the thread is finished. This would address variables going out of scope, but not any race conditions. Race conditions can be solved by using std::atomic or mutexes.
Since the second solution (using join, std::atomic, and/or mutexes) is more convoluted and requires checking lifetimes and race conditions, I would recommend the first solution (using a lambda that doesn't capture anything, with all arguments passed by copy) if possible.
I've come across classes whose only function is to continuously do some work in a loop and they are designed such that they define a public method that can be called to invoke this member function in a new std::thread. I'm referring to something like this:
class ThreadLooper {
public:
ThreadLooper(const std::string &thread_name)
: thread_name_{thread_name}, loopCounter_{0} {}
~ThreadLooper() {
cout << thread_name_ << ": destroyed and counter is " << loopCounter_
<< std::endl;
}
void run() {
std::thread([this]() { detachedThreadLoop(); }).detach();
}
private:
void detachedThreadLoop() {
cout << thread_name_ << ": detachedThreadLoop() started running"
<< std::endl;
while (true) {
using namespace std::literals::chrono_literals;
std::this_thread::sleep_for(2s);
++loopCounter_;
cout << thread_name_ << ": counter is " << loopCounter_ << std::endl;
}
}
std::string thread_name_;
std::atomic_uint64_t loopCounter_;
};
int main() {
cout << "In main()" << std::endl;
{
ThreadLooper threadLooper{"looper1"};
threadLooper.run();
using namespace std::literals::chrono_literals;
std::this_thread::sleep_for(20s);
cout << "main() done sleeping, exiting block scope..." << std::endl;
}
while (true) {
using namespace std::literals::chrono_literals;
std::this_thread::sleep_for(20s);
cout << "main() woke up..." << std::endl;
}
return 0;
}
It seems like because the function running in the detached thread has a pointer to the instance but can continue to run beyond the lifetime of that instance this is bad. I've seen other classes where the thread isn't detached and then in the destructor a flag is set to tell the thread loop to exit and the thread is then joined in the destructor. It seems like the latter is the correct way to do this and that the former relies on the fact that the class will only be used in situations where instances of it live for the duration of the program. Is this correct or am I missing something?
Yes, using std::thread::detach means you need to have your own method of making sure the thread terminates before all the resources it uses are destroyed.
In this case ThreadLooper will invoke undefined behaviour when the program exits the first block scope in main(). It's better to not use detach() then std::thread will call std::terminate if you've forgotten to call join() before the thread (and its containing object) are destroyed.
At a point in my code, I pass a *this to a method foo(const MyClass& arg). An exception is thrown deep inside this foo, but although a syntactically correct try-catch block exists up the stack, it gets neither handled (a message should have been emitted in that case), nor the process crashes. From the debugging logs, I can see that related thread gets stuck, although the rest of the threads keep going.
I've been through stack unwinding documentation, and somewhere I've seen that arguments to functions are also considered to be auto variables, and get destroyed during the unwinding process. That brings me to the question: what happens when I pass a const reference of this (inside which there is a corresponding catch block) to a method where an exception is thrown? Is it possible that the ref gets the caller object destroyed, and catch block is now unreachable even though stack unwinding has begun already?
Let me add some pseudoish-code:
void MyClass0::someFunc(void)
{
try
{
MyClass1 obj1;
obj1.someOtherFunc(*this);
// Some other stuff
}
catch (MyException&)
{
std::cout << "Handling exception...";
// Whatever... This message is not emitted.
}
}
void MyClass1::someOtherFunc(const MyClass0& argObj0)
{
// Some functions that eventually throw an unhandled MyException
}
Thanks in advance...
EDIT:
OK, trying to generate an executable code for reference, I believe I pretty much answered my own question.
Here's the code:
#include "sandbox.h"
#include <iostream>
MyClass0::MyClass0(void)
{
std::cout << "\nConstructing MyClass0";
}
MyClass0::~MyClass0(void)
{
std::cout << "\nDestructing MyClass0";
}
void MyClass0::trustIssues(void)
{
std::cout << "\nEntering " << __FUNCTION__;
try
{
MyClass1 myClass1;
myClass1.unwaryFunction(*this);
}
catch (MyException& exc)
{
std::cout << "\nException caught in " << __FUNCTION__;
std::cout << "\nLeaving " << __FUNCTION__ << " from inside catch block.";
return;
}
std::cout << "\nLeaving " << __FUNCTION__;
}
MyClass1::MyClass1(void)
{
std::cout << "\nConstructing MyClass1";
}
MyClass1::~MyClass1(void)
{
std::cout << "\nDestructing MyClass1";
}
void MyClass1::unwaryFunction(MyClass0& argClass0)
{
std::cout << "\nEntering " << __FUNCTION__;
suicidalFunction();
std::cout << "\nLeaving " << __FUNCTION__;
}
void suicidalFunction(void)
{
std::cout << "\nEntering " << __FUNCTION__;
MyException myException;
throw myException;
std::cout << "\nLeaving " << __FUNCTION__;
}
int main(int argc, char* argv[])
{
MyClass0 myClass0;
myClass0.trustIssues();
return 0;
}
The output has been:
Constructing MyClass0
Entering MyClass0::trustIssues
Constructing MyClass1
Entering MyClass1::unwaryFunction
Entering suicidalFunction
Destructing MyClass1
Exception caught in MyClass0::trustIssues
Leaving MyClass0::trustIssues from inside catch block.
This implies that the *this argument does not get destroyed on stack unwinding of unwaryFunction. I probably have some other bug in the actual code (as the message analogous to "Exception caught in..." does not get printed). I'll keep this question for future reference. Thanks for your concern anyway.
I am using VS2013.
I just read this and found that a future should block in its destructor.
I tried some code but the std::future did not block.
void PrintFoo()
{
while (true)
{
std::cout << "Foo" << std::endl;
Sleep(1000);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
{
auto f = std::async(std::launch::async, PrintFoo);
}
while (true)
{
Sleep(1000);
std::cout << "Waiting" << std::endl;
}
std::cout << "Before application end" << std::endl;
return 0;
}
I have the output:
Foo
Waiting
Foo
Waiting
Am I misunderstanding something?
Yes. Your braces around f introduce a new scope, and because f is defined in that scope, it will get destroyed when that scope ends. Which is immediately after, and f will then block. So technically, it should print Foo every second.
The actual output is more interesting though. Your compiler interleaves the two infinite loops, which it isn't allowed to do (because your loop has side effects) since C++11 (I guess VS2013 isn't fully C++11 standards compliant yet).
I'm trying to implement timer with standard environment
Here is a code I have:
bool shutdownDetected = false;
void signal_handler(const int sigid)
{
shutdownDetected = true;
}
int main(int argc, const char * argv[])
{
signal(SIGTERM, (sig_t)signal_handler);
std::async(std::launch::async, [&] () {
std::this_thread::sleep_for( std::chrono::milliseconds{5000});
std::cout << "On TIMER!" << std::endl;
} );
std::cout << "main function" << std::endl;
while (!shutdownDetected) {
}
return EXIT_SUCCESS;
}
As result I see in output after 5 seconds:
// 5 seconds left
On Timer
main function
but would like to see:
main function
// 5 seconds left
On Timer
Seems that my implementation hangs main thread as well. How to avoid this?
Your std::async command returns an std::future, which is then immediately destroyed. The problem is that destruction of a future involves 'joining' the thread you created, which means that the destructor is going to wait until the thread has ended itself and code execution in your main thread doesn't advance until that process has completed.
Simple answer is to assign the result of your std::async call to a variable, and possibly call its get() member function in your loop that tests for termination.
auto t = std::async(std::launch::async, [&] () {
std::this_thread::sleep_for( std::chrono::milliseconds{5000});
std::cout << "On TIMER!" << std::endl;
} );
std::cout << "main function" << std::endl;
t.get();
std::async(std::launch::async, [&] () {
std::this_thread::sleep_for( std::chrono::milliseconds{5000});
std::cout << "On TIMER!" << std::endl;
} );
Does not work unless you assign the std::future returned by std::async to a variable and keep it around. I did not know why this is, clearly because I couldn't be bothered to look it up. Vincent Savard did, and linked us to documentation on the destructor for std::future which says:
it may block if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state.
Since the returnded std::future is not assigned to anything, it is instantly destroyed and the destructor blocks until completion.
I'm going to leave out the signal handler as it's not relevant to the problem.
#include <iostream>
#include <future>
int main()
{
auto letMeLive = std::async(std::launch::async, [] () {
std::this_thread::sleep_for( std::chrono::milliseconds{5000});
std::cout << "On TIMER!" << std::endl;
} );
std::cout << "main function" << std::endl;
letMeLive.wait(); // instead of the signal handler
return EXIT_SUCCESS;
}