I'm attempting to implement a scope guard class and am running into problems with cancellation points on Linux. From what I've found, Linux implements pthread cancellation in terms of exceptions, where the exception cannot be handled in a user's catch block (i.e. it must be rethrown). This causes problems for the following scope guard implementation that attempts to ignore any exceptions that may be thrown by its associated function:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <functional>
struct scope_guard {
template <typename T>
scope_guard(T func) : function_(func) {}
~scope_guard() {
try {
function_();
}
catch (...) {}
}
std::function<void()> function_;
};
extern "C" {
void* thread_func(void*) {
try {
scope_guard guard( [](void) {
// wait for a cancellation event
while (true) {
pthread_testcancel();
}
});
throw int();
}
catch (int&) {}
return 0;
}
}
int main(void) {
pthread_t thread;
int ret;
if (0 != (ret = pthread_create(&thread, NULL, &thread_func, NULL))) {
fprintf(stderr, "pthread_create failed: %d\n", ret);
return 1;
}
sleep(1);
if (0 != (ret = pthread_cancel(thread))) {
fprintf(stderr, "pthread_cancel failed: %d\n", ret);
return 1;
}
void* thread_ret;
if (0 != (ret = pthread_join(thread, &thread_ret))) {
fprintf(stderr, "pthread_join failed: %d\n", ret);
return 1;
}
return 0;
}
~scope_guard() {
try {
function_();
}
catch (abi::__forced_unwind&) {
throw;
}
catch (...) {}
}
This works fine on Solaris, HPUX and AIX, but on Linux it produces the following error and aborts:
FATAL: exception not rethrown
This is caused by the catch (...)
block in ~scope_guard
which catches but does not rethrow the pthread cancellation exception.
Based on this article, it appears that the recommended way to handle these cancellation exceptions is to either rethrow the exception from a catch (...)
block, or to explicitly catch and rethrow the cancellation exception like so:
~scope_guard() {
try {
function_();
}
catch (abi::__forced_unwind&) {
throw;
}
catch (...) {}
}
Both of these approaches however cause a new exception to be thrown from this descructor, which causes the problem to terminate as an existing exception is already in the process of unwinding the stack. In my tests the following is reported:
terminate called without an active exception
Is there a way to handle this scenario on Linux?