What happens if I use wait_for on a future, which goes out of scope due to a timeout and then set_value is called on a promise? It would make little sense if it was undefined, however, I want to be sure and did not find an answer of my own. Example code below
#include <future>
#include <iostream>
#include <thread>
void task1() {
std::promise<void> promise;
auto futureOutOfScopeWork = [&promise]() {
std::future<void> future = promise.get_future();
auto status = future.wait_for(std::chrono::milliseconds(500));
if (status == std::future_status::ready) {
std::cout << "in time" << std::endl;
} else if (status == std::future_status::timeout) {
std::cout << "timeout" << std::endl;
} else {
std::cout << "invalid state" << std::endl;
}
};
std::thread futureThread(futureOutOfScopeWork);
futureThread.detach();
std::this_thread::sleep_for(std::chrono::milliseconds(750));
promise.set_value();
}
int main() {
std::thread startEverything(task1);
startEverything.detach();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
the startEverythring thread initiates a promise. startEverything then
starts another thread which invokes get_future on the promise. The future then goes out of scope because of a timeout. Am I running into undefined behaviour here? get_future was called, the future was destroyed and set_value invoked in a promise whose linked future hast gone out of scope.
Related
I'm studying concurrency in C++ and I'm trying to implement a multithreaded callback registration system. I came up with the following code, which is supposed to accept registration requests until an event occurs. After that, it should execute all the registered callbacks in order with which they were registered. The registration order doesn't have to be deterministic.
The code doesn't work as expected. First of all, it rarely prints the "Pushing callback with id" message. Secondly, it sometimes hangs (a deadlock caused by a race condition, I assume). I'd appreciate help in figuring out what's going on here. If you see that I overcomplicate some parts of the code or misuse some pieces, please also point it out.
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
class CallbackRegistrar{
public:
void registerCallbackAndExecute(std::function<void()> callback) {
if (!eventTriggered) {
std::unique_lock<std::mutex> lock(callbackMutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
callbackCond.wait(lock, [this, saved_id]{return releasedCallback.first == saved_id;});
releasedCallback.second();
callbackExecuted = true;
eventCond.notify_one();
}
else {
callback();
}
}
void registerEvent() {
eventTriggered = true;
while (!registeredCallbacks.empty()) {
releasedCallback = registeredCallbacks.front();
callbackCond.notify_all();
std::unique_lock<std::mutex> lock(eventMutex);
eventCond.wait(lock, [this]{return callbackExecuted;});
callbackExecuted = false;
registeredCallbacks.pop();
}
}
private:
std::queue<std::pair<unsigned, std::function<void()>>> registeredCallbacks;
bool eventTriggered{false};
bool callbackExecuted{false};
std::mutex callbackMutex;
std::mutex eventMutex;
std::condition_variable callbackCond;
std::condition_variable eventCond;
unsigned callback_id{1};
std::pair<unsigned, std::function<void()>> releasedCallback;
};
int main()
{
CallbackRegistrar registrar;
std::thread t1(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "First!\n";});
std::thread t2(&CallbackRegistrar::registerCallbackAndExecute, std::ref(registrar), []{std::cout << "Second!\n";});
registrar.registerEvent();
t1.join();
t2.join();
return 0;
}
This answer has been edited in response to more information being provided by the OP in a comment, the edit is at the bottom of the answer.
Along with the excellent suggestions in the comments, the main problem that I have found in your code is with the callbackCond condition variable wait condition that you have set up. What happens if releasedCallback.first does not equal savedId?
When I have run your code (with a thread-safe queue and eventTriggered as an atomic) I found that the problem was in this wait function, if you put a print statement in that function you will find that you get something like this:
releasedCallback.first: 0, savedId: 1
This then waits forever.
In fact, I've found that the condition variables used in your code aren't actually needed. You only need one, and it can live inside the thread-safe queue that you are going to build after some searching ;)
After you have the thread-safe queue, the code from above can be reduced to:
class CallbackRegistrar{
public:
using NumberedCallback = std::pair<unsigned int, std::function<void()>>;
void postCallback(std::function<void()> callback) {
if (!eventTriggered)
{
std::unique_lock<std::mutex> lock(mutex);
auto saved_id = callback_id;
std::cout << "Pushing callback with id " << saved_id << std::endl;
registeredCallbacks.push(std::make_pair(callback_id, callback));
++callback_id;
}
else
{
while (!registeredCallbacks.empty())
{
NumberedCallback releasedCallback;
registeredCallbacks.waitAndPop(releasedCallback);
releasedCallback.second();
}
callback();
}
}
void registerEvent() {
eventTriggered = true;
}
private:
ThreadSafeQueue<NumberedCallback> registeredCallbacks;
std::atomic<bool> eventTriggered{false};
std::mutex mutex;
unsigned int callback_id{1};
};
int main()
{
CallbackRegistrar registrar;
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++)
{
threads.push_back(std::thread(&CallbackRegistrar::postCallback,
std::ref(registrar),
[i]{std::cout << std::to_string(i) <<"\n";}
));
}
registrar.registerEvent();
for (auto& thread : threads)
{
thread.join();
}
return 0;
}
I'm not sure if this does exactly what you want, but it doesn't deadlock. It's a good starting point in any case, but you need to bring your own implementation of ThreadSafeQueue.
Edit
This edit is in response to the comment by the OP stating that "once the event occurs, all the callbacks should be executed in [the] order that they've been pushed to the queue and by the same thread that registered them".
This was not mentioned in the original question post. However, if that is the required behaviour then we need to have a condition variable wait in the postCallback method. I think this is also the reason why the OP had the condition variable in the postCallback method in the first place.
In the code below I have made a few edits to the callbacks, they now take input parameters. I did this to print some useful information while the code is running so that it is easier to see how it works, and, importantly how the condition variable wait is working.
The basic idea is similar to what you had done, I've just trimmed out the stuff you didn't need.
class CallbackRegistrar{
public:
using NumberedCallback = std::pair<unsigned int, std::function<void(int, int)>>;
void postCallback(std::function<void(int, int)> callback, int threadId) {
if (!m_eventTriggered)
{
// Lock the m_mutex
std::unique_lock<std::mutex> lock(m_mutex);
// Save the current callback ID and push the callback to the queue
auto savedId = m_currentCallbackId++;
std::cout << "Pushing callback with ID " << savedId << "\n";
m_registeredCallbacks.push(std::make_pair(savedId, callback));
// Wait until our thread's callback is next in the queue,
// this will occur when the ID of the last called callback is one less than our saved callback.
m_conditionVariable.wait(lock, [this, savedId, threadId] () -> bool
{
std::cout << "Waiting on thread " << threadId << " last: " << m_lastCalledCallbackId << ", saved - 1: " << (savedId - 1) << "\n";
return (m_lastCalledCallbackId == (savedId - 1));
});
// Once we are finished waiting, get the callback out of the queue
NumberedCallback retrievedCallback;
m_registeredCallbacks.waitAndPop(retrievedCallback);
// Update last callback ID and call the callback
m_lastCalledCallbackId = retrievedCallback.first;
retrievedCallback.second(m_lastCalledCallbackId, threadId);
// Notify one waiting thread
m_conditionVariable.notify_one();
}
else
{
// If the event is already triggered, call the callback straight away
callback(-1, threadId);
}
}
void registerEvent() {
// This is all we have to do here.
m_eventTriggered = true;
}
private:
ThreadSafeQueue<NumberedCallback> m_registeredCallbacks;
std::atomic<bool> m_eventTriggered{ false};
std::mutex m_mutex;
std::condition_variable m_conditionVariable;
unsigned int m_currentCallbackId{ 1};
std::atomic<unsigned int> m_lastCalledCallbackId{ 0};
};
The main function is as above, except I am creating 100 threads instead of 10, and I have made the callback print out information about how it was called.
for (int createdThreadId = 0; createdThreadId < 100; createdThreadId++)
{
threads.push_back(std::thread(&CallbackRegistrar::postCallback,
std::ref(registrar),
[createdThreadId](int registeredCallbackId, int callingThreadId)
{
if (registeredCallbackId < 0)
{
std::cout << "Callback " << createdThreadId;
std::cout << " called immediately, from thread: " << callingThreadId << "\n";
}
else
{
std::cout << "Callback " << createdThreadId;
std::cout << " called from thread " << callingThreadId;
std::cout << " after being registered as " << registeredCallbackId << "\n";
}
},
createdThreadId));
}
I am not entirely sure why you want to do this, as it seems to defeat the point of having multiple threads, although I may be missing something there. But, regardless, I hope this helps you to understand better the problem you are trying to solve.
Experimenting with this code some more, I found out why the "Pushing callback with id " part was rarely printed. It's because the call to registrar.registerEvent from the main thread was usually faster than the calls to registerCallbackAndExecute from separate threads. Because of that, the condition if (!eventTriggered) was almost never fulfilled (eventTriggered had been set to true in the registerEvent method) and hence all calls to registerCallbackAndExecute were falling into the else branch and executing straightaway.
Then, the program sometimes also didn't finish, because of a race condition between registerEvent and registerCallbackAndExecute. Sometimes, registerEvent was being called after the check if (!eventTriggered) but before pushing the callback to the queue. Then, registerEvent completed instantly (as the queue was empty) while the thread calling registerCallbackAndExecute was pushing the callback to the queue. The latter thread then kept waiting forever for the event (that had already happened) to happen.
For some time I have been trying to use std::thread, and in my project i wanted to make sure that the threads are not making one thing couple times at once, that's why i am trying to make a simple project that has something like "check" if thread is done, and then start again
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
using namespace std::chrono_literals;
void Thing()
{
std::this_thread::sleep_for(3s);
}
int main()
{
std::packaged_task<void()> task(Thing);
auto future = task.get_future();
std::thread t(std::move(task));
while (true) {
auto status = future.wait_for(0ms);
if (status != std::future_status::ready)
{
std::cout << "not yet" << std::endl;
}
else
{
t.join();
std::cout << "Join()" << std::endl;
}
std::this_thread::sleep_for(300ms);
}
}
using this code i have error at line with std::cout << "Join()" << std::endl; and the error says: Unhandled exception at 0x7632A842 in dasd.exe: Microsoft C++ exception: std::system_error at memory location 0x00AFF8D4.
this error is comes out when the thread is ready, and t.join() is called.
output of this project:
not yet
...
not yet
Join()
Thank You in advance
As you can see https://en.cppreference.com/w/cpp/thread/thread/join
join has as post condition
joinable() is false
and in error condition
invalid_argument if joinable() is false
So you cannot call it twice as you do.
You probably want to break the loop once you call join or rewrite your loop such as:
while (future.wait_for(300ms) != std::future_status::ready) {
std::cout << "not yet" << std::endl;
}
t.join();
std::cout << "Join()" << std::endl;
Actually, you don't need a while loop there. Instead, you could simply call join.
What join does is to wait for the thread to finish its job. After the thread finishes its job, it exits and cleans the stack and the second call to join doesn't make sense at all.
I also would suggest using std::async in case you want an async function that also returns a value.
I'm reading this tutorial on how to safely exit from threads.
In essence, it passes a future object to the function that is going to be executed from the thread, and checks, at every while loop, if that future already has a value (if it has, it exits the thread). See:
void threadFunction(std::future<void> futureObj)
{
std::cout << "Thread Start" << std::endl;
while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout)
{
std::cout << "Doing Some Work" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::cout << "Thread End" << std::endl;
}
The problem is that in order to check if futureObj is already setted, it has to wait for some time (here, 1 millissecond). So, I'm losing 1 millissecond on every iteration of the thread. Shouldn't this be preferable:
void threadFunction(bool *shouldStop)
{
std::cout << "Thread Start" << std::endl;
while (!*shouldStop)
{
std::cout << "Doing Some Work" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::cout << "Thread End" << std::endl;
}
In order to check shouldStop, no time is wasted. So my thread runs faster. Why he didn't do that?
UPDATE:
Here's a simple class that should do the same thing, I guess.
class Stoppable
{
private:
std::atomic<bool> _shouldContinue;
public:
Stoppable()
{
_shouldContinue.store(true);
}
virtual void run() = 0;
void operator()()
{
run();
}
bool shouldContinue()
{
return _shouldContinue.load();
}
void stop()
{
_shouldContinue.store(false);
}
};
Then, to use it, just subclass Stoppable and do like this:
void MySubClass::run()
{
while (shouldContinue())
{
//...
}
}
The second proposal:
void threadFunction(bool *shouldStop)
Is I am afraid wrong if you intend to set *shouldStop from another thread.
In this case it should be void threadFunction(std::atomic<bool> *shouldStop).
There is no one "right way" to signal a thread to stop as it will depend on whether you want your thread to work continually or only work when there is work to do. But there are wrong ways - usually involving undefined behaviour because of writing to a non-atomic variable in one thread and reading from it in another.
If you want your thread to continually work until stopped (for example, this is common in graphics rendering threads or real-time game logic threads) then there is no reason to wait before checking your stop signal.
If your thread is a worker thread which will only run while there is work to do, then it's more normal to use a std::condition_variable, std::mutex and a queue plus a signal.
In case it's interesting, here's an example of an implementation of a work queue. Note that this one permits completion of the work out of order with respect to submission. There are many strategies available:
#include <condition_variable>
#include <mutex>
#include <queue>
#include <functional>
#include <thread>
#include <vector>
#include <iostream>
#include <iomanip>
struct worker_control
{
std::queue<std::function<void()>> work_queue;
std::mutex m;
std::condition_variable cv;
bool stop_signal = false;
void post_work(std::function<void()> f)
{
auto lock = std::unique_lock(m);
work_queue.push(std::move(f));
lock.unlock();
cv.notify_one();
}
void request_stop()
{
auto lock = std::unique_lock(m);
stop_signal = true;
lock.unlock();
cv.notify_all();
}
};
std::mutex emit_mutex;
template<class...Args>
void emit(Args&&...args)
{
auto lock = std::unique_lock(emit_mutex);
std::cout << std::this_thread::get_id() << " : ";
((std::cout << args), ...);
std::cout << '\n';
}
void run_worker(worker_control& control)
{
auto should_run = [&]
{
return not control.work_queue.empty() or control.stop_signal;
};
while (1)
{
auto lock = std::unique_lock(control.m);
control.cv.wait(lock, should_run);
// at this point we own the lock on control.m
if (not control.work_queue.empty())
{
auto work = std::move(control.work_queue.front());
control.work_queue.pop();
lock.unlock(); // allow other workers access to the queue
work();
}
else
{
// we can only have got here if there is no work to do and we have been asked to stop
return;
}
}
}
int main()
{
std::vector<std::thread> worker_threads;
auto control = worker_control();
worker_threads.emplace_back([&]{ run_worker(control); });
worker_threads.emplace_back([&]{ run_worker(control); });
worker_threads.emplace_back([&]{ run_worker(control); });
control.post_work([]{ emit("the"); });
control.post_work([]{ emit("quick"); });
control.post_work([]{ emit("brown"); });
control.post_work([]{ emit("fox"); });
control.post_work([]{ emit("jumps"); });
control.post_work([]{ emit("over"); });
control.post_work([]{ emit("the"); });
control.post_work([]{ emit("lazy"); });
control.post_work([]{ emit("dog"); });
control.request_stop();
for (auto& t : worker_threads)
if (t.joinable())
t.join();
}
Example output:
140244960823040 : the
140244960823040 : fox
140244960823040 : jumps
140244960823040 : over
140244960823040 : the
140244960823040 : lazy
140244960823040 : dog
140244969215744 : quick
140244952430336 : brown
https://coliru.stacked-crooked.com/a/c1612695a3cfc955
What I want to is invoking a method foo() with a timeout (say 1 minute). If its execution costs less than 1 minute, return the result. Otherwise an exception will be thrown. Here is the code:
//PRINT "START" IN THE LOG
auto m = std::make_shared<std::mutex>();
auto cv = std::make_shared<std::condition_variable>();
auto ready = std::make_shared<bool>(false);
auto response = std::make_shared<TResponse>();
auto exception = std::make_shared<FooException>();
exception->Code = ErrorCode::None;
std::thread([=]
{
std::unique_lock<std::mutex> lk(*m);
cv->wait(lk, [=]{ return *ready; });
try
{
//PRINT "PROCESS" IN THE LOG
auto r = foo();
*response = std::move(r);
}
catch(const FooException& e)
{
*exception = std::move(e);
}
lk.unlock();
cv->notify_one();
}).detach();
std::unique_lock<std::mutex> lk(*m);
*ready = true;
cv->notify_one();
auto status = cv->wait_for(lk, std::chrono::seconds(60));
if (status == std::cv_status::timeout)
{
//PRINT "TIMEOUT" IN THE LOG
//throw timeout exception
}
else
{
//PRINT "FINISH" IN THE LOG
if (exception->Code == ErrorCode::None)
{
return *response;
}
else
{
throw *exception;
}
}
You can see I add logs START/PROCESS/FINISH/TIMEOUT in the code, every time this method is executed, I can see START/PROCESS/FINISH or START/PROCESS/TIMEOUT pattern in the logs. However, sometimes the logs are START/PROCESS, without any FINISH/TIMEOUT. I think cv->wait_for should block the current thread for 60 seconds at most, then it exists with either TIMEOUT or FINISH.
The foo() method contains disk IO operations to network drives that sometimes hangs for more than 1 hour(the reason is not related to this question, and it can't be resolved now), I tried to replace foo with a thread sleep, everything is working as expected. What's wrong with this code and how can I improve this?
Because you have no predicate in the cv->wait_for call, the thread might be unblocked spuriously. However, it is strange that no FINISH/TIMEOUT is printed. So we might need more information here: What does happen with the program? Does it hang, does it throw, does it just exit, does it print in the line after cv->wait_for?
You could try using std::async and see if the same behavior appears (furthermore, it would greatly simplify your code):
std::future<int> res = std::async(foo);
std::future_status stat = res.wait_for(std::chrono::seconds(60));
if (stat != std::future_status::ready) {
std::cout << "Timed out..." << "\n";
} else {
try {
int result = res.get();
std::cout << "Result = " << result << std::endl;
} catch (const FooException& e) {
std::cerr << e.what() << '\n';
}
}
EDIT As pointed out in the comments by CuriouslyRecurringThoughts the future of std::async blocks in the destructor. If that is not an option, the following code uses a std::promise and a detached thread instead:
std::promise<int> prom;
std::future<int> res = prom.get_future();
std::thread([p = std::move(prom)]() mutable {
try {
p.set_value(foo());
} catch (const std::exception& e) {
p.set_exception(std::current_exception());
}
}).detach();
Waiting for the std::future is done as shown before.
It seems that despite the timed wait your main thread deadlocks because even when cv->wait_for returns with timeout it still tries to lk.lock() on the mutex which is currently locked by the second thread.
As mentioned on cppreference about wait_for:
When unblocked, regardless of the reason, lock is reacquired and wait_for() exits.
I'm not sure why the promise/future solution didn't work for you since you didn't post that example here, but I've tried a simple version of it which seems to work even when the second thread "hangs":
using namespace std::chrono_literals;
std::cout << "START" << std::endl;
std::promise<void> p;
auto f = p.get_future();
std::thread t([p = std::move(p)]() mutable {
std::cout << "PROCESS" << std::endl;
std::this_thread::sleep_for(5min);
p.set_value();
});
auto status = f.wait_for(5s);
std::cout << (status == std::future_status::ready ? "FINISH" : "TIMEOUT") << std::endl;
t.join();
The output is as expected:
START
PROCESS
TIMEOUT
We can create a separate thread to run the call itself, and wait on a condition variable back in your main thread which will be signaled by the thread doing the call to foo once it returns.
The trick is to wait on the condition variable with your 60s timeout, so that if the call takes longer than the timeout you will still wake up, know about it, and be able to throw the exception - all in the main thread.
Please find below a code example:
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std::chrono_literals;
int foo()
{
//std::this_thread::sleep_for(10s); //Will Return Success
std::this_thread::sleep_for(70s); //Will Return Timeout
return 1;
}
int foo_wrapper()
{
std::mutex m;
std::condition_variable cv;
int retValue;
std::thread t([&cv, &retValue]()
{
retValue = foo();
cv.notify_one();
});
t.detach();
{
std::unique_lock<std::mutex> lock(m);
if(cv.wait_for(lock, 60s) == std::cv_status::timeout)
throw std::runtime_error("Timeout");
}
return retValue;
}
int main()
{
bool timedout = false;
try {
foo_wrapper();
}
catch(std::runtime_error& e) {
std::cout << e.what() << std::endl;
timedout = true;
}
if(!timedout)
std::cout << "Success" << std::endl;
else
std::cout << "Failure" << std::endl;
return 0;
}
If we use std::this_thread::sleep_for(10s); inside foo will return SUCCESS
And, if we use std::this_thread::sleep_for(70s); inside foo will return TIMEOUT
I hope it helps!
As Mike van Dyke says, and the documentation makes quite clear, you need a predicate to use a condition variable correctly, to deal with spurious wakeups:
When the condition variable is notified, a timeout expires, or a spurious wakeup occurs, the thread is awakened, and the mutex is atomically reacquired. The thread should then check the condition and resume waiting if the wake up was spurious.
Any use of a condvar for waiting without a loop and predicate is wrong. It should always have either an explicit while(!predicate) loop or look something like:
std::unique_lock<std::mutex> lk(*m);
auto status = cv->wait_for(lk, std::chrono::seconds(60), predicate);
if (status == std::cv_status::timeout)
{ /*...*/ } else { /*...*/ }
which means you need some predicate to check: setting *ready = false before notifying the condvar in your thread (and using !*ready as your predicate) would be fine.
As for why you didn't see the expected result - I have no idea, because I can't see your real logging code or what happens outside the code snippet you provided. Waking from wait_for without either having timed out or received a valid response or exception is the most likely, but you'll either have to debug your code or provide a complete example to help with that.
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;
}