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.
Related
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
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().
I have a class whose constructor will throw an exception. I also have a catch block to handle that exception. But still, I see that the exception is propagated back to the caller even though it is handled. I know, there should be an exception to inform the caller that construction failed. But in this case how does it (re-throw) happen?
class Test
{
public:
Test()
try
{
std::cout << "in child constructor" << std::endl;
throw 3.2;
}
catch(int &e)
{
std::cout << "in int catch: " << e << std::endl;
}
catch (double &e)
{
std::cout << "in double catch: " << e << std::endl;
}
};
int main (void)
{
try
{
Test obj;
}
catch (int &e)
{
std::cout << "in main int catch: " << e << std::endl;
}
catch (double &e)
{
std::cout << "in main double catch: " << e << std::endl;
}
return 0;
}
The output I got is
in child constructor
in double catch: 3.2
in main double catch: 3.2
This is correct according to standard. Section 15.3, point 15 of n3337.pdf reads:
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, a function returns when control reaches the end of a handler for the function-try-block (6.6.3). Flowing off the end of a function-try-block is equivalent to a return with no value; this results in undefined behavior in a value-returning function (6.6.3).
You can fully catch and prevent an exception from propagating inside constructor/destructor body. You cannot however catch the exceptions thrown from base class/member constructors/destructors this way.