diff --git a/miosix/config/miosix_settings.h b/miosix/config/miosix_settings.h index e48e39e79e57ce6e4521617512f5a3530a94f0b8..4a55c651ba2ba60a07c5d58fd5e6f4ff81222499 100644 --- a/miosix/config/miosix_settings.h +++ b/miosix/config/miosix_settings.h @@ -251,6 +251,23 @@ const unsigned char MAIN_PRIORITY=1; const unsigned int MAX_TIME_SLICE=1000000; #endif //SCHED_TYPE_PRIORITY +/// pthread_exit() is a dangerous function. To understand why, let's first +/// discuss how Linux implements it. On the surface, it looks like it neatly +/// works with C++ as it is implemented by throwing a special ForcedUnwind +/// exception thus calling C++ destructors. However, implementers left a number +/// of unhandled corner cases all leading to unexpected program termination. +/// These are: calling pthread_exit() from code that directly or indirectly... +/// 1) contains a catch(...) that does not rethrow? std::abort() gets called +/// 2) contains a function declared noexcept? std::abort() gets called +/// 3) is called from a destructor? you guessed it, std::abort() again! +/// For this reason pthread_exit() support is by default disabled in Miosix and +/// attempting to call this function is met with a linking error. If you really +/// need it, uncomment the line below. Oh, right, how does Miosix implement it? +/// Throwing a standard C++ exception, so a catch(...) can stop a pthread_exit() +/// which is imho neither better nor worse than Linux. +/// Reference: https://udrepper.livejournal.com/21541.html +//#define WITH_PTHREAD_EXIT + /// Enable support for pthread_key_create/pthread_key_delete/pthread_getspecific //#define WITH_PTHREAD_KEYS diff --git a/miosix/kernel/boot.cpp b/miosix/kernel/boot.cpp index b1646f45ec6bdbaef5345918854abc57b896b83e..48156d30f86ba0fb1379e0ee511736cc0561c4ff 100644 --- a/miosix/kernel/boot.cpp +++ b/miosix/kernel/boot.cpp @@ -38,6 +38,7 @@ #include "filesystem/file_access.h" #include "error.h" #include "logging.h" +#include "pthread_private.h" // settings for miosix #include "config/miosix_settings.h" #include "util/util.h" @@ -150,11 +151,15 @@ void *mainLoader(void *argv) #else //__NO_EXCEPTIONS try { main(0,NULL); + #ifdef WITH_PTHREAD_EXIT + } catch(PthreadExitException&) { + errorLog("***Attempting to pthread_exit from main\n"); + #endif //WITH_PTHREAD_EXIT } catch(std::exception& e) { - errorLog("***An exception propagated through a thread\n"); + errorLog("***An exception propagated through main\n"); errorLog("what():%s\n",e.what()); } catch(...) { - errorLog("***An exception propagated through a thread\n"); + errorLog("***An exception propagated through main\n"); } #endif //__NO_EXCEPTIONS diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index 204f93b5142b3bfe66710460a9165218bb069752..235b241dedc7f71270d22e45c1c094bd002be22d 100755 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -830,6 +830,10 @@ void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) #else //__NO_EXCEPTIONS try { result=threadfunc(argv); + #ifdef WITH_PTHREAD_EXIT + } catch(PthreadExitException& e) { + result=e.getReturnValue(); + #endif //WITH_PTHREAD_EXIT } catch(std::exception& e) { errorLog("***An exception propagated through a thread\n"); errorLog("what():%s\n",e.what()); diff --git a/miosix/kernel/pthread.cpp b/miosix/kernel/pthread.cpp index 218a7b36f530eab0462fdbb716885c7f91a343d0..f87f19f22f5ede8257bb81d068dc2a8bdc62aca1 100644 --- a/miosix/kernel/pthread.cpp +++ b/miosix/kernel/pthread.cpp @@ -390,6 +390,15 @@ int pthread_once(pthread_once_t *once, void (*func)()) int pthread_setcancelstate(int state, int *oldstate) { return 0; } //Stub +#ifdef WITH_PTHREAD_EXIT + +void pthread_exit(void *returnValue) +{ + throw PthreadExitException(returnValue); +} + +#endif //WITH_PTHREAD_EXIT + #ifdef WITH_PTHREAD_KEYS typedef void (*destructor_type)(void *); diff --git a/miosix/kernel/pthread_private.h b/miosix/kernel/pthread_private.h index bed3f28811205199a6bfe1cf32e9b90391a068da..d49b7332c0013fb813c51dd6879d19bbc12e5182 100644 --- a/miosix/kernel/pthread_private.h +++ b/miosix/kernel/pthread_private.h @@ -167,6 +167,35 @@ static inline unsigned int IRQdoMutexUnlockAllDepthLevels(pthread_mutex_t *mutex return result; } +#ifdef WITH_PTHREAD_EXIT + +/** + * Exception type thrown when pthread_exit is called. + * + * NOTE: This type should not derive from std::exception on purpose, as it would + * be bad if a \code catch(std::exception& e) \endcode would match this + * exception. + */ +class PthreadExitException +{ +public: + /** + * Constructor + * \param returnValue thread return value passed to pthread_exit + */ + PthreadExitException(void *returnValue) : returnValue(returnValue) {} + + /** + * \return the thread return value passed to pthread_exit + */ + void *getReturnValue() const { return returnValue; } + +private: + void *returnValue; +}; + +#endif //WITH_PTHREAD_EXIT + #ifdef WITH_PTHREAD_KEYS /** * Called at thread exit to call destructors for pthread keys