From d6a79cbb5fff6f00dc595cd92de643febb6259ff Mon Sep 17 00:00:00 2001 From: Sainan Date: Thu, 19 Dec 2024 12:16:34 +0100 Subject: [PATCH] Add Thread::detach --- soup/SelfDeletingThread.cpp | 3 +- soup/Thread.cpp | 102 ++++++++++++++++++++---------------- soup/Thread.hpp | 22 +++++--- soup/TransientToken.hpp | 54 +++++++++++++------ soup/WeakRef.hpp | 2 +- 5 files changed, 114 insertions(+), 69 deletions(-) diff --git a/soup/SelfDeletingThread.cpp b/soup/SelfDeletingThread.cpp index 071466f8..1b536102 100644 --- a/soup/SelfDeletingThread.cpp +++ b/soup/SelfDeletingThread.cpp @@ -24,7 +24,8 @@ NAMESPACE_SOUP msg.append(e.what()); logWriteLine(std::move(msg)); } - t->is_self_deleting = true; + t->detach(); + delete t; } } diff --git a/soup/Thread.cpp b/soup/Thread.cpp index 536e69fa..af4692f6 100644 --- a/soup/Thread.cpp +++ b/soup/Thread.cpp @@ -18,84 +18,62 @@ NAMESPACE_SOUP #endif threadCreateCallback(void* handover) { - auto t = reinterpret_cast(handover); - t->f(std::move(t->f_cap)); - t->f_cap.reset(); - const bool is_self_deleting = t->is_self_deleting; - t->running = false; - if (is_self_deleting) - { -#if SOUP_WINDOWS - CloseHandle(t->handle); - t->handle = INVALID_HANDLE_VALUE; -#else - pthread_detach(t->handle); - t->have_handle = false; -#endif - delete static_cast(t); - } + auto data = reinterpret_cast(handover); + data->f(std::move(data->f_cap)); + data->f_cap.reset(); + delete data; } void Thread::start(void(*f)(Capture&&), Capture&& cap) { SOUP_ASSERT(!isRunning()); - this->f = f; - this->f_cap = std::move(cap); + // If we still have a handle, relinquish it. + detach(); -#if SOUP_WINDOWS - // if we still have a handle, relinquish it - if (handle != INVALID_HANDLE_VALUE) - { - CloseHandle(handle); - } + auto data = new RunningData{ f, std::move(cap) }; + this->running_data = WeakRef(data); - handle = CreateThread(nullptr, 0, reinterpret_cast(&threadCreateCallback), this, 0, nullptr); +#if SOUP_WINDOWS + handle = CreateThread(nullptr, 0, reinterpret_cast(&threadCreateCallback), data, 0, nullptr); SOUP_IF_UNLIKELY (handle == NULL) { handle = INVALID_HANDLE_VALUE; + this->running_data.reset(); SOUP_THROW(Exception(format("Failed to create thread: {}", GetLastError()))); } #else - // if we still have a handle, relinquish it - awaitCompletion(); - pthread_attr_t attr; pthread_attr_init(&attr); - auto ret = pthread_create(&handle, &attr, reinterpret_cast(&threadCreateCallback), this); + auto ret = pthread_create(&handle, &attr, reinterpret_cast(&threadCreateCallback), data); SOUP_IF_UNLIKELY (ret != 0) { + this->running_data.reset(); SOUP_THROW(Exception(format("Failed to create thread: {}", ret))); } have_handle = true; #endif - - running = true; } - Thread::~Thread() noexcept +#if SOUP_WINDOWS || SOUP_LINUX + void Thread::setTimeCritical() noexcept { #if SOUP_WINDOWS - if (handle != INVALID_HANDLE_VALUE) - { - awaitCompletion(); - CloseHandle(handle); - } + SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL); #else - awaitCompletion(); + pthread_setschedprio(handle, 15); #endif } +#endif -#if SOUP_WINDOWS || SOUP_LINUX - void Thread::setTimeCritical() noexcept + bool Thread::isAttached() const noexcept { #if SOUP_WINDOWS - SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL); + return handle != INVALID_HANDLE_VALUE; #else - pthread_setschedprio(handle, 15); + return have_handle; #endif } -#endif void Thread::awaitCompletion() noexcept { @@ -103,12 +81,16 @@ NAMESPACE_SOUP if (handle != INVALID_HANDLE_VALUE) { WaitForSingleObject(handle, INFINITE); + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + running_data.reset(); } #else if (have_handle) { pthread_join(handle, nullptr); have_handle = false; + running_data.reset(); } #endif } @@ -117,16 +99,48 @@ NAMESPACE_SOUP { #if SOUP_WINDOWS std::vector handles{}; + handles.reserve(threads.size()); for (auto& t : threads) { - handles.emplace_back(t->handle); + if (t->handle != INVALID_HANDLE_VALUE) + { + handles.emplace_back(t->handle); + } } WaitForMultipleObjects((DWORD)handles.size(), handles.data(), TRUE, INFINITE); + for (auto& t : threads) + { + if (t->handle != INVALID_HANDLE_VALUE) + { + CloseHandle(t->handle); + t->handle = INVALID_HANDLE_VALUE; + t->running_data.reset(); + } + } #else for (auto& t : threads) { t->awaitCompletion(); } +#endif + } + + void Thread::detach() noexcept + { +#if SOUP_WINDOWS + if (handle != INVALID_HANDLE_VALUE) + { + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + running_data.reset(); + } +#else + if (have_handle) + { + pthread_detach(handle); + have_handle = false; + running_data.reset(); + } #endif } } diff --git a/soup/Thread.hpp b/soup/Thread.hpp index b6f8e42e..4c568344 100644 --- a/soup/Thread.hpp +++ b/soup/Thread.hpp @@ -13,6 +13,7 @@ #include "Capture.hpp" #include "UniquePtr.hpp" +#include "WeakRef.hpp" NAMESPACE_SOUP { @@ -20,33 +21,38 @@ NAMESPACE_SOUP class Thread { public: + struct RunningData + { + void(*f)(Capture&&); + Capture f_cap; + TransientToken transient_token; + }; + #if SOUP_WINDOWS HANDLE handle = INVALID_HANDLE_VALUE; #else pthread_t handle{}; bool have_handle = false; #endif - bool running = false; - bool is_self_deleting = false; - void(*f)(Capture&&); - Capture f_cap; + WeakRef running_data{}; explicit Thread() noexcept = default; explicit Thread(void(*f)(Capture&&), Capture&& cap = {}); - explicit Thread(const Thread& b) = delete; - explicit Thread(Thread&& b) = delete; void start(void(*f)(Capture&&), Capture&& cap = {}); - ~Thread() noexcept; + ~Thread() noexcept { awaitCompletion(); } #if SOUP_WINDOWS || SOUP_LINUX void setTimeCritical() noexcept; #endif - [[nodiscard]] bool isRunning() const noexcept { return running; } + [[nodiscard]] bool isAttached() const noexcept; + [[nodiscard]] bool isRunning() const noexcept { return running_data.isValid(); } void awaitCompletion() noexcept; static void awaitCompletion(const std::vector>& threads) noexcept; + + void detach() noexcept; }; } diff --git a/soup/TransientToken.hpp b/soup/TransientToken.hpp index 7e1a608a..25a05e77 100644 --- a/soup/TransientToken.hpp +++ b/soup/TransientToken.hpp @@ -6,33 +6,49 @@ NAMESPACE_SOUP { class TransientTokenBase { - private: + public: SharedPtr sp; - public: - TransientTokenBase() SOUP_EXCAL - : sp(soup::make_shared(true)) + TransientTokenBase(bool valid) SOUP_EXCAL + : TransientTokenBase(soup::make_shared(valid)) { } - TransientTokenBase(bool valid) SOUP_EXCAL - : sp(soup::make_shared(valid)) + protected: + TransientTokenBase(SharedPtr&& sp) noexcept + : sp(std::move(sp)) { } + TransientTokenBase(const SharedPtr& sp) noexcept + : sp(sp) + { + } + + public: [[nodiscard]] bool isValid() const noexcept { return *sp; } + }; - void invalidate() const noexcept + struct TransientToken : public TransientTokenBase + { + using TransientTokenBase::TransientTokenBase; + + TransientToken() SOUP_EXCAL + : TransientTokenBase(soup::make_shared(true)) { - *sp = false; } - void reset() noexcept + ~TransientToken() noexcept + { + invalidate(); + } + + void invalidate() const noexcept { - sp.reset(); + *sp = false; } void refresh() SOUP_EXCAL @@ -42,15 +58,23 @@ NAMESPACE_SOUP } }; - struct TransientToken : public TransientTokenBase + struct TransientTokenRef : public TransientTokenBase { using TransientTokenBase::TransientTokenBase; - ~TransientToken() noexcept + TransientTokenRef() SOUP_EXCAL + : TransientTokenBase(soup::make_shared(false)) + { + } + + TransientTokenRef(const TransientTokenBase& tt) noexcept + : TransientTokenBase(tt.sp) { - invalidate(); } - }; - using TransientTokenRef = TransientTokenBase; + void reset() SOUP_EXCAL + { + sp = soup::make_shared(false); + } + }; } diff --git a/soup/WeakRef.hpp b/soup/WeakRef.hpp index 6a960a69..8efc4a00 100644 --- a/soup/WeakRef.hpp +++ b/soup/WeakRef.hpp @@ -61,7 +61,7 @@ NAMESPACE_SOUP return ptr != nullptr; } - void reset() noexcept + void reset() SOUP_EXCAL { tt.reset(); ptr = nullptr;