Skip to content

Commit

Permalink
Add Thread::detach
Browse files Browse the repository at this point in the history
  • Loading branch information
Sainan committed Dec 19, 2024
1 parent be63f5d commit edf20a9
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 70 deletions.
3 changes: 2 additions & 1 deletion soup/SelfDeletingThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ NAMESPACE_SOUP
msg.append(e.what());
logWriteLine(std::move(msg));
}
t->is_self_deleting = true;
t->detach();
delete t;
}
}

Expand Down
128 changes: 83 additions & 45 deletions soup/Thread.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include "Thread.hpp"
#if !SOUP_WASM

#if !SOUP_WINDOWS
#include <cstring> // memcpy
#endif

#include "Exception.hpp"
#include "format.hpp"
#include "SelfDeletingThread.hpp"
Expand All @@ -12,103 +16,98 @@ NAMESPACE_SOUP
start(f, std::move(cap));
}

Thread::Thread(Thread&& b) noexcept
:
#if SOUP_WINDOWS
handle(b.handle),
#else
have_handle(b.have_handle),
#endif
running_ref(b.running_ref)
{
#if !SOUP_WINDOWS
memcpy(handle, b.handle, sizeof(handle));
#endif
b.forget();
}

static void
#if SOUP_WINDOWS
__stdcall
#endif
threadCreateCallback(void* handover)
{
auto t = reinterpret_cast<Thread*>(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<SelfDeletingThread*>(t);
}
auto data = reinterpret_cast<Thread::RunningData*>(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_ref = data->transient_token;

handle = CreateThread(nullptr, 0, reinterpret_cast<DWORD(__stdcall*)(LPVOID)>(&threadCreateCallback), this, 0, nullptr);
#if SOUP_WINDOWS
handle = CreateThread(nullptr, 0, reinterpret_cast<DWORD(__stdcall*)(LPVOID)>(&threadCreateCallback), data, 0, nullptr);
SOUP_IF_UNLIKELY (handle == NULL)
{
handle = INVALID_HANDLE_VALUE;
this->running_ref.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<void*(*)(void*)>(&threadCreateCallback), this);
auto ret = pthread_create(&handle, &attr, reinterpret_cast<void*(*)(void*)>(&threadCreateCallback), data);
SOUP_IF_UNLIKELY (ret != 0)
{
this->running_ref.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
{
#if SOUP_WINDOWS
if (handle != INVALID_HANDLE_VALUE)
{
WaitForSingleObject(handle, INFINITE);
CloseHandle(handle);
forget();
}
#else
if (have_handle)
{
pthread_join(handle, nullptr);
have_handle = false;
forget();
}
#endif
}
Expand All @@ -117,18 +116,57 @@ NAMESPACE_SOUP
{
#if SOUP_WINDOWS
std::vector<HANDLE> 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->forget();
}
}
#else
for (auto& t : threads)
{
t->awaitCompletion();
}
#endif
}

void Thread::detach() noexcept
{
#if SOUP_WINDOWS
if (handle != INVALID_HANDLE_VALUE)
{
CloseHandle(handle);
forget();
}
#else
if (have_handle)
{
pthread_detach(handle);
forget();
}
#endif
}

void Thread::forget() noexcept
{
#if SOUP_WINDOWS
handle = INVALID_HANDLE_VALUE;
#else
have_handle = false;
#endif
running_ref.reset();
}
}

#endif
27 changes: 19 additions & 8 deletions soup/Thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <vector>

#include "Capture.hpp"
#include "TransientToken.hpp"
#include "UniquePtr.hpp"

NAMESPACE_SOUP
Expand All @@ -20,33 +21,43 @@ 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;
TransientTokenRef running_ref{};

explicit Thread() noexcept = default;
explicit Thread() SOUP_EXCAL = default;
explicit Thread(void(*f)(Capture&&), Capture&& cap = {});
explicit Thread(const Thread& b) = delete;
explicit Thread(Thread&& b) = delete;
explicit Thread(Thread&& b) noexcept;
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_ref.isValid(); }

void awaitCompletion() noexcept;
static void awaitCompletion(const std::vector<UniquePtr<Thread>>& threads) noexcept;

void detach() noexcept;

protected:
void forget() noexcept;
};
}

Expand Down
54 changes: 39 additions & 15 deletions soup/TransientToken.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,49 @@ NAMESPACE_SOUP
{
class TransientTokenBase
{
private:
public:
SharedPtr<bool> sp;

public:
TransientTokenBase() SOUP_EXCAL
: sp(soup::make_shared<bool>(true))
TransientTokenBase(bool valid) SOUP_EXCAL
: TransientTokenBase(soup::make_shared<bool>(valid))
{
}

TransientTokenBase(bool valid) SOUP_EXCAL
: sp(soup::make_shared<bool>(valid))
protected:
TransientTokenBase(SharedPtr<bool>&& sp) noexcept
: sp(std::move(sp))
{
}

TransientTokenBase(const SharedPtr<bool>& 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<bool>(true))
{
*sp = false;
}

void reset() noexcept
~TransientToken() noexcept
{
invalidate();
}

void invalidate() const noexcept
{
sp.reset();
*sp = false;
}

void refresh() SOUP_EXCAL
Expand All @@ -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<bool>(false))
{
}

TransientTokenRef(const TransientTokenBase& tt) noexcept
: TransientTokenBase(tt.sp)
{
invalidate();
}
};

using TransientTokenRef = TransientTokenBase;
void reset() SOUP_EXCAL
{
sp = soup::make_shared<bool>(false);
}
};
}
2 changes: 1 addition & 1 deletion soup/WeakRef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ NAMESPACE_SOUP
return ptr != nullptr;
}

void reset() noexcept
void reset() SOUP_EXCAL
{
tt.reset();
ptr = nullptr;
Expand Down

0 comments on commit edf20a9

Please sign in to comment.