The following code spawns 2 functions as Asio handlers (which use Boost.Coroutine under the hood) on the same thread.
Each function throws and catches an exception in such an order so that the code in the catch blocks overlaps due to coroutine context switching, as follows:
fn1 throw
fn1 start catch
fn2 throw
fn1 end catch
fn2 end catch
And as it turns out (according to the log below), when the fn1's catch ends the exception object from the fn2 is destroyed, and when fn2's catch ends the exception object from fn1 is destroyed.
Effectively this means the catch block must not access its exception object after it was interrupted by other coroutines.
Is this an expected behavior or a bug in the compiler / Boost?
Is it safe at all to use context switching in a catch blocks like that?
GCC 4.8.5, 4.9.2, 8.2.0, Clang 5.0.0, Boost 1.55, 1.66, Linux, x86_64
The test code:
#define BOOST_COROUTINES_NO_DEPRECATION_WARNING
#define BOOST_COROUTINE_NO_DEPRECATION_WARNING
#include <iostream>
#include <string>
#include <boost/asio/spawn.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/steady_timer.hpp>
namespace ba = boost::asio;
ba::io_service io_service;
#define LOG_TRACE(x) do { std::cout << x << std::endl; } while (0)
class MyEx: public std::runtime_error {
public:
MyEx(std::string msg)
: std::runtime_error(msg)
{
LOG_TRACE("MyEx " << msg << " addr=" << (void*)this);
}
~MyEx()
{
LOG_TRACE("~MyEx " << what() << " addr=" << (void*)this);
}
};
void throw_ex(size_t n)
{
throw MyEx("exception " + std::to_string(n));
}
void sleep_ms(size_t ms, ba::yield_context yield)
{
ba::steady_timer timer(io_service);
timer.expires_from_now(std::chrono::milliseconds(ms));
timer.async_wait(yield);
}
void* curr_ex()
{
auto ep = std::current_exception();
return *reinterpret_cast<void**>(&ep);
}
void fn1(ba::yield_context yield)
{
try {
sleep_ms(100, yield);
throw_ex(1);
}
catch (const MyEx& e) {
LOG_TRACE("1: catch start " << e.what() << " curr_ex=" << curr_ex());
sleep_ms(300, yield);
LOG_TRACE("1: catch end, curr_ex=" << curr_ex());
}
}
void fn2(ba::yield_context yield)
{
try {
sleep_ms(200, yield);
throw_ex(2);
}
catch (const MyEx& e) {
LOG_TRACE("2: catch start " << e.what() << " curr_ex=" << curr_ex());
sleep_ms(300, yield);
LOG_TRACE("2: catch end, curr_ex=" << curr_ex());
}
}
int main ()
{
ba::spawn(io_service, [](ba::yield_context yield){ fn1(yield); });
ba::spawn(io_service, [](ba::yield_context yield){ fn2(yield); });
io_service.run();
return 0;
}
The output:
MyEx exception 1 addr=0x21fafe0
1: catch start exception 1 curr_ex=0x21fafe0
MyEx exception 2 addr=0x21fb080
2: catch start exception 2 curr_ex=0x21fb080
1: catch end, curr_ex=0x21fb080
~MyEx exception 2 addr=0x21fb080
2: catch end, curr_ex=0x21fafe0
~MyEx exception 1 addr=0x21fafe0
Related
GCC permits one to resume C++20 coroutines from catch sections and in the coroutine to call co_await again from its catch sections.
What is considered then the current handled exception in such cases?
Please consider an example:
#include <coroutine>
#include <iostream>
struct ReturnObject {
struct promise_type {
ReturnObject get_return_object() { return { std::coroutine_handle<promise_type>::from_promise(*this) }; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
void return_void() {}
};
std::coroutine_handle<promise_type> h_;
};
ReturnObject coroutine()
{
try {
std::cout << "Exception 1 being thrown\n";
throw 1;
}
catch( int a ) {
std::cout << "Exception caught in coroutine " << a << std::endl;
co_await std::suspend_always{};
try {
std::cout << "Current exception being rethrown\n";
throw;
}
catch( int b ) {
std::cout << "Exception caught in coroutine " << b << std::endl;
}
}
}
int main()
{
auto h = coroutine().h_;
try {
std::cout << "Exception 0 being thrown" << std::endl;
throw 0;
}
catch( int a ) {
std::cout << "Exception " << a << " caught in main" << std::endl;
h();
}
h();
h.destroy();
}
It prints ( https://gcc.godbolt.org/z/4deoG7Pnh ):
Exception 0 being thrown
Exception 0 caught in main
Exception 1 being thrown
Exception caught in coroutine 1
Current exception being rethrown
Exception caught in coroutine 0
Here I am mostly interesting in the last line, since it shows that throw; re-throws 0 and not 1, which was actually the one being processed. Is it correct?
co_await can only appear outside of a catch block, as specified by the standard:
An await-expression shall appear only in a potentially-evaluated expression within the compound-statement of a function-body outside of a handler.
co_yield is defined as a variation of co_await, so it has the same limitations:
A yield-expression shall appear only within a suspension context of a function ([expr.await]).
So the compiler should have deemed the code il-formed and issued an error.
Compiler: g++ 9.2.0
Operating system: Windows 10
g++ call:
g++ -E main.cpp -v -o main.i
g++ -c main.cpp -v -o main.o
g++ main.o -v -o main.exe
main.exe
main.cpp:
#include <chrono>
#include <iostream>
#include <string>
#include <exception>
#include <iostream>
//#include <thread>
#include "mingw.thread.h"
struct Object{
struct Exception : public std::exception{
std::string error_;
Exception(std::string str){
this->error_ = str;
}
~Exception() {
}
std::string get(){
return error_;
}
};
void DoSomeWork() {
try {
std::thread AnotherTh(&Object::GenerateException ,this);
AnotherTh.detach ();
while(true);
}
catch (...) {
throw ;
}
}
void GenerateException(){
std::this_thread::sleep_for (std::chrono::seconds(5));
throw Object::Exception ("Some error");
}
};
int main(){
try{
Object instance;
std::thread th(&Object::DoSomeWork,std::ref(instance));
th.join ();
}
catch (Object::Exception &ex ) {
std::cout << ex.get ();
}
catch (std::exception &ex ){
std::cout << ex.what ();
}
catch (...){
}
std::cout << "never reach this";
return 0;
}
Output:
terminate called after throwing an instance of 'Object::Exception'
what(): std::exception
I am launching main thread with a new thread (th) and wait for it ,inside of th starts another thread where exception will be throwed. So ,when it appears ,starts stack unwinding (from Object::GenerateException to Object::DoSomeWork as there is no more calls is Object::GenerateException's stack) and management is passed to Object::DoSomeWork's try-catch ,there is the same calls chain to main's try-catch as Object::DoSomeWork "knows" it was called from main.
I cannot understand why it cannot handle exception and pass it to main's try-catch.
Why program cannot reach proper return instruction after stack unwinding c++?
Because your code creates multiple threads and you are not catching the exception in the thread which is actually throwing the exception. The exceptions won't be propagated across threads even if you call join() member function of std::thread.
Try blocks are defined as dynamic constructs of the stack. A try block catches exceptions thrown by code reached dynamically, by call, from its contents.
When you create a new thread, you create a brand-new stack, that is not at all part of the dynamic context of the try block, even if the call to pthread_create or construct join-able std::thread() is inside the try.
To catch an exception originating in thread X, you have to have the try-catch clause in thread X (for example, around everything in the thread function, similarly to what you already do in main).
For a related question, see How can I propagate exceptions between threads?.
An example:
#include <chrono>
#include <iostream>
#include <string>
#include <exception>
#include <iostream>
#include <thread>
struct Object {
void DoSomeWork()
{
std::cout << "DoSomeWork Thread ID: " << std::this_thread::get_id() << std::endl;
try {
std::thread thread(&Object::GenerateException, this);
thread.detach();
while(true);
}
catch (...) {
std::cout << "Caught exception: " << std::this_thread::get_id() << std::endl;
throw ;
}
}
void GenerateException(void)
{
std::cout << "GenerateException Thread ID: " << std::this_thread::get_id() << std::endl;
try {
std::this_thread::sleep_for (std::chrono::seconds(5));
throw std::runtime_error("Some error");
} catch (...) {
std::cout << "Caught exception: " << std::this_thread::get_id() << std::endl;
throw;
}
}
};
int main()
{
std::cout << "Main Thread ID: " << std::this_thread::get_id() << std::endl;
try {
Object instance;
std::thread th(&Object::DoSomeWork,std::ref(instance));
th.join();
}
catch (const std::exception &ex) {
std::cout << ex.what() << std::endl;
std::cout << "Exception caught at: " << std::this_thread::get_id() << std::endl;
}
std::cout << "never reach this" << std::endl;
return 0;
}
Output:
Main Thread ID: 140596684195648
DoSomeWork Thread ID: 140596665124608
GenerateException Thread ID: 140596656670464
Caught exception: 140596656670464
terminate called after throwing an instance of 'std::runtime_error'
what(): Some error
Aborted (core dumped)
From this std::thread reference:
... if it terminates by throwing an exception, std::terminate is called.
If an uncaught exception is thrown in a thread, then the program will be forcibly terminated.
Let's say we have two running threads that both would throw exceptions and there are exception handlers in these threads.
Would C++ be able to handle that, not running into terminated or undefined behavior.
Is it correct that exception belongs to per thread, and each thread can have no more than one exception at a time?
Is it correct that exception belongs to per thread
That is correct.
and each thread can have no more than one exception at a time?
A thread can have more than one active exception. See int uncaught_exceptions() noexcept:
Detects how many exceptions in the current thread have been thrown or rethrown and not yet entered their matching catch clauses.
E.g.:
#include <iostream>
#include <stdexcept>
void f() {
throw std::runtime_error("error");
}
struct A {
~A() {
std::cout << "uncaught_exceptions: " << std::uncaught_exceptions() << '\n';
}
};
struct B {
~B() {
try {
A a;
f();
}
catch(std::exception&) {}
}
};
int main() {
try {
B b;
f();
}
catch(std::exception&) {}
}
Outputs:
uncaught_exceptions: 2
The following example shows that the exception handler is using the stack of thread t1 which made a division by zero exception. It means that exception belongs to per thread.
// g++ -std=c++0x -pthread -fnon-call-exceptions main.cpp
#include <iostream>
#include <thread>
#include <signal.h>
void handler(int signo) {
int handler_local_var;
std::cout << &handler_local_var << " in stack of handler" << std::endl;
throw signo;
}
void thread1(std::string msg) {
int t1_local_var;
std::cout << &t1_local_var << " in stack of " << msg << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
signal(SIGFPE,handler);
try {
int x = 100 / 0; /* ignore warning: division by zero [-Wdiv-by-zero] */
}
catch (...) {
std::cout << "caught" << std::endl;
}
while (1) {
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
void thread2(std::string msg) {
int t2_local_var;
std::cout << &t2_local_var << " in stack of " << msg << std::endl;
while (1) {
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
int main() {
int main_local_var;
std::cout << &main_local_var << " in stack of main" << std::endl;
std::thread t1(thread1,"t1");
std::thread t2(thread2,"t2");
while (1) {
std::this_thread::sleep_for(std::chrono::seconds(2)); /* Ctrl-C to stop */
}
return 0;
}
Test result:
$ ./a.out
0x7ffee7fea788 in stack of main
0x7f0b54b92d68 in stack of t2
0x7f0b55393d54 in stack of t1
0x7f0b55393754 in stack of handler
caught
In below code snippet i am trying to catch the exception after re-throwing the same but couldn't achieve the same . I am not sure what went wrong though as i already have preserved current teptr state through current_exception(). Thread is running in continuous loop so once its value reaches to the greater 2 then catch block is executed and control reaches out of the loop but still as expected i am not able to reach the other catch block in first attempt itself.
#include <boost/thread.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/chrono.hpp>
#include <iostream>
#include <boost/exception/all.hpp>
#include <exception>
using namespace std;
boost::exception_ptr teptr;
class myexception : public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
class ABC
{
public:
void start();
};
void ABC::start()
{
int i = 0;
cout << "running the thread" << std::endl;
while (1)
{
try
{
std::cout << "value of " << i << '\n';
if (i > 2)
{
throw boost::enable_current_exception(myex);
}
i++;
}
catch (exception& e)
{
cout << "actual exception is" << e.what() << '\n';
teptr = boost::current_exception();
break;
//throw myex;
}
}
}
int main()
{
ABC abc;
boost::thread thread_point;
while (1)
{
boost::thread thread_point;
thread_point = boost::thread(&ABC::start, abc);
if (teptr) {
try {
boost::rethrow_exception(teptr);
}
catch (const std::exception &ex)
{
std::cerr << "Thread exited with exception: " << ex.what() << "\n";
exit(0);
}
}
}
}
Your program access the variable teptr (as well as myex) from multiple threads simultaneously without synchronization. The behaviour is UNDEFINED.
What's worse, you're shadowing thread_point and creating many threads that aren't joined. You're literally running unlimited threads sharing the same global data.
I suppose you're really looking for futures - that allow you to return a value or an exception from wherever. All the exception handling magic is done for you:
Live On Coliru
#include <thread>
#include <future>
#include <iostream>
#include <sstream>
struct ABC {
int task(int until) {
for (int i = 0; i<10; ++i) {
if (i > until) {
std::ostringstream oss;
oss << "My exception happened in thread " << std::this_thread::get_id() << " at i=" << i;
throw std::runtime_error(oss.str());
}
}
return 42;
}
};
int main() {
for (int runs = 0; runs < 10; ++runs) {
ABC abc;
std::future<int> result = std::async(&ABC::task, &abc, rand()%20);
try {
std::cout << "Task returned " << result.get() << "\n";
} catch (const std::exception &ex) {
std::cout << "Task exited with exception: " << ex.what() << "\n";
std::cerr << "Thread exited with exception: " << ex.what() << "\n";
}
}
}
Prints (e.g.):
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=4
Thread exited with exception: My exception happened in thread 140288972076800 at i=4
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=7
Thread exited with exception: My exception happened in thread 140288972076800 at i=7
Task returned 42
Task returned 42
Task returned 42
Task returned 42
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=7
Thread exited with exception: My exception happened in thread 140288972076800 at i=7
Task returned 42
Task returned 42
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=2
Thread exited with exception: My exception happened in thread 140288972076800 at i=2
Update My answer is deficient and with error, see sehe's comment.
I'm not sure what your end goal is here but to figure out how to handle a throw from a thread. Yes, you can get around the compilers inability to throw between threads with Boost Exception.
#include <boost/thread.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/chrono.hpp>
#include <iostream>
#include <boost/exception/all.hpp>
#include <exception>
boost::exception_ptr teptr;
class myexception: public std::exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
class ABCc
{
public:
void start();
};
void ABCc::start()
{
int i=0;
std::cout<<"running the thread"<<std::endl;
while (1)
{
try
{
std::cout << "value of "<<i << '\n';
if(i>2)
{
throw boost::enable_current_exception(myex);
}
i++;
}
catch (std::exception& e)
{
std::cout << "actual exception is"<<e.what() << '\n';
teptr=boost::current_exception();
break;
// where were you going here???????
//throw myex;
}
}
}
int main()
{
ABCc abc;
boost::thread thread_point;
thread_point = boost::thread(&ABCc::start,abc);
while(1)
{
if (teptr) {
try {
boost::rethrow_exception(teptr);
}
catch(const std::exception &ex) {
std::cerr << "Thread may have exited; exception thrown: " << ex.what() << "\n";
break;
}
}
}
std::cout << "exception should have been caught" << std::endl;
return 0;
}
Note that you do not have to throw/catch in main. You were creating multiple threads by having boost::thread inside your loop, was that your intention?
I am trying to run some function in asynchronous manner. For this purpose I wrote class called Core where I use std::async to run function in different thread and std::shared_future<int> to wait for this thread and possibly to get future result. This is code of test program:
#include <iostream>
#include <future>
class Core : public std::enable_shared_from_this<Core>
{
public:
Core()
: isRunning_(false) {
};
~Core() {
isRunning_ = false;
if (f_.valid())
{
f_.wait();
std::cout << "Result is: " << f_.get() << std::endl;
}
};
void Start() {
isRunning_ = true;
auto self(shared_from_this());
f_ = std::async(std::launch::async, [self, this]() {
try {
while (true) {
if (!isRunning_)
break;
std::cout << "Boom" << std::endl; // Error occurs here
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
catch (const std::exception& e) {
std::cerr << "Loop error:" << e.what();
}
return 999;
});
}
private:
std::shared_future<int> f_;
std::atomic<bool> isRunning_;
};
int main()
{
try {
std::shared_ptr<Core> load(new Core);
load->Start();
throw std::runtime_error("Generate error"); // Added in order to generate error
}
catch (const std::exception& e) {
std::cout << "Error occurred: " << e.what();
}
return 0;
}
Each time when I start this program it crashes at this line:
std::cout << "Boom" << std::endl; // Error occurs here
with this error:
That is debugger error and call stack which I managed to get during debugging:
Looks like Core destructor function doesn't call at all. Why is it happens? weird!!!
Could you tell me where is my mistake? Thanks.
When main thread returns from main() it starts tearing down the environment before terminating the whole process. All this while background thread is accessing objects there are being destroyed or have been destroyed already.
I am not sure what you are triying to achieve, but you are doing something wrong:
Your lambda should execute some work and return immediately after it is done e.g. you should never loop forever.
Your main thread should wait for your future to complete by calling std::future<T>::get().