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:
parent
eb90d1a239
commit
01094611eb
@ -12,6 +12,12 @@ find_package (PythonInterp 3 REQUIRED)
|
|||||||
find_package (RAGEL 6.9 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)
|
include (TestBigEndian)
|
||||||
TEST_BIG_ENDIAN(ENDIANNESS)
|
TEST_BIG_ENDIAN(ENDIANNESS)
|
||||||
@ -59,7 +65,7 @@ if (BACKTRACE_LIB_FOUND)
|
|||||||
list (APPEND UTIL_FILES backtrace_execinfo.cpp)
|
list (APPEND UTIL_FILES backtrace_execinfo.cpp)
|
||||||
elseif (HAVE_CAPTURESTACKBACKTRACE)
|
elseif (HAVE_CAPTURESTACKBACKTRACE)
|
||||||
list (APPEND UTIL_FILES backtrace_stackwalk.cpp)
|
list (APPEND UTIL_FILES backtrace_stackwalk.cpp)
|
||||||
target_link_libraries(cruft DbgHelp)
|
target_link_libraries(cruft dbghelp)
|
||||||
else ()
|
else ()
|
||||||
list (APPEND UTIL_FILES backtrace_null.cpp)
|
list (APPEND UTIL_FILES backtrace_null.cpp)
|
||||||
endif ()
|
endif ()
|
||||||
@ -98,6 +104,7 @@ list (
|
|||||||
##-----------------------------------------------------------------------------
|
##-----------------------------------------------------------------------------
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
list (APPEND UTIL_FILES
|
list (APPEND UTIL_FILES
|
||||||
|
thread/thread_std.hpp
|
||||||
thread/event_futex.cpp
|
thread/event_futex.cpp
|
||||||
thread/flag_futex.cpp
|
thread/flag_futex.cpp
|
||||||
rand/system_linux.cpp
|
rand/system_linux.cpp
|
||||||
@ -141,7 +148,6 @@ if (WIN32)
|
|||||||
exe_win32.cpp
|
exe_win32.cpp
|
||||||
io_win32.cpp
|
io_win32.cpp
|
||||||
io_win32.hpp
|
io_win32.hpp
|
||||||
thread/event_win32.cpp
|
|
||||||
library_win32.cpp
|
library_win32.cpp
|
||||||
library_win32.hpp
|
library_win32.hpp
|
||||||
rand/system_win32.cpp
|
rand/system_win32.cpp
|
||||||
@ -164,35 +170,44 @@ endif ()
|
|||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
list (APPEND UTIL_FILES
|
list (APPEND UTIL_FILES
|
||||||
|
thread/condition_variable.hpp
|
||||||
thread/event.hpp
|
thread/event.hpp
|
||||||
thread/flag.hpp
|
thread/flag.hpp
|
||||||
|
thread/mutex.hpp
|
||||||
thread/primitive.hpp
|
thread/primitive.hpp
|
||||||
thread/semaphore.hpp
|
thread/semaphore.hpp
|
||||||
|
thread/thread.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
list (APPEND UTIL_FILES
|
list (APPEND UTIL_FILES
|
||||||
|
thread/condition_variable_std.hpp
|
||||||
thread/event_futex.cpp
|
thread/event_futex.cpp
|
||||||
thread/event_futex.hpp
|
thread/event_futex.hpp
|
||||||
thread/semaphore_linux.hpp
|
thread/semaphore_linux.hpp
|
||||||
thread/semaphore_linux.cpp
|
thread/semaphore_linux.cpp
|
||||||
thread/flag_futex.cpp
|
thread/flag_futex.cpp
|
||||||
thread/flag_futex.hpp
|
thread/flag_futex.hpp
|
||||||
|
thread/mutex_std.hpp
|
||||||
)
|
)
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
list (APPEND UTIL_FILES
|
list (APPEND UTIL_FILES
|
||||||
|
thread/condition_variable_win32.cpp
|
||||||
|
thread/condition_variable_win32.hpp
|
||||||
thread/event_std.cpp
|
thread/event_std.cpp
|
||||||
thread/event_std.hpp
|
thread/event_std.hpp
|
||||||
|
thread/event_win32.cpp
|
||||||
|
thread/mutex_win32.cpp
|
||||||
|
thread/mutex_win32.hpp
|
||||||
thread/semaphore_win32.hpp
|
thread/semaphore_win32.hpp
|
||||||
thread/semaphore_win32.cpp
|
thread/semaphore_win32.cpp
|
||||||
thread/flag_std.cpp
|
thread/flag_std.cpp
|
||||||
thread/flag_std.hpp
|
thread/flag_std.hpp
|
||||||
|
thread/thread_win32.cpp
|
||||||
|
thread/thread_win32.hpp
|
||||||
)
|
)
|
||||||
else ()
|
else ()
|
||||||
list (APPEND UTIL_FILES
|
message (FATAL_ERROR "Unsupported thread platform")
|
||||||
thread/flag_std.cpp
|
|
||||||
thread/flag_std.hpp
|
|
||||||
)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
@ -483,6 +498,7 @@ list (
|
|||||||
thread/flag.hpp
|
thread/flag.hpp
|
||||||
thread/monitor.cpp
|
thread/monitor.cpp
|
||||||
thread/monitor.hpp
|
thread/monitor.hpp
|
||||||
|
thread/mutex.hpp
|
||||||
thread/ticketlock.cpp
|
thread/ticketlock.cpp
|
||||||
thread/ticketlock.hpp
|
thread/ticketlock.hpp
|
||||||
thread/spinlock.cpp
|
thread/spinlock.cpp
|
||||||
@ -681,12 +697,15 @@ if (TESTS)
|
|||||||
string
|
string
|
||||||
stringid
|
stringid
|
||||||
strongdef
|
strongdef
|
||||||
|
thread/condition_variable
|
||||||
thread/event
|
thread/event
|
||||||
thread/flag
|
thread/flag
|
||||||
thread/monitor
|
thread/monitor
|
||||||
|
thread/mutex
|
||||||
thread/semaphore
|
thread/semaphore
|
||||||
thread/spinlock
|
thread/spinlock
|
||||||
thread/ticketlock
|
thread/ticketlock
|
||||||
|
thread/thread
|
||||||
time/8601
|
time/8601
|
||||||
traits
|
traits
|
||||||
tuple/index
|
tuple/index
|
||||||
|
@ -27,7 +27,7 @@ queue::default_parallelism (void) noexcept
|
|||||||
LOG_ERROR ("Unable to parse JOB_THREADS. Using the default");
|
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);
|
CHECK_GE (m_tasks.finishing.capacity (), thread_count);
|
||||||
|
|
||||||
for (auto &t: m_threads)
|
for (auto &t: m_threads)
|
||||||
t = std::thread (&queue::loop, this);
|
t = cruft::thread::thread (&queue::loop, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
|
|
||||||
#include "../pool.hpp"
|
#include "../pool.hpp"
|
||||||
|
|
||||||
#include "../thread/ticketlock.hpp"
|
|
||||||
#include "../thread/semaphore.hpp"
|
|
||||||
#include "../thread/flag.hpp"
|
#include "../thread/flag.hpp"
|
||||||
#include "../thread/monitor.hpp"
|
#include "../thread/monitor.hpp"
|
||||||
|
#include "../thread/semaphore.hpp"
|
||||||
|
#include "../thread/thread.hpp"
|
||||||
|
#include "../thread/ticketlock.hpp"
|
||||||
|
|
||||||
#include "../parallel/queue.hpp"
|
#include "../parallel/queue.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@ -204,10 +204,10 @@ namespace cruft::job {
|
|||||||
|
|
||||||
thread::semaphore m_pending;
|
thread::semaphore m_pending;
|
||||||
|
|
||||||
std::vector<std::thread> m_threads;
|
std::vector<thread::thread> m_threads;
|
||||||
|
|
||||||
thread::semaphore m_doomed;
|
thread::semaphore m_doomed;
|
||||||
std::thread m_reaper;
|
thread::thread m_reaper;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#include "win32/windows.hpp"
|
#include "win32/windows.hpp"
|
||||||
|
|
||||||
#include <cruft/util/cast.hpp>
|
#include "cast.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <libloaderapi.h>
|
#include <libloaderapi.h>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#include "job/queue.hpp"
|
#include "job/queue.hpp"
|
||||||
|
#include "thread/thread.hpp"
|
||||||
#include "tap.hpp"
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -32,7 +33,7 @@ main (void)
|
|||||||
std::atomic<int> count = 0;
|
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::vector<cruft::job::queue::cookie> cookies;
|
||||||
std::generate_n (std::back_inserter (cookies), INNER, [&] () {
|
std::generate_n (std::back_inserter (cookies), INNER, [&] () {
|
||||||
return q.submit (
|
return q.submit (
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
#include "parallel/queue.hpp"
|
#include "parallel/queue.hpp"
|
||||||
#include "thread/flag.hpp"
|
#include "thread/flag.hpp"
|
||||||
#include "thread/semaphore.hpp"
|
#include "thread/semaphore.hpp"
|
||||||
|
#include "thread/thread.hpp"
|
||||||
|
|
||||||
#include "tap.hpp"
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
|
|
||||||
static constexpr uint32_t slots = 4;
|
static constexpr uint32_t slots = 4;
|
||||||
@ -64,8 +65,8 @@ main ()
|
|||||||
|
|
||||||
// start n consumers, and n producers that fill an array with integers
|
// start n consumers, and n producers that fill an array with integers
|
||||||
std::vector<int> dst (parallelism * chunk_size);
|
std::vector<int> dst (parallelism * chunk_size);
|
||||||
std::vector<std::thread> consumers;
|
std::vector<cruft::thread::thread> consumers;
|
||||||
std::vector<std::thread> producers;
|
std::vector<cruft::thread::thread> producers;
|
||||||
|
|
||||||
for (int i = 0; i < parallelism; ++i) {
|
for (int i = 0; i < parallelism; ++i) {
|
||||||
consumers.emplace_back (
|
consumers.emplace_back (
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#include "thread/flag.hpp"
|
#include "thread/flag.hpp"
|
||||||
|
#include "thread/thread.hpp"
|
||||||
#include "parallel/stack.hpp"
|
#include "parallel/stack.hpp"
|
||||||
|
|
||||||
#include "tap.hpp"
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
|
|
||||||
int main ()
|
int main ()
|
||||||
@ -25,13 +25,13 @@ int main ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
static constexpr int SIZE = 4;
|
static constexpr int COUNT = 4;
|
||||||
cruft::parallel::stack<int> values (SIZE);
|
cruft::parallel::stack<int> values (COUNT);
|
||||||
for (int i = 0; i < SIZE; ++i)
|
for (int i = 0; i < COUNT; ++i)
|
||||||
values.push (i);
|
values.push (i);
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
for (int i = SIZE - 1; i >= 0; --i) {
|
for (int i = COUNT - 1; i >= 0; --i) {
|
||||||
int res = -1;
|
int res = -1;
|
||||||
values.pop (&res);
|
values.pop (&res);
|
||||||
|
|
||||||
@ -57,8 +57,8 @@ int main ()
|
|||||||
static int constexpr ITERATIONS = 8 * 1024;
|
static int constexpr ITERATIONS = 8 * 1024;
|
||||||
cruft::parallel::stack<int> store (8);
|
cruft::parallel::stack<int> store (8);
|
||||||
cruft::thread::flag ev;
|
cruft::thread::flag ev;
|
||||||
std::vector<std::thread> contestants;
|
std::vector<cruft::thread::thread> contestants;
|
||||||
for (unsigned i = 0; i < std::thread::hardware_concurrency () + 1; ++i)
|
for (unsigned i = 0; i < cruft::thread::thread::hardware_concurrency () + 1; ++i)
|
||||||
contestants.emplace_back (fight, std::ref (store), std::ref (ev), ITERATIONS);
|
contestants.emplace_back (fight, std::ref (store), std::ref (ev), ITERATIONS);
|
||||||
|
|
||||||
ev.notify_all ();
|
ev.notify_all ();
|
||||||
|
4
test/thread/condition_variable.cpp
Normal file
4
test/thread/condition_variable.cpp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
int main ()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
#include "thread/event.hpp"
|
#include "thread/event.hpp"
|
||||||
|
#include "thread/thread.hpp"
|
||||||
|
|
||||||
#include "tap.hpp"
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -19,14 +20,14 @@ main ()
|
|||||||
std::atomic<int> val = 0;
|
std::atomic<int> val = 0;
|
||||||
cruft::thread::event a;
|
cruft::thread::event a;
|
||||||
|
|
||||||
std::thread t ([&] () {
|
cruft::thread::thread t ([&] () {
|
||||||
a.wait ();
|
a.wait ();
|
||||||
++val;
|
++val;
|
||||||
});
|
});
|
||||||
|
|
||||||
// block for hopefully long enough to allow the above thread to change
|
// block for hopefully long enough to allow the above thread to change
|
||||||
// the value of the integer.
|
// 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");
|
tap.expect_eq (val, 0, "waiting actually blocks");
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "thread/flag.hpp"
|
#include "thread/flag.hpp"
|
||||||
#include "tap.hpp"
|
#include "thread/thread.hpp"
|
||||||
|
|
||||||
#include <thread>
|
#include "tap.hpp"
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -12,12 +12,12 @@ main ()
|
|||||||
cruft::thread::flag f;
|
cruft::thread::flag f;
|
||||||
std::atomic<int> value = 0;
|
std::atomic<int> value = 0;
|
||||||
|
|
||||||
std::thread t1 ([&] () {
|
cruft::thread::thread t1 ([&] () {
|
||||||
f.wait ();
|
f.wait ();
|
||||||
value = 1;
|
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");
|
tap.expect_eq (value, 0, "value hasn't been set during wait");
|
||||||
f.notify_all ();
|
f.notify_all ();
|
||||||
@ -25,12 +25,12 @@ main ()
|
|||||||
t1.join ();
|
t1.join ();
|
||||||
tap.expect_eq (value, 1, "value has been changed after wait");
|
tap.expect_eq (value, 1, "value has been changed after wait");
|
||||||
|
|
||||||
std::thread t2 ([&] () {
|
cruft::thread::thread t2 ([&] () {
|
||||||
f.wait ();
|
f.wait ();
|
||||||
value = 2;
|
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");
|
tap.expect_eq (value, 2, "second wait didn't appear to block");
|
||||||
t2.join ();
|
t2.join ();
|
||||||
|
|
||||||
@ -59,13 +59,13 @@ main ()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::thread> workers;
|
std::vector<cruft::thread::thread> workers;
|
||||||
std::generate_n (
|
std::generate_n (
|
||||||
std::back_inserter (workers),
|
std::back_inserter (workers),
|
||||||
parallelism,
|
parallelism,
|
||||||
[&, i = 0] () mutable
|
[&, i = 0] () mutable
|
||||||
{
|
{
|
||||||
return std::thread { func, i++ };
|
return cruft::thread::thread { func, i++ };
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto &t: workers)
|
for (auto &t: workers)
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tap.hpp"
|
|
||||||
#include "thread/monitor.hpp"
|
|
||||||
#include "thread/event.hpp"
|
#include "thread/event.hpp"
|
||||||
|
#include "thread/monitor.hpp"
|
||||||
#include "thread/semaphore.hpp"
|
#include "thread/semaphore.hpp"
|
||||||
|
#include "thread/thread.hpp"
|
||||||
|
|
||||||
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -48,8 +49,8 @@ main ()
|
|||||||
auto &enter = obj->enter;
|
auto &enter = obj->enter;
|
||||||
auto &leave = obj->leave;
|
auto &leave = obj->leave;
|
||||||
|
|
||||||
std::thread t1 ([&] () { obj->inc (); });
|
cruft::thread::thread t1 ([&] () { obj->inc (); });
|
||||||
std::thread t2 ([&] () { obj->inc (); });
|
cruft::thread::thread t2 ([&] () { obj->inc (); });
|
||||||
|
|
||||||
tap.expect_eq (value, 0, "wrapped class was left initialised");
|
tap.expect_eq (value, 0, "wrapped class was left initialised");
|
||||||
|
|
||||||
|
12
test/thread/mutex.cpp
Normal file
12
test/thread/mutex.cpp
Normal 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 ();
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
#include "thread/semaphore.hpp"
|
#include "thread/semaphore.hpp"
|
||||||
#include "thread/flag.hpp"
|
#include "thread/flag.hpp"
|
||||||
#include "tap.hpp"
|
#include "thread/thread.hpp"
|
||||||
|
#include "thread/mutex.hpp"
|
||||||
|
|
||||||
#include <thread>
|
#include "tap.hpp"
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -35,7 +35,7 @@ main ()
|
|||||||
// the spawned thread attempts to double acquire a semaphore with
|
// the spawned thread attempts to double acquire a semaphore with
|
||||||
// only one available and so should get blocked immediately.
|
// only one available and so should get blocked immediately.
|
||||||
std::atomic<int> test = 0;
|
std::atomic<int> test = 0;
|
||||||
std::thread t ([&] () {
|
cruft::thread::thread t ([&] () {
|
||||||
sem.lock ();
|
sem.lock ();
|
||||||
sem.lock ();
|
sem.lock ();
|
||||||
test = 1;
|
test = 1;
|
||||||
@ -44,7 +44,7 @@ main ()
|
|||||||
// wait until we know the thread should have been blocked. it should
|
// wait until we know the thread should have been blocked. it should
|
||||||
// not have touched the 'test' variable at this point.
|
// not have touched the 'test' variable at this point.
|
||||||
while (sem.value () > 0)
|
while (sem.value () > 0)
|
||||||
std::this_thread::yield ();
|
cruft::thread::this_thread::yield ();
|
||||||
|
|
||||||
tap.expect_eq (test, 0, "locking blocks in foreign thread");
|
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;
|
constexpr int iterations = 1 << 16;
|
||||||
std::vector<std::thread> threads;
|
std::vector<cruft::thread::thread> threads;
|
||||||
|
|
||||||
cruft::thread::semaphore sem (0);
|
cruft::thread::semaphore sem (0);
|
||||||
|
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
#include "thread/flag.hpp"
|
#include "thread/flag.hpp"
|
||||||
#include "thread/spinlock.hpp"
|
#include "thread/spinlock.hpp"
|
||||||
|
#include "thread/thread.hpp"
|
||||||
|
|
||||||
#include "tap.hpp"
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
@ -37,14 +38,14 @@ main ()
|
|||||||
l.unlock ();
|
l.unlock ();
|
||||||
tap.expect (true, "unlocked without contention");
|
tap.expect (true, "unlocked without contention");
|
||||||
|
|
||||||
if (std::thread::hardware_concurrency () < 2) {
|
if (cruft::thread::thread::hardware_concurrency () < 2) {
|
||||||
tap.skip ("n-way fight");
|
tap.skip ("n-way fight");
|
||||||
} else {
|
} else {
|
||||||
constexpr int iterations = 1 << 12;
|
constexpr int iterations = 1 << 12;
|
||||||
cruft::thread::flag start_flag;
|
cruft::thread::flag start_flag;
|
||||||
|
|
||||||
std::vector<std::thread> contestants;
|
std::vector<cruft::thread::thread> contestants;
|
||||||
for (unsigned i = 0; i < std::thread::hardware_concurrency (); ++i)
|
for (unsigned i = 0; i < cruft::thread::thread::hardware_concurrency (); ++i)
|
||||||
contestants.emplace_back (fight, std::ref (start_flag), std::ref (l), iterations);
|
contestants.emplace_back (fight, std::ref (start_flag), std::ref (l), iterations);
|
||||||
|
|
||||||
start_flag.notify_all ();
|
start_flag.notify_all ();
|
||||||
@ -52,7 +53,7 @@ main ()
|
|||||||
for (auto &t: contestants)
|
for (auto &t: contestants)
|
||||||
t.join ();
|
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 ();
|
return tap.status ();
|
||||||
|
29
test/thread/thread.cpp
Normal file
29
test/thread/thread.cpp
Normal 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 ();
|
||||||
|
}
|
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
#include "thread/flag.hpp"
|
#include "thread/flag.hpp"
|
||||||
#include "thread/ticketlock.hpp"
|
#include "thread/ticketlock.hpp"
|
||||||
#include "tap.hpp"
|
#include "thread/thread.hpp"
|
||||||
|
#include "thread/mutex.hpp"
|
||||||
|
|
||||||
#include <thread>
|
#include "tap.hpp"
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -44,7 +44,7 @@ main ()
|
|||||||
l.unlock ();
|
l.unlock ();
|
||||||
tap.expect (true, "unlocked without contention");
|
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");
|
tap.skip ("reasonably fair under contention");
|
||||||
} else {
|
} else {
|
||||||
// measure fairness under contention is below some threshold
|
// measure fairness under contention is below some threshold
|
||||||
@ -63,8 +63,21 @@ main ()
|
|||||||
|
|
||||||
ffs_t::time_point a_finish, b_finish;
|
ffs_t::time_point a_finish, b_finish;
|
||||||
|
|
||||||
std::thread a (fight, std::ref (start_flag), std::ref (l), iterations, std::ref (a_finish));
|
cruft::thread::thread a (
|
||||||
std::thread b (fight, std::ref (start_flag), std::ref (l), iterations, std::ref (b_finish));
|
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 ();
|
auto start = std::chrono::high_resolution_clock::now ();
|
||||||
start_flag.notify_all ();
|
start_flag.notify_all ();
|
||||||
|
19
thread/condition_variable.hpp
Normal file
19
thread/condition_variable.hpp
Normal 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
|
17
thread/condition_variable_std.hpp
Normal file
17
thread/condition_variable_std.hpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
42
thread/condition_variable_win32.cpp
Normal file
42
thread/condition_variable_win32.cpp
Normal 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);
|
||||||
|
}
|
61
thread/condition_variable_win32.hpp
Normal file
61
thread/condition_variable_win32.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "condition_variable.hpp"
|
||||||
|
#include "mutex.hpp"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
|
|
||||||
namespace cruft::thread {
|
namespace cruft::thread {
|
||||||
@ -48,8 +49,8 @@ namespace cruft::thread {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<int> m_value;
|
std::atomic<int> m_value;
|
||||||
std::mutex m_mutex;
|
mutex m_mutex;
|
||||||
std::condition_variable m_cv;
|
condition_variable m_cv;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "mutex.hpp"
|
||||||
|
#include "condition_variable.hpp"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
namespace cruft::thread {
|
namespace cruft::thread {
|
||||||
/// a fire-once event that users can wait upon. if already fired then
|
/// a fire-once event that users can wait upon. if already fired then
|
||||||
@ -33,7 +34,7 @@ namespace cruft::thread {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<bool> fired;
|
std::atomic<bool> fired;
|
||||||
std::mutex m_mutex;
|
::cruft::thread::mutex m_mutex;
|
||||||
std::condition_variable m_condition;
|
::cruft::thread::condition_variable m_condition;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
4
thread/fwd.hpp
Normal file
4
thread/fwd.hpp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace cruft::thread {
|
||||||
|
}
|
@ -9,18 +9,18 @@
|
|||||||
#ifndef CRUFT_UTIL_THREAD_MONITOR_HPP
|
#ifndef CRUFT_UTIL_THREAD_MONITOR_HPP
|
||||||
#define CRUFT_UTIL_THREAD_MONITOR_HPP
|
#define CRUFT_UTIL_THREAD_MONITOR_HPP
|
||||||
|
|
||||||
#include <mutex>
|
#include "mutex.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
namespace cruft::thread {
|
namespace cruft::thread {
|
||||||
template <typename ValueT, typename MutexT = std::mutex>
|
template <typename ValueT, typename MutexT = cruft::thread::mutex>
|
||||||
class monitor {
|
class monitor {
|
||||||
public:
|
public:
|
||||||
template <typename ...Args>
|
template <typename ...Args>
|
||||||
monitor (Args &&...args):
|
monitor (Args &&...args):
|
||||||
m_value (std::forward<Args> (args)...)
|
m_value (::std::forward<Args> (args)...)
|
||||||
{ ; }
|
{ ; }
|
||||||
|
|
||||||
class proxy {
|
class proxy {
|
||||||
@ -36,7 +36,7 @@ namespace cruft::thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::lock_guard<MutexT> m_guard;
|
::std::lock_guard<MutexT> m_guard;
|
||||||
ValueT &m_value;
|
ValueT &m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
19
thread/mutex.hpp
Normal file
19
thread/mutex.hpp
Normal 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
17
thread/mutex_std.hpp
Normal 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
39
thread/mutex_win32.cpp
Normal 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
41
thread/mutex_win32.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@ -6,9 +6,6 @@
|
|||||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CRUFT_UTIL_THREAD_SEMAPHORE_HPP
|
|
||||||
#define CRUFT_UTIL_THREAD_SEMAPHORE_HPP
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
namespace cruft::thread {
|
namespace cruft::thread {
|
||||||
@ -37,8 +34,6 @@ namespace cruft::thread {
|
|||||||
int operator-- (void);
|
int operator-- (void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<int> m_value;
|
::std::atomic<int> m_value;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -42,6 +42,6 @@ namespace cruft::thread {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<LONG> m_value;
|
std::atomic<LONG> m_value;
|
||||||
win32::handle m_handle;
|
::cruft::win32::handle m_handle;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -37,10 +37,10 @@ namespace cruft::thread {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static_assert (
|
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"
|
"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
19
thread/thread.hpp
Normal 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
18
thread/thread_std.hpp
Normal 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
47
thread/thread_win32.cpp
Normal 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
144
thread/thread_win32.hpp
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -6,9 +6,6 @@
|
|||||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CRUFT_UTIL_THREAD_TICKETLOCK_HPP
|
|
||||||
#define CRUFT_UTIL_THREAD_TICKETLOCK_HPP
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
namespace cruft::thread {
|
namespace cruft::thread {
|
||||||
@ -20,9 +17,7 @@ namespace cruft::thread {
|
|||||||
void unlock (void);
|
void unlock (void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<unsigned> current;
|
::std::atomic<unsigned> current;
|
||||||
std::atomic<unsigned> next;
|
::std::atomic<unsigned> next;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -8,12 +8,13 @@
|
|||||||
|
|
||||||
#include "time.hpp"
|
#include "time.hpp"
|
||||||
|
|
||||||
#include <thread>
|
#include "thread/thread.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
void
|
void
|
||||||
cruft::sleep (uint64_t ns)
|
cruft::sleep (uint64_t ns)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for (std::chrono::nanoseconds (ns));
|
cruft::thread::this_thread::sleep_for (std::chrono::nanoseconds (ns));
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
namespace cruft::win32 {
|
namespace cruft::win32 {
|
||||||
struct handle {
|
struct handle {
|
||||||
|
using native_type = HANDLE;
|
||||||
|
|
||||||
handle ();
|
handle ();
|
||||||
explicit handle (posix::fd&&);
|
explicit handle (posix::fd&&);
|
||||||
explicit handle (HANDLE&&);
|
explicit handle (HANDLE&&);
|
||||||
|
Loading…
Reference in New Issue
Block a user