158 lines
4.5 KiB
C++
158 lines
4.5 KiB
C++
/*
|
|
* 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 ClockT, class DurationT>
|
|
void
|
|
sleep_until (std::chrono::time_point<ClockT, DurationT> const& sleep_time)
|
|
{
|
|
if (ClockT::is_steady)
|
|
return sleep_for (sleep_time - ClockT::now ());
|
|
|
|
for (auto now = ClockT::now (); now < sleep_time; now = ClockT::now ())
|
|
sleep_for (sleep_time - now);
|
|
}
|
|
|
|
|
|
template <class Clock, class Duration>
|
|
void sleep_until (std::chrono::time_point<Clock,Duration> const& sleep_time);
|
|
}
|
|
};
|
|
}
|