From 8e59c7f5923d3dc5e307124f39868ee870b1be9c Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Fri, 11 Jan 2013 15:00:43 +0000 Subject: Import b9db3a010143160624f123763025ab544b69bd9a of libcxxrt. This brings in three fixes: - Don't treat pointers to members as pointers in catch blocks (they're usually fat pointers). - Correctly catch foreign exceptions in catchalls. - Ensure that a happens-before relationship is established when setting terminate handlers in one thread and calling them in another. --- atomic.h | 29 ++++++++++++++++++++ exception.cc | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- memory.cc | 33 ++++++++++++++--------- typeinfo.h | 18 ++++++------- 4 files changed, 132 insertions(+), 34 deletions(-) create mode 100644 atomic.h diff --git a/atomic.h b/atomic.h new file mode 100644 index 000000000000..bcd8a47ac89d --- /dev/null +++ b/atomic.h @@ -0,0 +1,29 @@ + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +/** + * Swap macro that enforces a happens-before relationship with a corresponding + * ATOMIC_LOAD. + */ +#if __has_feature(cxx_atomic) +#define ATOMIC_SWAP(addr, val)\ + __atomic_exchange(addr, val, __ATOMIC_ACQ_REL) +#elif __has_builtin(__sync_swap) +#define ATOMIC_SWAP(addr, val)\ + __sync_swap(addr, val) +#else +#define ATOMIC_SWAP(addr, val)\ + __sync_lock_test_and_set(addr, val) +#endif + +#if __has_feature(cxx_atomic) +#define ATOMIC_LOAD(addr)\ + __atomic_load(addr, __ATOMIC_ACQUIRE) +#else +#define ATOMIC_LOAD(addr)\ + (__sync_synchronize(), *addr) +#endif diff --git a/exception.cc b/exception.cc index fe56297f9dd5..0cb2535f8fd1 100644 --- a/exception.cc +++ b/exception.cc @@ -32,6 +32,7 @@ #include #include "typeinfo.h" #include "dwarf_eh.h" +#include "atomic.h" #include "cxxabi.h" #pragma weak pthread_key_create @@ -154,6 +155,17 @@ struct __cxa_thread_info * The exception currently running in a cleanup. */ _Unwind_Exception *currentCleanup; + /** + * Our state with respect to foreign exceptions. Usually none, set to + * caught if we have just caught an exception and rethrown if we are + * rethrowing it. + */ + enum + { + none, + caught, + rethrown + } foreign_exception_state; /** * The public part of this structure, accessible from outside of this * module. @@ -308,7 +320,16 @@ static void thread_cleanup(void* thread_info) __cxa_thread_info *info = (__cxa_thread_info*)thread_info; if (info->globals.caughtExceptions) { - free_exception_list(info->globals.caughtExceptions); + // If this is a foreign exception, ask it to clean itself up. + if (info->foreign_exception_state != __cxa_thread_info::none) + { + _Unwind_Exception *e = (_Unwind_Exception*)info->globals.caughtExceptions; + e->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT, e); + } + else + { + free_exception_list(info->globals.caughtExceptions); + } } free(thread_info); } @@ -780,7 +801,8 @@ extern "C" void __cxa_decrement_exception_refcount(void* thrown_exception) */ extern "C" void __cxa_rethrow() { - __cxa_eh_globals *globals = __cxa_get_globals(); + __cxa_thread_info *ti = thread_info_fast(); + __cxa_eh_globals *globals = &ti->globals; // Note: We don't remove this from the caught list here, because // __cxa_end_catch will be called when we unwind out of the try block. We // could probably make this faster by providing an alternative rethrow @@ -795,6 +817,15 @@ extern "C" void __cxa_rethrow() std::terminate(); } + if (ti->foreign_exception_state != __cxa_thread_info::none) + { + ti->foreign_exception_state = __cxa_thread_info::rethrown; + _Unwind_Exception *e = (_Unwind_Exception*)ex; + _Unwind_Reason_Code err = _Unwind_Resume_or_Rethrow(e); + report_failure(err, ex); + return; + } + assert(ex->handlerCount > 0 && "Rethrowing uncaught exception!"); // ex->handlerCount will be decremented in __cxa_end_catch in enclosing @@ -848,9 +879,9 @@ static bool check_type_signature(__cxa_exception *ex, void *&adjustedPtr) { void *exception_ptr = (void*)(ex+1); - const std::type_info *ex_type = ex->exceptionType; + const std::type_info *ex_type = ex ? ex->exceptionType : 0; - bool is_ptr = ex_type->__is_pointer_p(); + bool is_ptr = ex ? ex_type->__is_pointer_p() : false; if (is_ptr) { exception_ptr = *(void**)exception_ptr; @@ -911,8 +942,8 @@ static handler_type check_action_record(_Unwind_Context *context, action_record = displacement ? action_record_offset_base + displacement : 0; // We only check handler types for C++ exceptions - foreign exceptions - // are only allowed for cleanup. - if (filter > 0 && 0 != ex) + // are only allowed for cleanups and catchalls. + if (filter > 0) { std::type_info *handler_type = get_type_info_entry(context, lsda, filter); if (check_type_signature(ex, handler_type, adjustedPtr)) @@ -1133,8 +1164,10 @@ extern "C" void *__cxa_begin_catch(void *e) throw() extern "C" void *__cxa_begin_catch(void *e) #endif { - // Decrement the uncaught exceptions count - __cxa_eh_globals *globals = __cxa_get_globals(); + // We can't call the fast version here, because if the first exception that + // we see is a foreign exception then we won't have called it yet. + __cxa_thread_info *ti = thread_info(); + __cxa_eh_globals *globals = &ti->globals; globals->uncaughtExceptions--; _Unwind_Exception *exceptionObject = (_Unwind_Exception*)e; @@ -1177,9 +1210,22 @@ extern "C" void *__cxa_begin_catch(void *e) { ex->handlerCount++; } + ti->foreign_exception_state = __cxa_thread_info::none; return ex->adjustedPtr; } + else + { + // If this is a foreign exception, then we need to be able to + // store it. We can't chain foreign exceptions, so we give up + // if there are already some outstanding ones. + if (globals->caughtExceptions != 0) + { + std::terminate(); + } + globals->caughtExceptions = (__cxa_exception*)exceptionObject; + ti->foreign_exception_state = __cxa_thread_info::caught; + } // exceptionObject is the pointer to the _Unwind_Exception within the // __cxa_exception. The throw object is after this return ((char*)exceptionObject + sizeof(_Unwind_Exception)); @@ -1195,10 +1241,23 @@ extern "C" void __cxa_end_catch() { // We can call the fast version here because the slow version is called in // __cxa_throw(), which must have been called before we end a catch block - __cxa_eh_globals *globals = __cxa_get_globals_fast(); + __cxa_thread_info *ti = thread_info_fast(); + __cxa_eh_globals *globals = &ti->globals; __cxa_exception *ex = globals->caughtExceptions; assert(0 != ex && "Ending catch when no exception is on the stack!"); + + if (ti->foreign_exception_state != __cxa_thread_info::none) + { + globals->caughtExceptions = 0; + if (ti->foreign_exception_state != __cxa_thread_info::rethrown) + { + _Unwind_Exception *e = (_Unwind_Exception*)ti->globals.caughtExceptions; + e->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT, e); + } + ti->foreign_exception_state = __cxa_thread_info::none; + return; + } bool deleteException = true; @@ -1328,7 +1387,7 @@ namespace std { if (thread_local_handlers) { return pathscale::set_unexpected(f); } - return __sync_lock_test_and_set(&unexpectedHandler, f); + return ATOMIC_SWAP(&terminateHandler, f); } /** * Sets the function that is called to terminate the program. @@ -1336,7 +1395,8 @@ namespace std terminate_handler set_terminate(terminate_handler f) throw() { if (thread_local_handlers) { return pathscale::set_terminate(f); } - return __sync_lock_test_and_set(&terminateHandler, f); + + return ATOMIC_SWAP(&terminateHandler, f); } /** * Terminates the program, calling a custom terminate implementation if @@ -1390,7 +1450,7 @@ namespace std { return info->unexpectedHandler; } - return unexpectedHandler; + return ATOMIC_LOAD(&unexpectedHandler); } /** * Returns the current terminate handler. @@ -1402,7 +1462,7 @@ namespace std { return info->terminateHandler; } - return terminateHandler; + return ATOMIC_LOAD(&terminateHandler); } } #ifdef __arm__ diff --git a/memory.cc b/memory.cc index bd7fd22ae5fd..cc879e0d0d06 100644 --- a/memory.cc +++ b/memory.cc @@ -36,14 +36,8 @@ #include #include #include "stdexcept.h" +#include "atomic.h" -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#if !__has_builtin(__sync_swap) -#define __sync_swap __sync_lock_test_and_set -#endif namespace std { @@ -67,7 +61,12 @@ namespace std __attribute__((weak)) new_handler set_new_handler(new_handler handler) { - return __sync_swap(&new_handl, handler); + return ATOMIC_SWAP(&new_handl, handler); + } + __attribute__((weak)) + new_handler get_new_handler(void) + { + return ATOMIC_LOAD(&new_handl); } } @@ -75,12 +74,17 @@ namespace std __attribute__((weak)) void* operator new(size_t size) { + if (0 == size) + { + size = 1; + } void * mem = malloc(size); while (0 == mem) { - if (0 != new_handl) + new_handler h = std::get_new_handler(); + if (0 != h) { - new_handl(); + h(); } else { @@ -95,14 +99,19 @@ void* operator new(size_t size) __attribute__((weak)) void* operator new(size_t size, const std::nothrow_t &) throw() { + if (0 == size) + { + size = 1; + } void *mem = malloc(size); while (0 == mem) { - if (0 != new_handl) + new_handler h = std::get_new_handler(); + if (0 != h) { try { - new_handl(); + h(); } catch (...) { diff --git a/typeinfo.h b/typeinfo.h index 82239778ec5b..a8a1a980e926 100644 --- a/typeinfo.h +++ b/typeinfo.h @@ -69,6 +69,14 @@ namespace std * type_info and correctly fill in the values in the vtables. */ public: + /** + * Returns true if this is some pointer type, false otherwise. + */ + virtual bool __is_pointer_p() const { return false; } + /** + * Returns true if this is some function type, false otherwise. + */ + virtual bool __is_function_p() const { return false; } /** * Catch function. Allows external libraries to implement * their own basic types. This is used, for example, in the @@ -95,14 +103,6 @@ namespace std { return false; } - /** - * Returns true if this is some pointer type, false otherwise. - */ - virtual bool __is_pointer_p() const { return false; } - /** - * Returns true if this is some function type, false otherwise. - */ - virtual bool __is_function_p() const { return false; } }; } @@ -284,7 +284,6 @@ namespace ABI_NAMESPACE /** Pointer is a pointer to a member of an incomplete class. */ __incomplete_class_mask = 0x10 }; - virtual bool __is_pointer_p() const { return true; } virtual bool __do_catch(const type_info *thrown_type, void **thrown_object, unsigned outer) const; @@ -296,6 +295,7 @@ namespace ABI_NAMESPACE struct __pointer_type_info : public __pbase_type_info { virtual ~__pointer_type_info(); + virtual bool __is_pointer_p() const { return true; } }; /** -- cgit v1.2.3