From f51192b14587846b461e8bc21607b25460d310e4 Mon Sep 17 00:00:00 2001 From: lihuiba Date: Sun, 27 Oct 2024 17:02:55 +0800 Subject: [PATCH 1/2] switch exception state during context switching --- thread/test/test.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ thread/thread.cpp | 21 +++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/thread/test/test.cpp b/thread/test/test.cpp index b1edc3e6..d9d8b767 100644 --- a/thread/test/test.cpp +++ b/thread/test/test.cpp @@ -21,6 +21,7 @@ limitations under the License. #include #include #include +#include #include #include #include "../../test/gtest.h" @@ -404,6 +405,49 @@ TEST(Perf, ThreadSwitchWithStandaloneTSUpdater) return; } +TEST(exception, switch) +{ + class Foo { + public: + ~Foo() { + LOG_INFO("before thread_yield():", VALUE(std::uncaught_exceptions())); + photon::thread_yield(); + LOG_INFO("after thread_yield():", VALUE(std::uncaught_exceptions())); + } + }; + + class Bar { + public: + ~Bar() { + try { + Foo foo; + throw "asdf"; + } catch(...) { } + } + }; + + bool quit = false; + auto th = photon::thread_create11([&](){ + while(!quit) { + LOG_INFO(VALUE(std::uncaught_exceptions())); + EXPECT_EQ(std::uncaught_exceptions(), 0); + try { + throw 3.1415926f; + } catch(...) { } + photon::thread_yield(); + } + }); + thread_enable_join(th); + try { + Foo foo; + Bar bar; + throw 123; + } catch(...) { } + + quit = true; + thread_join((join_handle*)th); +} + thread_local int shot_count; thread_local photon::condition_variable shot_cond; uint64_t on_timer_asdf(void* arg) diff --git a/thread/thread.cpp b/thread/thread.cpp index f3595163..58c49ebf 100644 --- a/thread/thread.cpp +++ b/thread/thread.cpp @@ -649,6 +649,20 @@ namespace photon } static void _photon_thread_die(thread* th) asm("_photon_thread_die"); + struct __cxa_eh_globals + { + void * caughtExceptions; + unsigned int uncaughtExceptions; + }; + + extern "C" __cxa_eh_globals* __cxa_get_globals (void); + + static void* cxa_get_globals() asm("cxa_get_globals"); + + static __attribute__((used)) + void* cxa_get_globals() { + return __cxa_get_globals(); + } #if defined(__x86_64__) #if !defined(_WIN64) @@ -812,6 +826,11 @@ R"( DEF_ASM_FUNC(_photon_thread_stub) R"( + sub sp, sp, #16 + bl cxa_get_globals + add sp, sp, #16 + str xzr, [x0, #0x0] + str wzr, [x0, #0x8] ldp x0, x1, [x29, #0x40] //; load arg, start into x0, x1 str xzr, [x29, #0x40] //; set arg as 0 blr x1 //; start(x0) @@ -828,6 +847,7 @@ R"( inline void switch_context(thread* from, thread* to) { prepare_switch(from, to); + auto exception_state = *__cxa_get_globals(); auto _t_ = to->stack.pointer_ref(); register auto f asm("x0") = from->stack.pointer_ref(); register auto t asm("x1") = _t_; @@ -842,6 +862,7 @@ R"( // Corouptable register, may change ,should save "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18"); + *__cxa_get_globals() = exception_state; } inline void switch_context_defer(thread* from, thread* to, From cedd27ad5570951ef5039cefa805f457808fc783 Mon Sep 17 00:00:00 2001 From: lihuiba Date: Mon, 28 Oct 2024 10:34:36 +0800 Subject: [PATCH 2/2] change the design to hooking of __cxa_get_globals(), so that we don't need to switch exception state --- thread/test/test.cpp | 16 +++++++++++---- thread/thread.cpp | 46 ++++++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/thread/test/test.cpp b/thread/test/test.cpp index d9d8b767..f85cfb00 100644 --- a/thread/test/test.cpp +++ b/thread/test/test.cpp @@ -405,14 +405,22 @@ TEST(Perf, ThreadSwitchWithStandaloneTSUpdater) return; } +inline int std_uncaught_exceptions() { +#if __cplusplus > 201700L + return std::uncaught_exceptions(); +#else + return std::uncaught_exception(); +#endif +} + TEST(exception, switch) { class Foo { public: ~Foo() { - LOG_INFO("before thread_yield():", VALUE(std::uncaught_exceptions())); + LOG_INFO("before thread_yield():", VALUE(std_uncaught_exceptions())); photon::thread_yield(); - LOG_INFO("after thread_yield():", VALUE(std::uncaught_exceptions())); + LOG_INFO("after thread_yield():", VALUE(std_uncaught_exceptions())); } }; @@ -429,8 +437,8 @@ TEST(exception, switch) bool quit = false; auto th = photon::thread_create11([&](){ while(!quit) { - LOG_INFO(VALUE(std::uncaught_exceptions())); - EXPECT_EQ(std::uncaught_exceptions(), 0); + LOG_INFO(VALUE(std_uncaught_exceptions())); + EXPECT_EQ(std_uncaught_exceptions(), 0); try { throw 3.1415926f; } catch(...) { } diff --git a/thread/thread.cpp b/thread/thread.cpp index 58c49ebf..8934d28d 100644 --- a/thread/thread.cpp +++ b/thread/thread.cpp @@ -31,6 +31,7 @@ limitations under the License. #include #include #include +#include #ifdef _WIN64 #include @@ -197,6 +198,7 @@ namespace photon size_t stack_size; // offset 96B condition_variable cond; /* used for join */ + uint64_t __gh_globals[2] = {0}; enum shift { joinable = 0, @@ -649,20 +651,33 @@ namespace photon } static void _photon_thread_die(thread* th) asm("_photon_thread_die"); - struct __cxa_eh_globals - { - void * caughtExceptions; - unsigned int uncaughtExceptions; + struct __cxa_eh_globals { + void* caughtExceptions; + unsigned int uncaughtExceptions; }; - - extern "C" __cxa_eh_globals* __cxa_get_globals (void); - - static void* cxa_get_globals() asm("cxa_get_globals"); - - static __attribute__((used)) - void* cxa_get_globals() { - return __cxa_get_globals(); + inline void install_hook(void* target, void* hook) { + auto page = (uint64_t)target & ~4095UL; + int ret = mprotect((void*)page, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(ret == 0); (void)ret; + *(uint16_t*)((char*)target + 0) = 0xb848; // movabs imm64 to rax + *(uint64_t*)((char*)target + 2) = (uint64_t)hook; + *(uint16_t*)((char*)target + 10) = 0xe0ff; // jmp *rax } + inline void* install_hook(const char* sym, void* hook) { + auto addr = dlsym(RTLD_DEFAULT, sym); + assert(addr); + install_hook(addr, hook); + return addr; + } + static __cxa_eh_globals* cxa_get_globals (void) { + thread_local __cxa_eh_globals eh = {0}; + return !CURRENT ? &eh : (__cxa_eh_globals*) CURRENT->__gh_globals; + } + __attribute__((constructor)) + static void install_eh_globals_hook() { + install_hook("__cxa_get_globals", (void*)&cxa_get_globals); + } + #if defined(__x86_64__) #if !defined(_WIN64) @@ -826,11 +841,6 @@ R"( DEF_ASM_FUNC(_photon_thread_stub) R"( - sub sp, sp, #16 - bl cxa_get_globals - add sp, sp, #16 - str xzr, [x0, #0x0] - str wzr, [x0, #0x8] ldp x0, x1, [x29, #0x40] //; load arg, start into x0, x1 str xzr, [x29, #0x40] //; set arg as 0 blr x1 //; start(x0) @@ -847,7 +857,6 @@ R"( inline void switch_context(thread* from, thread* to) { prepare_switch(from, to); - auto exception_state = *__cxa_get_globals(); auto _t_ = to->stack.pointer_ref(); register auto f asm("x0") = from->stack.pointer_ref(); register auto t asm("x1") = _t_; @@ -862,7 +871,6 @@ R"( // Corouptable register, may change ,should save "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18"); - *__cxa_get_globals() = exception_state; } inline void switch_context_defer(thread* from, thread* to,