thread: add minimal thread primitives for win32

This allows us to get around the lack of these types under MinGW
This commit is contained in:
Danny Robson 2019-06-22 15:46:34 +10:00
parent eb90d1a239
commit 01094611eb
38 changed files with 654 additions and 90 deletions

View File

@ -12,6 +12,12 @@ find_package (PythonInterp 3 REQUIRED)
find_package (RAGEL 6.9 REQUIRED)
if (WIN32)
# Enable features for Windows Vista and higher
add_definitions("-D_WIN32_WINNT=0x0600")
endif ()
###############################################################################
include (TestBigEndian)
TEST_BIG_ENDIAN(ENDIANNESS)
@ -59,7 +65,7 @@ if (BACKTRACE_LIB_FOUND)
list (APPEND UTIL_FILES backtrace_execinfo.cpp)
elseif (HAVE_CAPTURESTACKBACKTRACE)
list (APPEND UTIL_FILES backtrace_stackwalk.cpp)
target_link_libraries(cruft DbgHelp)
target_link_libraries(cruft dbghelp)
else ()
list (APPEND UTIL_FILES backtrace_null.cpp)
endif ()
@ -98,6 +104,7 @@ list (
##-----------------------------------------------------------------------------
if (LINUX)
list (APPEND UTIL_FILES
thread/thread_std.hpp
thread/event_futex.cpp
thread/flag_futex.cpp
rand/system_linux.cpp
@ -141,7 +148,6 @@ if (WIN32)
exe_win32.cpp
io_win32.cpp
io_win32.hpp
thread/event_win32.cpp
library_win32.cpp
library_win32.hpp
rand/system_win32.cpp
@ -164,35 +170,44 @@ endif ()
###############################################################################
list (APPEND UTIL_FILES
thread/condition_variable.hpp
thread/event.hpp
thread/flag.hpp
thread/mutex.hpp
thread/primitive.hpp
thread/semaphore.hpp
thread/thread.hpp
)
if (LINUX)
list (APPEND UTIL_FILES
thread/condition_variable_std.hpp
thread/event_futex.cpp
thread/event_futex.hpp
thread/semaphore_linux.hpp
thread/semaphore_linux.cpp
thread/flag_futex.cpp
thread/flag_futex.hpp
thread/mutex_std.hpp
)
elseif (WIN32)
list (APPEND UTIL_FILES
thread/condition_variable_win32.cpp
thread/condition_variable_win32.hpp
thread/event_std.cpp
thread/event_std.hpp
thread/event_win32.cpp
thread/mutex_win32.cpp
thread/mutex_win32.hpp
thread/semaphore_win32.hpp
thread/semaphore_win32.cpp
thread/flag_std.cpp
thread/flag_std.hpp
thread/thread_win32.cpp
thread/thread_win32.hpp
)
else ()
list (APPEND UTIL_FILES
thread/flag_std.cpp
thread/flag_std.hpp
)
message (FATAL_ERROR "Unsupported thread platform")
endif ()
@ -483,6 +498,7 @@ list (
thread/flag.hpp
thread/monitor.cpp
thread/monitor.hpp
thread/mutex.hpp
thread/ticketlock.cpp
thread/ticketlock.hpp
thread/spinlock.cpp
@ -681,12 +697,15 @@ if (TESTS)
string
stringid
strongdef
thread/condition_variable
thread/event
thread/flag
thread/monitor
thread/mutex
thread/semaphore
thread/spinlock
thread/ticketlock
thread/thread
time/8601
traits
tuple/index

View File

@ -27,7 +27,7 @@ queue::default_parallelism (void) noexcept
LOG_ERROR ("Unable to parse JOB_THREADS. Using the default");
}
return std::thread::hardware_concurrency ();
return cruft::thread::thread::hardware_concurrency ();
}
@ -62,7 +62,7 @@ queue::queue (unsigned thread_count, unsigned pool_size):
CHECK_GE (m_tasks.finishing.capacity (), thread_count);
for (auto &t: m_threads)
t = std::thread (&queue::loop, this);
t = cruft::thread::thread (&queue::loop, this);
}

View File

@ -11,16 +11,16 @@
#include "../pool.hpp"
#include "../thread/ticketlock.hpp"
#include "../thread/semaphore.hpp"
#include "../thread/flag.hpp"
#include "../thread/monitor.hpp"
#include "../thread/semaphore.hpp"
#include "../thread/thread.hpp"
#include "../thread/ticketlock.hpp"
#include "../parallel/queue.hpp"
#include <array>
#include <deque>
#include <thread>
#include <vector>
#include <new>
#include <cstddef>
@ -204,10 +204,10 @@ namespace cruft::job {
thread::semaphore m_pending;
std::vector<std::thread> m_threads;
std::vector<thread::thread> m_threads;
thread::semaphore m_doomed;
std::thread m_reaper;
thread::thread m_reaper;
};
}

View File

@ -11,7 +11,7 @@
#include "win32/windows.hpp"
#include <cruft/util/cast.hpp>
#include "cast.hpp"
#include <filesystem>
#include <libloaderapi.h>

View File

@ -1,5 +1,6 @@
///////////////////////////////////////////////////////////////////////////////
#include "job/queue.hpp"
#include "thread/thread.hpp"
#include "tap.hpp"
#include <chrono>
@ -32,7 +33,7 @@ main (void)
std::atomic<int> count = 0;
{
cruft::job::queue q {std::thread::hardware_concurrency (), INNER};
cruft::job::queue q {cruft::thread::thread::hardware_concurrency (), INNER};
std::vector<cruft::job::queue::cookie> cookies;
std::generate_n (std::back_inserter (cookies), INNER, [&] () {
return q.submit (

View File

@ -1,10 +1,11 @@
#include "parallel/queue.hpp"
#include "thread/flag.hpp"
#include "thread/semaphore.hpp"
#include "thread/thread.hpp"
#include "tap.hpp"
#include <algorithm>
#include <thread>
static constexpr uint32_t slots = 4;
@ -64,8 +65,8 @@ main ()
// start n consumers, and n producers that fill an array with integers
std::vector<int> dst (parallelism * chunk_size);
std::vector<std::thread> consumers;
std::vector<std::thread> producers;
std::vector<cruft::thread::thread> consumers;
std::vector<cruft::thread::thread> producers;
for (int i = 0; i < parallelism; ++i) {
consumers.emplace_back (

View File

@ -1,9 +1,9 @@
#include "thread/flag.hpp"
#include "thread/thread.hpp"
#include "parallel/stack.hpp"
#include "tap.hpp"
#include <thread>
int main ()
@ -25,13 +25,13 @@ int main ()
}
{
static constexpr int SIZE = 4;
cruft::parallel::stack<int> values (SIZE);
for (int i = 0; i < SIZE; ++i)
static constexpr int COUNT = 4;
cruft::parallel::stack<int> values (COUNT);
for (int i = 0; i < COUNT; ++i)
values.push (i);
bool success = true;
for (int i = SIZE - 1; i >= 0; --i) {
for (int i = COUNT - 1; i >= 0; --i) {
int res = -1;
values.pop (&res);
@ -57,8 +57,8 @@ int main ()
static int constexpr ITERATIONS = 8 * 1024;
cruft::parallel::stack<int> store (8);
cruft::thread::flag ev;
std::vector<std::thread> contestants;
for (unsigned i = 0; i < std::thread::hardware_concurrency () + 1; ++i)
std::vector<cruft::thread::thread> contestants;
for (unsigned i = 0; i < cruft::thread::thread::hardware_concurrency () + 1; ++i)
contestants.emplace_back (fight, std::ref (store), std::ref (ev), ITERATIONS);
ev.notify_all ();

View File

@ -0,0 +1,4 @@
int main ()
{
}

View File

@ -1,8 +1,9 @@
#include "thread/event.hpp"
#include "thread/thread.hpp"
#include "tap.hpp"
#include <atomic>
#include <thread>
int
@ -19,14 +20,14 @@ main ()
std::atomic<int> val = 0;
cruft::thread::event a;
std::thread t ([&] () {
cruft::thread::thread t ([&] () {
a.wait ();
++val;
});
// block for hopefully long enough to allow the above thread to change
// the value of the integer.
std::this_thread::sleep_for (std::chrono::milliseconds (100));
cruft::thread::this_thread::sleep_for (std::chrono::milliseconds (100));
tap.expect_eq (val, 0, "waiting actually blocks");

View File

@ -1,7 +1,7 @@
#include "thread/flag.hpp"
#include "tap.hpp"
#include "thread/thread.hpp"
#include <thread>
#include "tap.hpp"
int
@ -12,12 +12,12 @@ main ()
cruft::thread::flag f;
std::atomic<int> value = 0;
std::thread t1 ([&] () {
cruft::thread::thread t1 ([&] () {
f.wait ();
value = 1;
});
std::this_thread::sleep_for (std::chrono::milliseconds (100));
cruft::thread::this_thread::sleep_for (std::chrono::milliseconds (100));
tap.expect_eq (value, 0, "value hasn't been set during wait");
f.notify_all ();
@ -25,12 +25,12 @@ main ()
t1.join ();
tap.expect_eq (value, 1, "value has been changed after wait");
std::thread t2 ([&] () {
cruft::thread::thread t2 ([&] () {
f.wait ();
value = 2;
});
std::this_thread::sleep_for (std::chrono::milliseconds (100));
cruft::thread::this_thread::sleep_for (std::chrono::milliseconds (100));
tap.expect_eq (value, 2, "second wait didn't appear to block");
t2.join ();
@ -59,13 +59,13 @@ main ()
}
};
std::vector<std::thread> workers;
std::vector<cruft::thread::thread> workers;
std::generate_n (
std::back_inserter (workers),
parallelism,
[&, i = 0] () mutable
{
return std::thread { func, i++ };
return cruft::thread::thread { func, i++ };
});
for (auto &t: workers)

View File

@ -6,13 +6,14 @@
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
*/
#include "tap.hpp"
#include "thread/monitor.hpp"
#include "thread/event.hpp"
#include "thread/monitor.hpp"
#include "thread/semaphore.hpp"
#include "thread/thread.hpp"
#include "tap.hpp"
#include <atomic>
#include <thread>
///////////////////////////////////////////////////////////////////////////////
@ -48,8 +49,8 @@ main ()
auto &enter = obj->enter;
auto &leave = obj->leave;
std::thread t1 ([&] () { obj->inc (); });
std::thread t2 ([&] () { obj->inc (); });
cruft::thread::thread t1 ([&] () { obj->inc (); });
cruft::thread::thread t2 ([&] () { obj->inc (); });
tap.expect_eq (value, 0, "wrapped class was left initialised");

12
test/thread/mutex.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "tap.hpp"
#include "thread/mutex.hpp"
int main ()
{
cruft::TAP::logger tap;
cruft::thread::mutex a;
cruft::thread::mutex b;
return tap.status ();
}

View File

@ -1,9 +1,9 @@
#include "thread/semaphore.hpp"
#include "thread/flag.hpp"
#include "tap.hpp"
#include "thread/thread.hpp"
#include "thread/mutex.hpp"
#include <thread>
#include <mutex>
#include "tap.hpp"
///////////////////////////////////////////////////////////////////////////////
@ -35,7 +35,7 @@ main ()
// the spawned thread attempts to double acquire a semaphore with
// only one available and so should get blocked immediately.
std::atomic<int> test = 0;
std::thread t ([&] () {
cruft::thread::thread t ([&] () {
sem.lock ();
sem.lock ();
test = 1;
@ -44,7 +44,7 @@ main ()
// wait until we know the thread should have been blocked. it should
// not have touched the 'test' variable at this point.
while (sem.value () > 0)
std::this_thread::yield ();
cruft::thread::this_thread::yield ();
tap.expect_eq (test, 0, "locking blocks in foreign thread");
@ -56,9 +56,9 @@ main ()
}
{
const auto parallelism = std::thread::hardware_concurrency ();
const auto parallelism = cruft::thread::thread::hardware_concurrency ();
constexpr int iterations = 1 << 16;
std::vector<std::thread> threads;
std::vector<cruft::thread::thread> threads;
cruft::thread::semaphore sem (0);

View File

@ -8,9 +8,10 @@
#include "thread/flag.hpp"
#include "thread/spinlock.hpp"
#include "thread/thread.hpp"
#include "tap.hpp"
#include <thread>
#include <mutex>
@ -37,14 +38,14 @@ main ()
l.unlock ();
tap.expect (true, "unlocked without contention");
if (std::thread::hardware_concurrency () < 2) {
if (cruft::thread::thread::hardware_concurrency () < 2) {
tap.skip ("n-way fight");
} else {
constexpr int iterations = 1 << 12;
cruft::thread::flag start_flag;
std::vector<std::thread> contestants;
for (unsigned i = 0; i < std::thread::hardware_concurrency (); ++i)
std::vector<cruft::thread::thread> contestants;
for (unsigned i = 0; i < cruft::thread::thread::hardware_concurrency (); ++i)
contestants.emplace_back (fight, std::ref (start_flag), std::ref (l), iterations);
start_flag.notify_all ();
@ -52,7 +53,7 @@ main ()
for (auto &t: contestants)
t.join ();
tap.expect (true, "n-way fight, %! contestants", std::thread::hardware_concurrency ());
tap.expect (true, "n-way fight, %! contestants", cruft::thread::thread::hardware_concurrency ());
}
return tap.status ();

29
test/thread/thread.cpp Normal file
View File

@ -0,0 +1,29 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#include "thread/thread.hpp"
#include "tap.hpp"
#include <atomic>
static void run (std::atomic<int> &val) { ++val; }
int main ()
{
cruft::TAP::logger tap;
// Get the thread to increment an int. If it's been incremented once after
// joining then we assume the thread ran successfully.
std::atomic<int> val = 0;
cruft::thread::thread dude (run, std::ref (val));
dude.join ();
tap.expect_eq (val.load (), 1, "thread appears to have run");
return tap.status ();
}

View File

@ -8,10 +8,10 @@
#include "thread/flag.hpp"
#include "thread/ticketlock.hpp"
#include "tap.hpp"
#include "thread/thread.hpp"
#include "thread/mutex.hpp"
#include <thread>
#include <mutex>
#include "tap.hpp"
///////////////////////////////////////////////////////////////////////////////
@ -44,7 +44,7 @@ main ()
l.unlock ();
tap.expect (true, "unlocked without contention");
if (true || std::thread::hardware_concurrency () < 2) {
if (true || cruft::thread::thread::hardware_concurrency () < 2) {
tap.skip ("reasonably fair under contention");
} else {
// measure fairness under contention is below some threshold
@ -63,8 +63,21 @@ main ()
ffs_t::time_point a_finish, b_finish;
std::thread a (fight, std::ref (start_flag), std::ref (l), iterations, std::ref (a_finish));
std::thread b (fight, std::ref (start_flag), std::ref (l), iterations, std::ref (b_finish));
cruft::thread::thread a (
fight,
std::ref (start_flag),
std::ref (l),
iterations,
std::ref (a_finish)
);
cruft::thread::thread b (
fight,
std::ref (start_flag),
std::ref (l),
iterations,
std::ref (b_finish)
);
auto start = std::chrono::high_resolution_clock::now ();
start_flag.notify_all ();

View File

@ -0,0 +1,19 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "../platform.hpp"
#if defined(PLATFORM_LINUX)
#include "condition_variable_std.hpp"
#elif defined(PLATFORM_WIN32)
#include "condition_variable_win32.hpp"
#else
#error "Unsupported platform for condition_variable"
#endif

View File

@ -0,0 +1,17 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include <condition_variable>
namespace cruft::thread {
inline namespace std {
using condition_variable = ::std::condition_variable;
}
}

View File

@ -0,0 +1,42 @@
#include "condition_variable_win32.hpp"
#include <synchapi.h>
using cruft::thread::win32::condition_variable;
///////////////////////////////////////////////////////////////////////////////
condition_variable::condition_variable ()
: m_native (CONDITION_VARIABLE_INIT)
{
InitializeConditionVariable (&m_native);
}
//-----------------------------------------------------------------------------
condition_variable::~condition_variable ()
{ ; }
///////////////////////////////////////////////////////////////////////////////
void
condition_variable::wait (std::unique_lock<cruft::thread::mutex> &lock)
{
SleepConditionVariableCS (
&m_native, &lock.mutex ()->native_handle (), INFINITE
);
}
///////////////////////////////////////////////////////////////////////////////
void condition_variable::notify_one (void) noexcept
{
WakeConditionVariable (&m_native);
}
//-----------------------------------------------------------------------------
void condition_variable::notify_all () noexcept
{
WakeAllConditionVariable (&m_native);
}

View File

@ -0,0 +1,61 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "mutex.hpp"
#include <mutex>
namespace cruft::thread {
inline namespace win32 {
enum class cv_status {
no_timeout,
timeout
};
class condition_variable {
public:
condition_variable ();
condition_variable (condition_variable const&) noexcept = delete;
condition_variable& operator= (condition_variable const&) = delete;
~condition_variable ();
void notify_one (void) noexcept;
void notify_all (void) noexcept;
void wait (std::unique_lock<mutex>&);
template <typename PredicateT>
void wait (
std::unique_lock<mutex> &lock,
PredicateT pred
) {
while (!pred ())
wait (lock);
}
template <class RepT, class PeriodT>
cv_status
wait_for (
std::unique_lock<mutex>& lock,
std::chrono::duration<RepT, PeriodT> const &rel_time
);
template <class RepT, class PeriodT, class PredicateT>
bool wait_for (
std::unique_lock<mutex>& lock,
std::chrono::duration<RepT, PeriodT> const &rel_time,
PredicateT pred
);
private:
CONDITION_VARIABLE m_native;
};
};
}

View File

@ -8,9 +8,10 @@
#pragma once
#include "condition_variable.hpp"
#include "mutex.hpp"
#include <atomic>
#include <mutex>
#include <condition_variable>
namespace cruft::thread {
@ -48,8 +49,8 @@ namespace cruft::thread {
private:
std::atomic<int> m_value;
std::mutex m_mutex;
std::condition_variable m_cv;
mutex m_mutex;
condition_variable m_cv;
};
}

View File

@ -8,9 +8,10 @@
#pragma once
#include "mutex.hpp"
#include "condition_variable.hpp"
#include <atomic>
#include <mutex>
#include <condition_variable>
namespace cruft::thread {
/// a fire-once event that users can wait upon. if already fired then
@ -33,7 +34,7 @@ namespace cruft::thread {
private:
std::atomic<bool> fired;
std::mutex m_mutex;
std::condition_variable m_condition;
::cruft::thread::mutex m_mutex;
::cruft::thread::condition_variable m_condition;
};
}

4
thread/fwd.hpp Normal file
View File

@ -0,0 +1,4 @@
#pragma once
namespace cruft::thread {
}

View File

@ -9,18 +9,18 @@
#ifndef CRUFT_UTIL_THREAD_MONITOR_HPP
#define CRUFT_UTIL_THREAD_MONITOR_HPP
#include <mutex>
#include "mutex.hpp"
#include <utility>
#include <functional>
namespace cruft::thread {
template <typename ValueT, typename MutexT = std::mutex>
template <typename ValueT, typename MutexT = cruft::thread::mutex>
class monitor {
public:
template <typename ...Args>
monitor (Args &&...args):
m_value (std::forward<Args> (args)...)
m_value (::std::forward<Args> (args)...)
{ ; }
class proxy {
@ -36,7 +36,7 @@ namespace cruft::thread {
}
private:
std::lock_guard<MutexT> m_guard;
::std::lock_guard<MutexT> m_guard;
ValueT &m_value;
};

19
thread/mutex.hpp Normal file
View File

@ -0,0 +1,19 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "../platform.hpp"
#if defined(PLATFORM_LINUX)
#include "mutex_std.hpp"
#elif defined(PLATFORM_WIN32)
#include "mutex_win32.hpp"
#else
#error "Unsupported mutex platform"
#endif

17
thread/mutex_std.hpp Normal file
View File

@ -0,0 +1,17 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include <mutex>
namespace cruft::thread {
inline namespace std {
using mutex = ::std::mutex;
}
}

39
thread/mutex_win32.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "mutex.hpp"
using cruft::thread::win32::mutex;
///////////////////////////////////////////////////////////////////////////////
mutex::~mutex ()
{
DeleteCriticalSection (&m_section);
}
///////////////////////////////////////////////////////////////////////////////
void mutex::lock (void)
{
EnterCriticalSection (&m_section);
}
//-----------------------------------------------------------------------------
void mutex::unlock (void)
{
LeaveCriticalSection (&m_section);
}
//-----------------------------------------------------------------------------
bool mutex::try_lock (void)
{
return TryEnterCriticalSection (&m_section);
}
///////////////////////////////////////////////////////////////////////////////
CRITICAL_SECTION&
mutex::native_handle (void)
{
return m_section;
}

41
thread/mutex_win32.hpp Normal file
View File

@ -0,0 +1,41 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "../win32/handle.hpp"
#include "../win32/windows.hpp"
// Include the std mutex header so we can get access to expected
// functionality like std::lock_guard.
#include <mutex>
namespace cruft::thread {
inline namespace win32 {
class mutex {
public:
constexpr mutex () noexcept
: m_section {}
{
InitializeCriticalSection (&m_section);
}
mutex (mutex const&) = delete;
~mutex ();
void lock (void);
void unlock (void);
bool try_lock (void);
CRITICAL_SECTION& native_handle (void);
private:
CRITICAL_SECTION m_section;
};
};
}

View File

@ -6,9 +6,6 @@
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef CRUFT_UTIL_THREAD_SEMAPHORE_HPP
#define CRUFT_UTIL_THREAD_SEMAPHORE_HPP
#include <atomic>
namespace cruft::thread {
@ -37,8 +34,6 @@ namespace cruft::thread {
int operator-- (void);
private:
std::atomic<int> m_value;
::std::atomic<int> m_value;
};
};
#endif

View File

@ -42,6 +42,6 @@ namespace cruft::thread {
private:
std::atomic<LONG> m_value;
win32::handle m_handle;
::cruft::win32::handle m_handle;
};
};

View File

@ -37,10 +37,10 @@ namespace cruft::thread {
private:
static_assert (
std::atomic<bool>::is_always_lock_free,
::std::atomic<bool>::is_always_lock_free,
"we require a lock free primitive or the entire point is moot"
);
std::atomic<bool> held;
::std::atomic<bool> held;
};
};

19
thread/thread.hpp Normal file
View File

@ -0,0 +1,19 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "../platform.hpp"
#if defined(PLATFORM_LINUX)
#include "thread_std.hpp"
#elif defined(PLATFORM_WIN32)
#include "thread_win32.hpp"
#else
#error "Unsupported threading system"
#endif

18
thread/thread_std.hpp Normal file
View File

@ -0,0 +1,18 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include <thread>
namespace cruft::thread {
inline namespace std {
using thread = ::std::thread;
namespace this_thread = ::std::this_thread;
}
}

47
thread/thread_win32.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "thread_win32.hpp"
using cruft::thread::win32::thread;
///////////////////////////////////////////////////////////////////////////////
thread::thread () noexcept
{ ; }
//-----------------------------------------------------------------------------
thread::thread (thread &&rhs) noexcept
{
std::swap (m_handle, rhs.m_handle);
}
//-----------------------------------------------------------------------------
thread&
thread::operator= (thread &&rhs) noexcept
{
std::swap (m_handle, rhs.m_handle);
return *this;
}
///////////////////////////////////////////////////////////////////////////////
void thread::join ()
{
WaitForSingleObject (m_handle, INFINITE);
}
///////////////////////////////////////////////////////////////////////////////
unsigned int thread::hardware_concurrency (void) noexcept
{
SYSTEM_INFO res;
GetNativeSystemInfo (&res);
return res.dwNumberOfProcessors;
}
///////////////////////////////////////////////////////////////////////////////
void cruft::thread::win32::this_thread::yield (void) noexcept
{
Sleep (0);
}

144
thread/thread_win32.hpp Normal file
View File

@ -0,0 +1,144 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "win32/handle.hpp"
#include "win32/windows.hpp"
#include "win32/except.hpp"
#include <chrono>
namespace cruft::thread {
inline namespace win32 {
namespace detail {
template <class T>
std::decay_t<T>
decay_copy (T&& v)
{
return std::forward<T>(v);
}
template <typename FunctionT, typename ...ArgsT>
class dispatcher {
public:
dispatcher (FunctionT &&func, ArgsT &&...args)
: m_func (std::forward<FunctionT> (func))
, m_args (std::forward<ArgsT> (args)...)
{ ; }
void run (void)
{
return _run (
std::make_index_sequence<sizeof...(ArgsT)> {}
);
}
private:
template <std::size_t... S>
void _run (std::index_sequence <S...>)
{
std::invoke (
decay_copy (std::forward<FunctionT> (m_func)),
decay_copy (std::forward<ArgsT> (std::get<S> (m_args)))...
);
}
FunctionT m_func;
std::tuple<ArgsT...> m_args;
};
template <typename DispatcherT>
DWORD WINAPI run (LPVOID arg)
{
auto *obj = reinterpret_cast<DispatcherT*> (arg);
try {
obj->run ();
return 0;
} catch (...) {
delete obj;
throw;
}
}
};
struct thread {
using id = ::cruft::win32::handle::native_type;
thread () noexcept;
thread (thread &&) noexcept;
template <typename FunctionT, typename... ArgsT>
thread (FunctionT &&func, ArgsT&&...args)
{
using dispatch_t = detail::dispatcher<FunctionT,ArgsT...>;
auto data = std::make_unique<dispatch_t> (
std::forward<FunctionT> (func),
std::forward<ArgsT> (args)...
);
m_handle.reset (
CreateThread (
nullptr, 0,
detail::run<dispatch_t>,
static_cast<void*> (data.get ()),
0,
nullptr
)
);
if (!m_handle)
throw std::system_error (
cruft::win32::error::last_code (),
std::generic_category ()
);
data.release ();
}
thread (thread const&) = delete;
thread& operator= (thread&&) noexcept;
void join (void);
static unsigned int hardware_concurrency () noexcept;
private:
::cruft::win32::handle m_handle;
};
namespace this_thread {
void yield (void) noexcept;
thread::id get_id (void) noexcept;
template <class Rep, class Period>
void sleep_for (
std::chrono::duration<Rep, Period> const& sleep_duration
) {
auto remain = std::chrono::duration_cast<std::chrono::milliseconds> (
sleep_duration
).count ();
while (remain > 0) {
auto const step = cruft::min (remain, INFINITE - 1);
Sleep (remain);
remain -= step;
}
}
template <class Clock, class Duration>
void sleep_until (std::chrono::time_point<Clock,Duration> const& sleep_time);
}
};
}

View File

@ -6,9 +6,6 @@
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef CRUFT_UTIL_THREAD_TICKETLOCK_HPP
#define CRUFT_UTIL_THREAD_TICKETLOCK_HPP
#include <atomic>
namespace cruft::thread {
@ -20,9 +17,7 @@ namespace cruft::thread {
void unlock (void);
private:
std::atomic<unsigned> current;
std::atomic<unsigned> next;
::std::atomic<unsigned> current;
::std::atomic<unsigned> next;
};
};
#endif

View File

@ -8,12 +8,13 @@
#include "time.hpp"
#include <thread>
#include "thread/thread.hpp"
#include <chrono>
///////////////////////////////////////////////////////////////////////////////
void
cruft::sleep (uint64_t ns)
{
std::this_thread::sleep_for (std::chrono::nanoseconds (ns));
cruft::thread::this_thread::sleep_for (std::chrono::nanoseconds (ns));
}

View File

@ -14,6 +14,8 @@
namespace cruft::win32 {
struct handle {
using native_type = HANDLE;
handle ();
explicit handle (posix::fd&&);
explicit handle (HANDLE&&);