libcruft-util/thread/thread_win32.hpp

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);
}
};
}