зеркало из https://github.com/microsoft/STL.git
391 строка
11 KiB
C++
391 строка
11 KiB
C++
// thread standard header
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#pragma once
|
|
#ifndef _THREAD_
|
|
#define _THREAD_
|
|
#include <yvals_core.h>
|
|
#if _STL_COMPILER_PREPROCESSOR
|
|
#include <chrono>
|
|
#include <memory>
|
|
#include <process.h>
|
|
#include <tuple>
|
|
#include <xthreads.h>
|
|
#if _HAS_CXX20
|
|
#include <compare>
|
|
#include <stop_token>
|
|
#endif // _HAS_CXX20
|
|
|
|
#ifdef _M_CEE_PURE
|
|
#error <thread> is not supported when compiling with /clr:pure.
|
|
#endif // _M_CEE_PURE
|
|
|
|
#pragma pack(push, _CRT_PACKING)
|
|
#pragma warning(push, _STL_WARNING_LEVEL)
|
|
#pragma warning(disable : _STL_DISABLED_WARNINGS)
|
|
_STL_DISABLE_CLANG_WARNINGS
|
|
#pragma push_macro("new")
|
|
#undef new
|
|
|
|
_STD_BEGIN
|
|
#if _HAS_CXX20
|
|
class jthread;
|
|
#endif // _HAS_CXX20
|
|
|
|
class thread { // class for observing and managing threads
|
|
public:
|
|
class id;
|
|
|
|
using native_handle_type = void*;
|
|
|
|
thread() noexcept : _Thr{} {}
|
|
|
|
private:
|
|
#if _HAS_CXX20
|
|
friend jthread;
|
|
#endif // _HAS_CXX20
|
|
|
|
template <class _Tuple, size_t... _Indices>
|
|
static unsigned int __stdcall _Invoke(void* _RawVals) noexcept /* terminates */ {
|
|
// adapt invoke of user's callable object to _beginthreadex's thread procedure
|
|
const unique_ptr<_Tuple> _FnVals(static_cast<_Tuple*>(_RawVals));
|
|
_Tuple& _Tup = *_FnVals;
|
|
_STD invoke(_STD move(_STD get<_Indices>(_Tup))...);
|
|
_Cnd_do_broadcast_at_thread_exit(); // TRANSITION, ABI
|
|
return 0;
|
|
}
|
|
|
|
template <class _Tuple, size_t... _Indices>
|
|
_NODISCARD static constexpr auto _Get_invoke(index_sequence<_Indices...>) noexcept {
|
|
return &_Invoke<_Tuple, _Indices...>;
|
|
}
|
|
|
|
template <class _Fn, class... _Args>
|
|
void _Start(_Fn&& _Fx, _Args&&... _Ax) {
|
|
using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>;
|
|
auto _Decay_copied = _STD make_unique<_Tuple>(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
|
|
constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{});
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to
|
|
// extern C function under -EHc. Undefined behavior may occur
|
|
// if this function throws an exception. (/Wall)
|
|
_Thr._Hnd =
|
|
reinterpret_cast<void*>(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id));
|
|
#pragma warning(pop)
|
|
|
|
if (_Thr._Hnd) { // ownership transferred to the thread
|
|
(void) _Decay_copied.release();
|
|
} else { // failed to start thread
|
|
_Thr._Id = 0;
|
|
_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);
|
|
}
|
|
}
|
|
|
|
public:
|
|
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
|
|
_NODISCARD_CTOR explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
|
|
_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
|
|
}
|
|
|
|
~thread() noexcept {
|
|
if (joinable()) {
|
|
_STD terminate();
|
|
}
|
|
}
|
|
|
|
thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}
|
|
|
|
thread& operator=(thread&& _Other) noexcept {
|
|
if (joinable()) {
|
|
_STD terminate();
|
|
}
|
|
|
|
_Thr = _STD exchange(_Other._Thr, {});
|
|
return *this;
|
|
}
|
|
|
|
thread(const thread&) = delete;
|
|
thread& operator=(const thread&) = delete;
|
|
|
|
void swap(thread& _Other) noexcept {
|
|
_STD swap(_Thr, _Other._Thr);
|
|
}
|
|
|
|
_NODISCARD bool joinable() const noexcept {
|
|
return _Thr._Id != 0;
|
|
}
|
|
|
|
void join() {
|
|
if (!joinable()) {
|
|
_Throw_Cpp_error(_INVALID_ARGUMENT);
|
|
}
|
|
|
|
if (_Thr._Id == _Thrd_id()) {
|
|
_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);
|
|
}
|
|
|
|
if (_Thrd_join(_Thr, nullptr) != _Thrd_success) {
|
|
_Throw_Cpp_error(_NO_SUCH_PROCESS);
|
|
}
|
|
|
|
_Thr = {};
|
|
}
|
|
|
|
void detach() {
|
|
if (!joinable()) {
|
|
_Throw_Cpp_error(_INVALID_ARGUMENT);
|
|
}
|
|
|
|
_Check_C_return(_Thrd_detach(_Thr));
|
|
_Thr = {};
|
|
}
|
|
|
|
_NODISCARD id get_id() const noexcept;
|
|
|
|
_NODISCARD native_handle_type native_handle() noexcept /* strengthened */ { // return Win32 HANDLE as void *
|
|
return _Thr._Hnd;
|
|
}
|
|
|
|
_NODISCARD static unsigned int hardware_concurrency() noexcept {
|
|
return _Thrd_hardware_concurrency();
|
|
}
|
|
|
|
private:
|
|
_Thrd_t _Thr;
|
|
};
|
|
|
|
template <class _Rep, class _Period>
|
|
_NODISCARD auto _To_absolute_time(const chrono::duration<_Rep, _Period>& _Rel_time) noexcept {
|
|
constexpr auto _Zero = chrono::duration<_Rep, _Period>::zero();
|
|
const auto _Now = chrono::steady_clock::now();
|
|
decltype(_Now + _Rel_time) _Abs_time = _Now; // return common type
|
|
if (_Rel_time > _Zero) {
|
|
constexpr auto _Forever = (chrono::steady_clock::time_point::max) ();
|
|
if (_Abs_time < _Forever - _Rel_time) {
|
|
_Abs_time += _Rel_time;
|
|
} else {
|
|
_Abs_time = _Forever;
|
|
}
|
|
}
|
|
return _Abs_time;
|
|
}
|
|
|
|
namespace this_thread {
|
|
_NODISCARD thread::id get_id() noexcept;
|
|
|
|
inline void yield() noexcept {
|
|
_Thrd_yield();
|
|
}
|
|
|
|
inline void sleep_until(const xtime* _Abs_time) {
|
|
_Thrd_sleep(_Abs_time);
|
|
}
|
|
|
|
template <class _Clock, class _Duration>
|
|
void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) {
|
|
#if _HAS_CXX20
|
|
static_assert(chrono::is_clock_v<_Clock>, "Clock type required");
|
|
#endif // _HAS_CXX20
|
|
for (;;) {
|
|
const auto _Now = _Clock::now();
|
|
if (_Abs_time <= _Now) {
|
|
return;
|
|
}
|
|
|
|
_CSTD xtime _Tgt;
|
|
(void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now);
|
|
_Thrd_sleep(&_Tgt);
|
|
}
|
|
}
|
|
|
|
template <class _Rep, class _Period>
|
|
void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time) {
|
|
sleep_until(_To_absolute_time(_Rel_time));
|
|
}
|
|
} // namespace this_thread
|
|
|
|
class thread::id { // thread id
|
|
public:
|
|
id() noexcept : _Id(0) {} // id for no thread
|
|
|
|
private:
|
|
id(_Thrd_id_t _Other_id) : _Id(_Other_id) {}
|
|
|
|
_Thrd_id_t _Id;
|
|
|
|
friend thread::id thread::get_id() const noexcept;
|
|
friend thread::id this_thread::get_id() noexcept;
|
|
friend bool operator==(thread::id _Left, thread::id _Right) noexcept;
|
|
#if _HAS_CXX20
|
|
friend strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept;
|
|
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
|
friend bool operator<(thread::id _Left, thread::id _Right) noexcept;
|
|
#endif // !_HAS_CXX20
|
|
template <class _Ch, class _Tr>
|
|
friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id);
|
|
friend hash<thread::id>;
|
|
};
|
|
|
|
_NODISCARD inline thread::id thread::get_id() const noexcept {
|
|
return _Thr._Id;
|
|
}
|
|
|
|
_NODISCARD inline thread::id this_thread::get_id() noexcept {
|
|
return _Thrd_id();
|
|
}
|
|
|
|
inline void swap(thread& _Left, thread& _Right) noexcept {
|
|
_Left.swap(_Right);
|
|
}
|
|
|
|
_NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept {
|
|
return _Left._Id == _Right._Id;
|
|
}
|
|
|
|
#if _HAS_CXX20
|
|
_NODISCARD inline strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept {
|
|
return _Left._Id <=> _Right._Id;
|
|
}
|
|
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
|
_NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept {
|
|
return !(_Left == _Right);
|
|
}
|
|
|
|
_NODISCARD inline bool operator<(thread::id _Left, thread::id _Right) noexcept {
|
|
return _Left._Id < _Right._Id;
|
|
}
|
|
|
|
_NODISCARD inline bool operator<=(thread::id _Left, thread::id _Right) noexcept {
|
|
return !(_Right < _Left);
|
|
}
|
|
|
|
_NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept {
|
|
return _Right < _Left;
|
|
}
|
|
|
|
_NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept {
|
|
return !(_Left < _Right);
|
|
}
|
|
#endif // !_HAS_CXX20
|
|
|
|
template <class _Ch, class _Tr>
|
|
basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) {
|
|
return _Str << _Id._Id;
|
|
}
|
|
|
|
// STRUCT TEMPLATE SPECIALIZATION hash
|
|
template <>
|
|
struct hash<thread::id> {
|
|
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef thread::id _ARGUMENT_TYPE_NAME;
|
|
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef size_t _RESULT_TYPE_NAME;
|
|
|
|
_NODISCARD size_t operator()(const thread::id _Keyval) const noexcept {
|
|
return _Hash_representation(_Keyval._Id);
|
|
}
|
|
};
|
|
|
|
#if _HAS_CXX20
|
|
class jthread {
|
|
public:
|
|
using id = thread::id;
|
|
using native_handle_type = thread::native_handle_type;
|
|
|
|
jthread() noexcept : _Impl{}, _Ssource{nostopstate} {}
|
|
|
|
template <class _Fn, class... _Args, enable_if_t<!is_same_v<remove_cvref_t<_Fn>, jthread>, int> = 0>
|
|
_NODISCARD_CTOR explicit jthread(_Fn&& _Fx, _Args&&... _Ax) {
|
|
if constexpr (is_invocable_v<decay_t<_Fn>, stop_token, decay_t<_Args>...>) {
|
|
_Impl._Start(_STD forward<_Fn>(_Fx), _Ssource.get_token(), _STD forward<_Args>(_Ax)...);
|
|
} else {
|
|
_Impl._Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
|
|
}
|
|
}
|
|
|
|
~jthread() {
|
|
_Try_cancel_and_join();
|
|
}
|
|
|
|
jthread(const jthread&) = delete;
|
|
jthread(jthread&&) noexcept = default;
|
|
jthread& operator=(const jthread&) = delete;
|
|
|
|
jthread& operator=(jthread&& _Other) noexcept {
|
|
// note: the standard specifically disallows making self-move-assignment a no-op here
|
|
// N4861 [thread.jthread.cons]/13
|
|
// Effects: If joinable() is true, calls request_stop() and then join(). Assigns the state
|
|
// of x to *this and sets x to a default constructed state.
|
|
_Try_cancel_and_join();
|
|
_Impl = _STD move(_Other._Impl);
|
|
_Ssource = _STD move(_Other._Ssource);
|
|
return *this;
|
|
}
|
|
|
|
void swap(jthread& _Other) noexcept {
|
|
_Impl.swap(_Other._Impl);
|
|
_Ssource.swap(_Other._Ssource);
|
|
}
|
|
|
|
_NODISCARD bool joinable() const noexcept {
|
|
return _Impl.joinable();
|
|
}
|
|
|
|
void join() {
|
|
_Impl.join();
|
|
}
|
|
|
|
void detach() {
|
|
_Impl.detach();
|
|
}
|
|
|
|
_NODISCARD id get_id() const noexcept {
|
|
return _Impl.get_id();
|
|
}
|
|
|
|
_NODISCARD native_handle_type native_handle() noexcept /* strengthened */ {
|
|
return _Impl.native_handle();
|
|
}
|
|
|
|
_NODISCARD stop_source get_stop_source() noexcept {
|
|
return _Ssource;
|
|
}
|
|
|
|
_NODISCARD stop_token get_stop_token() const noexcept {
|
|
return _Ssource.get_token();
|
|
}
|
|
|
|
bool request_stop() noexcept {
|
|
return _Ssource.request_stop();
|
|
}
|
|
|
|
friend void swap(jthread& _Lhs, jthread& _Rhs) noexcept {
|
|
_Lhs.swap(_Rhs);
|
|
}
|
|
|
|
_NODISCARD static unsigned int hardware_concurrency() noexcept {
|
|
return thread::hardware_concurrency();
|
|
}
|
|
|
|
private:
|
|
void _Try_cancel_and_join() noexcept {
|
|
if (_Impl.joinable()) {
|
|
_Ssource.request_stop();
|
|
_Impl.join();
|
|
}
|
|
}
|
|
|
|
thread _Impl;
|
|
stop_source _Ssource;
|
|
};
|
|
#endif // _HAS_CXX20
|
|
_STD_END
|
|
|
|
#pragma pop_macro("new")
|
|
_STL_RESTORE_CLANG_WARNINGS
|
|
#pragma warning(pop)
|
|
#pragma pack(pop)
|
|
#endif // _STL_COMPILER_PREPROCESSOR
|
|
#endif // _THREAD_
|