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 d6a79cb
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 69 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
102 changes: 58 additions & 44 deletions soup/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,97 +18,79 @@ NAMESPACE_SOUP
#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_data = WeakRef<RunningData>(data);

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_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<void*(*)(void*)>(&threadCreateCallback), this);
auto ret = pthread_create(&handle, &attr, reinterpret_cast<void*(*)(void*)>(&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
{
#if SOUP_WINDOWS
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
}
Expand All @@ -117,16 +99,48 @@ 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->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
}
}
Expand Down
22 changes: 14 additions & 8 deletions soup/Thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,46 @@

#include "Capture.hpp"
#include "UniquePtr.hpp"
#include "WeakRef.hpp"

NAMESPACE_SOUP
{
// This class itself is not thread-safe. If you need multiple threads to access the same instance, use a mutex.
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<RunningData> 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<UniquePtr<Thread>>& threads) noexcept;

void detach() 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 d6a79cb

Please sign in to comment.