Discussion:
C++: PTHREAD_CANCEL_ASYNCHRONOUS and random crash
(too old to reply)
Joel Yliluoma
2007-07-28 10:55:40 UTC
Permalink
I am trying to terminate threads in a C++ program using POSIX threads.
I use pthread_cancel(), and the thread is configured with
PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_ASYNCHRONOUS.

However, when I run this test program, it crashes randomly, saying
"terminate called without an active exception".

How do I fix it so that it does not crash?
I want to use PTHREAD_CANCEL_ASYNCHRONOUS, because the thread will
be doing a loop of cpu-intensive stuff and I don't want to insert
pthread_testcancel() in the loop.

If I replace the std::vector<> with a custom-built class that has
constructors and destructors, it will still crash. If I use a plain
old C array, it does not crash. However, in the actual program where
I need this solution, there are C++ objects and it cannot be easily
avoided.

#include <vector>

#include <stdio.h>
#include <pthread.h>

void* Runner(void*p)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);

const unsigned n=40000;

std::vector<int> temp(n);

for(unsigned c=0; c<5; ++c)
for(unsigned a=0; a<n; ++a)
temp[a]=n-a;
fprintf(stderr, "- Got %u\n", n);
return NULL;
}

int main(void)
{
const unsigned n = 5;
pthread_t threads[n];
for(;;)
{
fprintf(stderr, "Creating %u threads\n", n);
for(unsigned a=0; a<n; ++a)
pthread_create(&threads[a], NULL, Runner, NULL);

unsigned c = 2;
fprintf(stderr, "Cancelling thread %u\n", c);
pthread_cancel(threads[c]);

fprintf(stderr, "Joining %u threads\n", n);
for(unsigned a=0; a<n; ++a)
pthread_join(threads[a], NULL);

fprintf(stderr, "** Everything ok\n");
}
}
--
Joel Yliluoma - http://bisqwit.iki.fi/
: comprehension = 1 / (2 ^ precision)
Ian Collins
2007-07-28 11:42:11 UTC
Permalink
Post by Joel Yliluoma
I am trying to terminate threads in a C++ program using POSIX threads.
I use pthread_cancel(), and the thread is configured with
PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_ASYNCHRONOUS.
However, when I run this test program, it crashes randomly, saying
"terminate called without an active exception".
On what platform?
--
Ian Collins.
Joel Yliluoma
2007-07-28 11:52:58 UTC
Permalink
Post by Ian Collins
Post by Joel Yliluoma
I am trying to terminate threads in a C++ program using POSIX threads.
I use pthread_cancel(), and the thread is configured with
PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_ASYNCHRONOUS.
However, when I run this test program, it crashes randomly, saying
"terminate called without an active exception".
On what platform?
Linux, both 64-bit and 32-bit are affected.
Compiler is g++-4.1, tested also 3.3; crashes but more tersely ("Abort").
--
Joel Yliluoma - http://bisqwit.iki.fi/
: comprehension = 1 / (2 ^ precision)
David Schwartz
2007-07-28 19:22:06 UTC
Permalink
Post by Joel Yliluoma
I am trying to terminate threads in a C++ program using POSIX threads.
I use pthread_cancel(), and the thread is configured with
PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_ASYNCHRONOUS.
However, when I run this test program, it crashes randomly, saying
"terminate called without an active exception".
Well, what did you expect? What do you think should happen if you
cancel a thread while it's in the middle of a memory allocation?
Post by Joel Yliluoma
How do I fix it so that it does not crash?
Don't leave asynchronous cancellation enabled when your thread is in a
state where asynchronous cancellation is not known to be safe.
Post by Joel Yliluoma
I want to use PTHREAD_CANCEL_ASYNCHRONOUS, because the thread will
be doing a loop of cpu-intensive stuff and I don't want to insert
pthread_testcancel() in the loop.
Then you must make sure asynchronous cancellation is only enabled when
the thread is in a cancellation-safe state.
Post by Joel Yliluoma
If I replace the std::vector<> with a custom-built class that has
constructors and destructors, it will still crash.
Because the constructors and destructors are not asynchronous
cancellation safe, no surprise there.
Post by Joel Yliluoma
If I use a plain
old C array, it does not crash.
Because you have no asynchronous cancellation unsafe states.
Post by Joel Yliluoma
However, in the actual program where
I need this solution, there are C++ objects and it cannot be easily
avoided.
You have three choices:

1) Don't use asynchronous cancellation.

2) Make sure any thread you plan to cancel asynchronously never enters
a state where asynchronous cancellation is unsafe.

3) Disable asynchronous cancellation when you enter states where
asynchronous cancellation is unsafe.

DS
Joel Yliluoma
2007-08-01 07:31:12 UTC
Permalink
Post by David Schwartz
Well, what did you expect? What do you think should happen if you
cancel a thread while it's in the middle of a memory allocation?
It is not a problem pertinent to memory allocation.

In fact, I think it has more to do with C++ standard forbidding
the throwing of exceptions while in execution of a destructor.

Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
--
Joel Yliluoma - http://bisqwit.iki.fi/
: comprehension = 1 / (2 ^ precision)
Torsten Robitzki
2007-08-01 08:35:50 UTC
Permalink
Post by Joel Yliluoma
Post by David Schwartz
Well, what did you expect? What do you think should happen if you
cancel a thread while it's in the middle of a memory allocation?
It is not a problem pertinent to memory allocation.
In fact, I think it has more to do with C++ standard forbidding
the throwing of exceptions while in execution of a destructor.
That is simply not true. You can throw an exception from a destructor
but it is very seldom a good idea. std::unexpected() will be called if
an exception will be thrown while exception handling is in progress.
Post by Joel Yliluoma
Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
It might be implemented as an exception. To write robust, reliable C++
code one need some functions that have the guaranty to never ever throw
an exception (std::vector<>::swap() is such a function). So cancelling a
thread that is executing C++ code, that is not written do be cancelled
asynchron, leads to weak and unreliable programs.

If you really have to use asynchron cancellation, you should enable
cancellation just for the parts of the program, you have written and
that are save to be cancelled asynchron. So a call to
std::vector<>::push() wouldn't be save to be cancelled.

regards,
Torsten
Ian Collins
2007-08-01 08:46:39 UTC
Permalink
Post by Joel Yliluoma
Post by David Schwartz
Well, what did you expect? What do you think should happen if you
cancel a thread while it's in the middle of a memory allocation?
It is not a problem pertinent to memory allocation.
It might be, all sorts of nasties might happen if a vector is being
allocated. There's a more than fair chance of a lockup if either the
vector code or IO has a mutex locked when the thread is canceled.
Post by Joel Yliluoma
In fact, I think it has more to do with C++ standard forbidding
the throwing of exceptions while in execution of a destructor.
There is no such prohibition, just good style.
Post by Joel Yliluoma
Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
Who says the cancel is an exception? If it was and the exception was
unhandled, why the "terminate called without an active exception" message?
--
Ian Collins.
Joel Yliluoma
2007-08-01 09:09:29 UTC
Permalink
Post by Ian Collins
Post by Joel Yliluoma
It is not a problem pertinent to memory allocation.
It might be, all sorts of nasties might happen if a vector is being
allocated. There's a more than fair chance of a lockup if either the
vector code or IO has a mutex locked when the thread is canceled.
Yes, it might be, but it is not the common denominator. The same error
happens regardless of whether there is code involved that allocates
memory dynamically or not.
It has more to do with constructors and destructors.
For example, the code attached below does the same error, even though
there is no dynamic memory allocation involved.
Post by Ian Collins
Post by Joel Yliluoma
In fact, I think it has more to do with C++ standard forbidding
the throwing of exceptions while in execution of a destructor.
There is no such prohibition, just good style.
Ah, I see.
Post by Ian Collins
Post by Joel Yliluoma
Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
Who says the cancel is an exception? If it was and the exception was
unhandled, why the "terminate called without an active exception" message?
Well, that is to say GCC seems to do it that way...
With the frame unwinding and all.

Example code below. No dynamic memory allocation.

#include <stdio.h>
#include <pthread.h>

const unsigned n=40000;

struct tempobj
{
public:
tempobj();
~tempobj();

int& ref() { return a; }

int a;
};
tempobj::tempobj() { }
tempobj::~tempobj() { }

void* Runner(void*p)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);

tempobj temp;
for(unsigned c=0; c<5; ++c)
for(unsigned a=0; a<n; ++a)
temp.ref() = n-a;

putchar('.');
return NULL;

}
volatile unsigned v;
int main(void)
{
const unsigned n = 5;
pthread_t threads[n];
for(;;)
{
for(unsigned a=0; a<n; ++a)
pthread_create(&threads[a], NULL, Runner, NULL);

for(unsigned b=0; b<400000; ++b) v=b;

unsigned c = 2;
pthread_cancel(threads[c]);
for(unsigned a=0; a<n; ++a)
pthread_join(threads[a], NULL);
}
}
--
Joel Yliluoma - http://bisqwit.iki.fi/
: comprehension = 1 / (2 ^ precision)
Joel Yliluoma
2007-08-01 09:13:54 UTC
Permalink
Post by Joel Yliluoma
Example code below. No dynamic memory allocation.
Compile with inlining disabled to invoke the error.
I didn't bother going to lengths to ensure compiler won't inline stuff.
--
Joel Yliluoma - http://bisqwit.iki.fi/
: comprehension = 1 / (2 ^ precision)
Ian Collins
2007-08-01 09:31:06 UTC
Permalink
Post by Joel Yliluoma
Post by Ian Collins
Post by Joel Yliluoma
It is not a problem pertinent to memory allocation.
It might be, all sorts of nasties might happen if a vector is being
allocated. There's a more than fair chance of a lockup if either the
vector code or IO has a mutex locked when the thread is canceled.
Yes, it might be, but it is not the common denominator. The same error
happens regardless of whether there is code involved that allocates
memory dynamically or not.
It has more to do with constructors and destructors.
For example, the code attached below does the same error, even though
there is no dynamic memory allocation involved.
Not on my system (Solaris), it just runs forever. Constructors and
destructors are just function calls.

The point wasn't so much the exact reason why the problem occurred, but
that allowing asynchronous cancellation during library calls where it
isn't guaranteed to be safe is a bad idea.
Post by Joel Yliluoma
Post by Ian Collins
Post by Joel Yliluoma
Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
Who says the cancel is an exception? If it was and the exception was
unhandled, why the "terminate called without an active exception" message?
Well, that is to say GCC seems to do it that way...
With the frame unwinding and all.
Um, then I'd have expected there to be an active exception when
terminate was called.
--
Ian Collins.
David Schwartz
2007-08-02 04:54:36 UTC
Permalink
Post by Joel Yliluoma
Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
Pthread cancellation is most definitely *not* an exception. It is
neither thrown, caught, nor handled.

DS
Dave Butenhof
2007-08-02 13:55:54 UTC
Permalink
Post by David Schwartz
Post by Joel Yliluoma
Since the cancel is an exception, and it may be thrown at _any_
time, it may also be thrown during the execution of a destructor,
leading into the shown behavior.
Pthread cancellation is most definitely *not* an exception. It is
neither thrown, caught, nor handled.
Mostly true. ;-)

Pthread cancellation is, in fact, designed and intended to be an
exception, and the standard was carefully written to not only ALLOW but
(to the small extent feasible) ENCOURAGE implementation of cancellation
using exceptions where available. POSIX cleanup handlers are nothing but
detached exception finalization (with no continuation option simply
because of the limitations of POSIX semantics).

Although based in truth on Ada 'finally' semantics, in C++ terms the
intent would probably best map into pthread_cleanup_push() constructing
a local "cancel guard" object that would properly manage calling the
cleanup handler.

But of course as neither ISO C nor POSIX had any such concept as
"exception", there was no way to formally require anything truly useful.

So, to correct your statement, "POSIX most definitely does not require
that Pthread cancellation be an exception."

On any platform with any interest at all in C++ and threading, the
platform's C runtime & compiler WILL support exceptions and cancellation
WILL BE an exception, and the C/C++/threads runtime code will
interoperate to make this all work correctly; but as this spans several
independent standards there's no way to specify that it "shall work". We
must instead simply rely on people being smart enough to figure out that
it SHOULD work. ;-)

Loading...