Skip to content

Commit

Permalink
Extend the Win32Thread class
Browse files Browse the repository at this point in the history
- Now it can serve effectively the same as std::jthread
  • Loading branch information
visuve committed Mar 29, 2024
1 parent d623c5f commit a787ca2
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
54 changes: 54 additions & 0 deletions HackLib/Win32Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ Win32Thread::Win32Thread(HANDLE handle) :
{
}

Win32Thread::Win32Thread(DWORD identifier, DWORD access) :
Win32Handle(OpenThread(access, true, identifier)),
_identifier(identifier)
{
}

Win32Thread::Win32Thread(std::function<uint32_t(std::stop_token)> callback) :
Win32Handle(CreateThread(nullptr, 0, ThreadProcedure, this, 0, &_identifier)),
_callback(std::bind(callback, _stopSource.get_token()))
{
}

Win32Thread::~Win32Thread()
{
if (_callback) // Yuck...
{
_stopSource.request_stop();

WaitForSingleObject(_handle, INFINITE);
}
}

void Win32Thread::Wait(std::chrono::milliseconds timeout) const
{
DWORD result = WaitForSingleObject(_handle, static_cast<DWORD>(timeout.count()));
Expand Down Expand Up @@ -35,4 +57,36 @@ DWORD Win32Thread::ExitCode()

_handle = nullptr;
return exitCode;
}

DWORD Win32Thread::Identifier()
{
if (!_identifier)
{
_identifier = GetThreadId(_handle);

if (!_identifier)
{
throw Win32Exception("GetThreadId");
}
}

return _identifier;
}

DWORD Win32Thread::ThreadProcedure(void* context)
{
auto self = reinterpret_cast<Win32Thread*>(context);

if (!self)
{
throw RuntimeException("Thread context was null!");
}

if (!self->_callback)
{
throw RuntimeException("Thread callback was null!");
}

return self->_callback();
}
11 changes: 11 additions & 0 deletions HackLib/Win32Thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,21 @@ class Win32Thread : public Win32Handle
{
public:
Win32Thread(HANDLE handle);
Win32Thread(DWORD identifier, DWORD access = THREAD_ALL_ACCESS);
Win32Thread(std::function<uint32_t(std::stop_token)> callback);
~Win32Thread();

NonCopyable(Win32Thread);

void Wait(std::chrono::milliseconds timeout = std::chrono::milliseconds(INFINITE)) const;

DWORD ExitCode();
DWORD Identifier();

private:
static DWORD ThreadProcedure(void*);

DWORD _identifier = 0;
std::stop_source _stopSource;
std::function<uint32_t()> _callback;
};
74 changes: 74 additions & 0 deletions HackLibTests/ThreadTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "Win32Thread.hpp"

TEST(ThreadTests, Basic)
{
size_t i = 0;

auto function = [&](std::stop_token)->uint32_t
{
while (i < 10)
{
++i;
}

return 0xABCDEFu;
};

Win32Thread thread(function);

thread.Wait(100ms);

EXPECT_EQ(thread.ExitCode(), 0xABCDEFu);
EXPECT_EQ(i, 10u);
}

TEST(ThreadTests, Abandon)
{
size_t i = 0;

auto function = [&](std::stop_token token)->uint32_t
{
while (!token.stop_requested())
{
++i;
}

return 0;
};

{
Win32Thread thread(function);
EXPECT_THROW(thread.Wait(1ms), std::system_error);
}

EXPECT_GT(i, 0u);
}

TEST(ThreadTests, Identifier)
{
size_t i = 0;

auto function = [&](std::stop_token token)->uint32_t
{
while (!token.stop_requested())
{
++i;
}

return 0;
};

{
Win32Thread thread(function);
DWORD threadID = thread.Identifier();

Win32Thread clone(threadID);
DWORD cloneID = clone.Identifier();

EXPECT_EQ(threadID, cloneID);

Sleep(1);
}

EXPECT_GT(i, 0u);
}

0 comments on commit a787ca2

Please sign in to comment.