Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thread Cancellation and C++ Exceptions

27 views
Skip to first unread message

kar...@cdotd.ernet.in

unread,
May 13, 1999, 3:00:00 AM5/13/99
to
Hi,
There seems to be a problem in canceling a thread which is in a method
which can throw an exception. I have included my program too. It
does the following

main() creates an object and goes to sleep for 10 seconds
The constructor of the object creates a thread
The thread sleeps for 600 seconds in a method which can throw
an exception.
main() wakes up and 'deletes' the object
The destructor of the object cancels the object's thread and
does a pthread_join() and prints the status.

When I execute the program, the main thread does a successful
pthread_cancel() and blocks in pthread_join() and the program terminates
abnormally.

In Solaris 2.6, it gives
Segmentation violation (core dumped)

In Digital Unix 4.0D, it gives
Resources Lost (core dumped)

However, if I compile the program with g++, it works fine. At present, I
have compiled g++ only for Digital. So I was able to run it only on
Digital.

The program works fine if I remove the 'throw' declaration of the
exception in the declaration of the method inside the class and in the
method definition. Pls. note that I DID NOT remove the actual statement
which throws the exception.

Here is the program.

# include <pthread.h>
# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <stdlib.h>

# include <iostream.h>

/**
** A test exception. Stores nothing
**/
class NewJunkException
{
public :

NewJunkException() {}
~NewJunkException() {}
};


/**
** The class which has the thread
**/
class Asd
{
public :

Asd();
~Asd();


~Asd();

private :

void *threadFunc() throw(NewJunkException);
static void *threadWrapper(void *);


pthread_t thrid;
};


/**
** main()
** Creates the object
** Sleeps for 10 seconds
** Deletes the object
**/
main()
{
cout << "main() : Creating object" << endl;
Asd *a = new Asd();

cout << "main() : sleep(10)" << endl;
sleep(10);

cout << "main() : Deleting object" << endl;
delete a;
}

/**
** The constructor. Just creates the thread.
**/
Asd:: Asd()
{
int rval;

cout << "Asd:: Asd() : pthread_create(&thrid,...)" << endl;
if((rval = pthread_create(&thrid, 0, threadWrapper, this)) != 0)
{
cout << "Asd:: Asd() : pthread_create() failed; " << strerror(rval)
<< endl;
exit(-1);
}

}

void *Asd:: threadWrapper(void *arg)
{
try
{
((Asd *) arg)->threadFunc();
}
catch(NewJunkException e)
{
}

return 0;
}

/**
** The thread's main function. Sleeps for 600 second
** so that the main thread will be able to send a
** cancel request after it's 10 second sleep.
**/
void *Asd:: threadFunc() throw(NewJunkException)
{

cout << "Asd:: threadFunc() : sleep(600)" << endl;
sleep(600);

throw NewJunkException();

return 0;
}


/**
** The destructor
** Cancels the thread
** Does a pthread_join()
**/
Asd:: ~Asd()
{
int rval;

cout << "Asd:: ~Asd() : pthread_cancel(thrid)" << endl;
if((rval = pthread_cancel(thrid)) != 0)
{
cout << "Asd:: ~Asd() : pthread_cancel() failed; " << strerror(rval)
<< endl;
exit(-1);
}
cout << "Asd:: ~Asd() : pthread_cancel(thrid) OVER" << endl;

void *status;
cout << "Asd:: ~Asd() : pthread_join()" << endl;
pthread_join(thrid, &status);
cout << "Asd:: ~Asd() : pthread_join()ed; status = " << status <<
endl;
printf("pthread_join()ed : status = %d\n", status);


}

The changes done to make this work were

1. The declaration

void *threadFunc() throw(NewJunkException);

was changed to

void *threadFunc();


2. The declaration of the method body

void *threadFunc() throw(NewJunkException)

was changed to

void *threadFunc()


The statement
throw NewJunkException();
was NOT touched at all.

After this change, there is no problem. When compiled with g++ there is
no problem even if these changes are not done.

How is it that it works with g++ and not with the C++ compilers of
Solaris and Digital Unix?

I am not sure if it is a thread related problem or an exception related
problem or if I am doing something silly. I am posting it here '
cause the error occurs in a thread. I am planning to post this is
comp.lang.c++ too.

Thanks in advance.

kar...@cdotd.ernet.in
dkarth...@cdotd.ernet.in


--== Sent via Deja.com http://www.deja.com/ ==--
---Share what you know. Learn what you don't.---

John Hickin

unread,
May 13, 1999, 3:00:00 AM5/13/99
to

As I read your posting I recall seeing, perhaps in this newsgroup, the
nice little ditty

C++ and Threads: welcome to a life of pain

pthreads says nothing about C++ and standard C++ says nothing about
threads of any kind so problems are highy likely to occur. Still, any
Unix C++ implementation should take pthreads into account in some way or
another; things will get a little better in time.

Programmers, however, should give their code every chance of succeeding.
In the absence of a help from any standard you should:

Assume it doesn't work everywhere

Here are some clues to fixing up your pthreads program:

1. Thread functions are extern "C"; a static member function isn't
extern "C".
2. C knows nothing of C++ exceptions; C++ exceptions should never be
allowed unwind the stack of a function compiled by a C compiler.

Case 2 arises in your example because your thread function is started up
by code, probably written in C and compiled with a C compiler, that is
part of the pthreads implementation.

You code could therefore have something like this:

extern "C" void* threadWrapperX( void* p ) {
return Asd::threadWrapper(p);
}

and pthread_create should use threadWrapperX.

Aside: some compilers can't tolerate extern "C" functions
and static member functions with the same name;
hence the trailing X.

Next Asd::threadWrapper should:

a) catch the NewJunkException by reference (see Meyers MEC++, Item 13).
b) Have a catch(...) clause.

Finally, I believe that reliable catching of non-POD classes requires a
virtual destructor.

Tom Payne

unread,
May 13, 1999, 3:00:00 AM5/13/99
to
kar...@cdotd.ernet.in wrote:
: Hi,

: There seems to be a problem in canceling a thread which is in a method
: which can throw an exception. I have included my program too. It
: does the following

: main() creates an object and goes to sleep for 10 seconds
: The constructor of the object creates a thread
: The thread sleeps for 600 seconds in a method which can throw
: an exception.

The object has not been constructed (and all access to it yields
undefined behavior) until the constructor has returned.

: main() wakes up and 'deletes' the object


: The destructor of the object cancels the object's thread and
: does a pthread_join() and prints the status.

If pthread's cancel involves a signal then again you have undefined
behavior when signal invoked code throws an exception.

Tom Payne

Dave Butenhof

unread,
May 17, 1999, 3:00:00 AM5/17/99
to
kar...@cdotd.ernet.in wrote:

> Hi,
> There seems to be a problem in canceling a thread which is in a method
> which can throw an exception. I have included my program too. It
> does the following
>
> main() creates an object and goes to sleep for 10 seconds
> The constructor of the object creates a thread
> The thread sleeps for 600 seconds in a method which can throw
> an exception.

> main() wakes up and 'deletes' the object
> The destructor of the object cancels the object's thread and
> does a pthread_join() and prints the status.

You've got two independent and asynchronous threads sharing the same
object, without any synchronization, and one of them is destroying it while
the other runs. Does that sound dangerous to you? It should.

> When I execute the program, the main thread does a successful
> pthread_cancel() and blocks in pthread_join() and the program terminates
> abnormally.
>
> In Solaris 2.6, it gives
> Segmentation violation (core dumped)
>
> In Digital Unix 4.0D, it gives
> Resources Lost (core dumped)
>
> However, if I compile the program with g++, it works fine. At present, I
> have compiled g++ only for Digital. So I was able to run it only on
> Digital.

I really don't know what the g++ generated code and runtime library do for
exceptions on Digital UNIX. If they do anything other than using native
(libexc) exceptions, then they won't run when the thread is cancelled, and
therefore wouldn't trigger any bugs in the destructors or exception code
(due to dangling references to the object that you've destroyed). The same
may well be true for Solaris.

As other replies have already said, the interactions of C++ and POSIX are
undefined, because neither formally recognizes the existance of the other.
Due to the close similarity between C++ and C, you're usually OK, as long
as you stay away from the non-C aspects of C++, like exceptions and
objects. There is no standard anywhere even suggesting what
POSIX cancellation of a C++ program will do. (Although many
implementations, including both those you mention, make this generally
work.)

As I said, another area of danger is objects. You cannot
reliably/portably/correctly pass the name of a method and "this" to
pthread_create and expect the thread to run correctly, even with the
typecasting you've used. Again, this works on some implementations. That's
sheer accident. You should create a "glue" routine that's extern "C" and a
static class function rather than an object method. Pass "this" to it, and
have it cast this to the appropriate type and call the desired method.

butenhof.vcf
0 new messages