#pragma once #include "../common/noncopyable.hpp" #include "../common/errors.hpp" #include <stdint.h> #include <functional> #include <stdlib.h> namespace sprawl { namespace threading { class Thread; class Handle; void RunThread(Thread* thread); enum class ThreadDestructionBehavior { Default, Join, Detach, Abort, }; } namespace this_thread { threading::Handle GetHandle(); void Sleep(uint64_t nanoseconds); void SleepUntil(uint64_t nanosecondTimestamp); void Yield(); } } #ifdef _WIN32 # include "thread_windows.hpp" # undef Yield #else # include "thread_linux.hpp" #endif class sprawl::threading::Thread : public sprawl::noncopyable { public: static void SetDefaultDestructionBehavior(ThreadDestructionBehavior behavior); void SetDestructionBehavior(ThreadDestructionBehavior behavior); template<typename Function, typename... Args> Thread(Function&& f, Args&&... args); template<typename Function, typename... Args> Thread(char const* const threadName, Function&& f, Args&&... args); template<typename Function> Thread(Function&& f); template<typename Function> Thread(char const* const threadName, Function&& f); ~Thread(); bool Joinable() { return m_handle != Handle(); } Handle const& GetHandle() { return m_handle; } void Join(); void Detach(); void Start(); void SetExceptionHandler(); #if SPRAWL_EXCEPTIONS_ENABLED bool HasException() { return bool(m_exception); } #else bool HasException() { return false; } #endif protected: void PlatformJoin(); void PlatformDetach(); friend void RunThread(Thread* thread); char const* const m_threadName; std::function<void()> m_function; Handle m_handle; ThreadDestructionBehavior m_destructionBehavior; #if SPRAWL_EXCEPTIONS_ENABLED bool m_handleExceptions; std::exception_ptr m_exception; #endif static ThreadDestructionBehavior ms_defaultDestructionBehavior; private: Thread(Thread&& other); Thread(Thread const& other); Thread& operator=(Thread&& other); Thread& operator=(Thread const& other); }; template<typename Function, typename... Args> sprawl::threading::Thread::Thread(Function&& f, Args&&... args) : m_threadName(nullptr) , m_function(std::bind(f, args...)) , m_handle() , m_destructionBehavior(ThreadDestructionBehavior::Default) #if SPRAWL_EXCEPTIONS_ENABLED , m_handleExceptions(false) , m_exception() #endif { // } template<typename Function, typename... Args> sprawl::threading::Thread::Thread(char const* const threadName, Function&& f, Args&&... args) : m_threadName(threadName) , m_function(std::bind(f, args...)) , m_handle() , m_destructionBehavior(ThreadDestructionBehavior::Default) #if SPRAWL_EXCEPTIONS_ENABLED , m_handleExceptions(false) , m_exception() #endif { // } template<typename Function> sprawl::threading::Thread::Thread(Function&& f) : m_threadName(nullptr) , m_function(f) , m_handle() , m_destructionBehavior(ThreadDestructionBehavior::Default) #if SPRAWL_EXCEPTIONS_ENABLED , m_handleExceptions(false) , m_exception() #endif { } template<typename Function> sprawl::threading::Thread::Thread(char const* const threadName, Function&& f) : m_threadName(threadName) , m_function(f) , m_handle() , m_destructionBehavior(ThreadDestructionBehavior::Default) #if SPRAWL_EXCEPTIONS_ENABLED , m_handleExceptions(false) , m_exception() #endif { } inline void sprawl::threading::Thread::Join() { if(!Joinable()) { std::terminate(); } PlatformJoin(); m_handle = Handle(); #if SPRAWL_EXCEPTIONS_ENABLED if(m_exception) { std::rethrow_exception(m_exception); } #endif } inline void sprawl::threading::Thread::Detach() { if(!Joinable()) { std::terminate(); } #if SPRAWL_EXCEPTIONS_ENABLED m_handleExceptions = false; #endif PlatformDetach(); m_handle = Handle(); #if SPRAWL_EXCEPTIONS_ENABLED if(m_exception) { std::rethrow_exception(m_exception); } #endif } inline void sprawl::threading::RunThread(Thread* thread) { #if SPRAWL_EXCEPTIONS_ENABLED try { thread->m_function(); } catch(...) { if(thread->m_handleExceptions) { thread->m_exception = std::current_exception(); } else { throw; } } #else thread->m_function(); #endif } inline /*static*/ void sprawl::threading::Thread::SetDefaultDestructionBehavior(ThreadDestructionBehavior behavior) { if(behavior == ThreadDestructionBehavior::Default) { behavior = ThreadDestructionBehavior::Abort; } ms_defaultDestructionBehavior = behavior; } inline void sprawl::threading::Thread::SetDestructionBehavior(ThreadDestructionBehavior behavior) { m_destructionBehavior = behavior; } inline void sprawl::threading::Thread::SetExceptionHandler() { #if SPRAWL_EXCEPTIONS_ENABLED m_handleExceptions = true; #endif }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 16768 | ShadauxCat |
Improvements to error handling in builds with exceptions disabled: - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if Get() is called when an error flag is set. (In release buils or with SPRAWL_ERRORSTATE_PERMISSIVE defined, Get() will return junk memory in this case.) - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if its destructor is called without checking the errorstate if an error is present (equivalent to an exception terminating the application if no catch() block is present for it). - On linux builds and when running "Analyze" through visual studio, a warning will be issued if any function returning ErrorState has its return value ignored. (This only applies to builds with exceptions not enabled; when exceptions are enabled no warning is issued) - Many functions that could return ErrorState were having their return values silently ignored in internal sprawl code so the user would not find out about errors if exceptions are disabled; now anything in sprawl code that calls a function returning ErrorState will either handle the error, or (in most cases) surface it back up to the user. - As a positive side-effect of the warnings for ignoring ErrorState, several constructors that were capable of throwing exceptions are no longer capable of doing so. #review-16769 |
||
#4 | 16131 | ShadauxCat |
- Exposed FILE* object in sprawl::filesystem::File - Added ability to specify flush behavior for custom handlers via std::function (interesting note - apparently with optimization enabled, calls to std::function can execute faster than virtual function calls) - Threads that destruct with no Join() after exiting with an uncaught exception will terminate with an error message rather than swallowing the exception and letting it disappear #review-16132 |
||
#3 | 16111 | ShadauxCat |
- Threads can now optionally catch exceptions to be rethrown on another thread when it calls Join(). The exception state of a thread can also be queried via HasException() - if this returns true, the thread exited due to an uncaught exception that will be rethrown on Join(). - The behavior of the thread destructor can now be manipulated via SetDestructionBehavior(). By default, a thread whose destructor is called without Join() being called on it will call std::terminate(). But that behavior can be changed such that the destructor can either call Join() automatically, or detach the thread, if it destructs without Join() being called. (If it calls Join() on a thread that caught an exception, the exception will NOT be rethrown, it will be lost to the aether. Throwing exceptions in a destructor is bad, but I'm on the fence whether this should just throw those exceptions away or call std::terminate() in that case. Open for input.) #review-16112 |
||
#2 | 13650 | ShadauxCat |
- Windows implementations of thread and time libraries - Added coroutines - Added some more unit tests, fixed some unit tests in windows environments - Fixed an issue where multi threading was not properly detected on Linux - Fixed the makefiles to build with threading by default on linux - Changed the pool allocator to use thread-local pools instead of locking mutexes - Fixed output of sprawl::string in the StringBuilder library to take length into account - Added string builder options for StringLiteral - Added thread local implementation #review |
||
#1 | 12508 | ShadauxCat |
-Added threading library. Currently only functional for Linux; Windows will fail to link. (I will fix this soon.) -Fixed missing move and copy constructors in List and ForwardList -Fixed broken move constructor in HashMap -Fixed missing const get() in HashMap -Fixed broken operator-> in ListIterator -Added sprawl::noncopyable -Added sketch headers for filesystem library -Made StringLiteral hashable, added special hashes for pointers and integers in murmur3 -Fixed compiler warning in async_network -Updated memory allocators to use new threading library for mutexes -Added accessibility to sprawl::StringLiteral to be able toa ccess its pointer and length and perform pointer comparisons #review-12504 |