This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Cancellation while throwing exception


I was just reading a document that claims C++ code should never throw exceptions in destructors, because Bad Things could happen if one is throwing while unwinding the stack due to another exception - you can't have two simultaneous active exceptions, so either std::unexpected() gets called, or maybe std::terminate() directly (don't remember exactly).

It occurred to me that under NPTL, this is essentially the same as cancellation while throwing an exception. The attached test program shows that indeed std::terminate() gets called.

Maybe cancellation should be disabled for the duration of exception throwing?

Thanks,
Scott Lamb
/*
 * $Id: test_cancellation_while_throwing.cc 544 2004-01-25 00:02:35Z slamb $
 * Scott Lamb <slamb@slamb.org>
 *
 * Tests cancellation while throwing a C++ exception. This is likely to
 * present problems for systems which implement cancellation as an exception,
 * since only one exception can be active at once.
 *
 * Some test results:
 *
 * - Mac OS X Panther 10.3.2:
 *   Returns success. This is expected, since OS X does not implement
 *   cancellation as an exception.
 *
 * - HP Tru64 UNIX 5.1B:
 *   Resources lost(coredump)
 *
 * - Fedora Core 1, glibc 2.3.3-3, no LD_ASSUME_KERNEL:
 *   terminate called without an active exception
 *   Aborted
 *
 * - Fedora Core 1, glibc 2.3.3-3, LD_ASSUME_KERNEL=2.4.1:
 *   Returns success. Again, expected; cancellation is not an exception.
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <exception>

enum ErrorReturnType {
    DIRECT,
    ERRNO
};

void error_wrap(int retval, const char *funcname, ErrorReturnType type) {
    if (type == ERRNO && retval < 0) {
        fprintf(stderr, "%s returned %d (%s)\n",
                funcname, errno, strerror(errno));
        abort();
    } else if (type == DIRECT && retval != 0) {
        fprintf(stderr, "%s returned %d (%s)\n",
                funcname, retval, strerror(retval));
        abort();
    }
}

class CancelInDestructor {
public:
    CancelInDestructor() {}

    ~CancelInDestructor() {
        error_wrap(pthread_cancel(pthread_self()), "pthread_cancel", DIRECT);
        pthread_testcancel();
    }
};

void* cancel_thread_main(void*) {
    try {
        CancelInDestructor cid;
        throw 1;
    } catch (int&) {}
    return NULL;
}

int main() {
    pthread_t cancel_thread;

#if __GNUC__ >= 3
    std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
#endif

    error_wrap(pthread_create(&cancel_thread, NULL, &cancel_thread_main, NULL),
               "pthread_create", DIRECT);
    error_wrap(pthread_join(cancel_thread, NULL), "pthread_join", DIRECT);
    return 0;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]