зеркало из https://github.com/microsoft/STL.git
1482 строки
49 KiB
C++
1482 строки
49 KiB
C++
// future standard header
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef _FUTURE_
|
|
#define _FUTURE_
|
|
#include <yvals_core.h>
|
|
#if _STL_COMPILER_PREPROCESSOR
|
|
|
|
#ifdef _M_CEE_PURE
|
|
#error <future> is not supported when compiling with /clr:pure.
|
|
#endif // defined(_M_CEE_PURE)
|
|
|
|
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
|
|
#include <experimental/resumable>
|
|
#endif // _RESUMABLE_FUNCTIONS_SUPPORTED
|
|
|
|
#include <__msvc_chrono.hpp>
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <ppltasks.h>
|
|
#include <system_error>
|
|
#include <thread>
|
|
#include <utility>
|
|
|
|
#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
|
|
template <class _Alloc>
|
|
struct _Allocator_deleter {
|
|
_Alloc _Al;
|
|
|
|
using pointer = typename allocator_traits<_Alloc>::pointer;
|
|
void operator()(pointer _Ptr) noexcept { // delete the pointer
|
|
_STD _Destroy_in_place(*_Ptr);
|
|
_Al.deallocate(_Ptr, 1);
|
|
}
|
|
};
|
|
|
|
template <class _Alloc>
|
|
using _Unique_ptr_alloc = unique_ptr<typename _Alloc::value_type, _Allocator_deleter<_Alloc>>;
|
|
|
|
template <class _Alloc, class... _Args>
|
|
_NODISCARD _Unique_ptr_alloc<_Alloc> _Make_unique_alloc(_Alloc& _Al, _Args&&... _Vals) {
|
|
// construct an object with an allocator and return it owned by a unique_ptr
|
|
_Alloc_construct_ptr<_Alloc> _Constructor{_Al};
|
|
_Constructor._Allocate();
|
|
_STD _Construct_in_place(*_Constructor._Ptr, _STD forward<_Args>(_Vals)...);
|
|
return _Unique_ptr_alloc<_Alloc>(_Constructor._Release(), _Allocator_deleter<_Alloc>{_Al});
|
|
}
|
|
|
|
struct _From_raw_state_tag { // internal tag type for constructing a future from a raw state
|
|
explicit _From_raw_state_tag() = default;
|
|
};
|
|
|
|
_EXPORT_STD enum class future_errc { // names for futures errors
|
|
broken_promise = 1,
|
|
future_already_retrieved,
|
|
promise_already_satisfied,
|
|
no_state
|
|
};
|
|
|
|
_EXPORT_STD enum class launch { // names for launch options passed to async
|
|
async = 0x1,
|
|
deferred = 0x2
|
|
};
|
|
|
|
_BITMASK_OPS(_EXPORT_STD, launch)
|
|
|
|
_EXPORT_STD enum class future_status { // names for timed wait function returns
|
|
ready,
|
|
timeout,
|
|
deferred
|
|
};
|
|
|
|
template <>
|
|
struct is_error_code_enum<future_errc> : true_type {};
|
|
|
|
_EXPORT_STD _NODISCARD const error_category& future_category() noexcept;
|
|
|
|
_EXPORT_STD _NODISCARD inline error_code make_error_code(future_errc _Ec) noexcept {
|
|
return error_code(static_cast<int>(_Ec), _STD future_category());
|
|
}
|
|
|
|
_EXPORT_STD _NODISCARD inline error_condition make_error_condition(future_errc _Ec) noexcept {
|
|
return error_condition(static_cast<int>(_Ec), _STD future_category());
|
|
}
|
|
|
|
_NODISCARD inline const char* _Future_error_map(int _Errcode) noexcept { // convert to name of future error
|
|
switch (static_cast<future_errc>(_Errcode)) { // switch on error code value
|
|
case future_errc::broken_promise:
|
|
return "broken promise";
|
|
|
|
case future_errc::future_already_retrieved:
|
|
return "future already retrieved";
|
|
|
|
case future_errc::promise_already_satisfied:
|
|
return "promise already satisfied";
|
|
|
|
case future_errc::no_state:
|
|
return "no state";
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
_EXPORT_STD class future_error : public logic_error { // future exception
|
|
public:
|
|
explicit future_error(error_code _Errcode) // internal, TRANSITION, will be removed
|
|
: logic_error(""), _Mycode(_Errcode) {}
|
|
|
|
explicit future_error(future_errc _Ec) : logic_error(""), _Mycode(_STD make_error_code(_Ec)) {}
|
|
|
|
_NODISCARD const error_code& code() const noexcept {
|
|
return _Mycode;
|
|
}
|
|
|
|
_NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { // get message string
|
|
return _Future_error_map(_Mycode.value());
|
|
}
|
|
|
|
#if !_HAS_EXCEPTIONS
|
|
protected:
|
|
void _Doraise() const override { // perform class-specific exception handling
|
|
_RAISE(*this);
|
|
}
|
|
#endif // !_HAS_EXCEPTIONS
|
|
|
|
private:
|
|
error_code _Mycode; // the stored error code
|
|
};
|
|
|
|
// TRANSITION, ABI: remove `2` suffix
|
|
[[noreturn]] inline void _Throw_future_error2(const future_errc _Ec) {
|
|
_THROW(future_error{_Ec});
|
|
}
|
|
|
|
class _Future_error_category2 : public error_category { // categorize a future error
|
|
public:
|
|
constexpr _Future_error_category2() noexcept : error_category(_Future_addr) {}
|
|
|
|
const char* name() const noexcept override {
|
|
return "future";
|
|
}
|
|
|
|
string message(int _Errcode) const override {
|
|
const char* _Name = _Future_error_map(_Errcode);
|
|
if (_Name) {
|
|
return _Name;
|
|
}
|
|
|
|
return _Syserror_map(_Errcode);
|
|
}
|
|
};
|
|
|
|
_EXPORT_STD _NODISCARD inline const error_category& future_category() noexcept {
|
|
return _Immortalize_memcpy_image<_Future_error_category2>();
|
|
}
|
|
|
|
template <class _Ty>
|
|
class _Associated_state;
|
|
|
|
template <class _Ty>
|
|
struct __declspec(novtable) _Deleter_base { // abstract base class for managing deletion of state objects
|
|
virtual void _Delete(_Associated_state<_Ty>*) noexcept = 0;
|
|
virtual ~_Deleter_base() = default; // TRANSITION, vNext (make non-virtual, never called polymorphically)
|
|
};
|
|
|
|
template <class _Ty, class _Derived, class _Alloc>
|
|
struct _State_deleter : _Deleter_base<_Ty> { // manage allocator and deletion state objects
|
|
_State_deleter(const _Alloc& _Al) : _My_alloc(_Al) {}
|
|
|
|
_State_deleter(const _State_deleter&) = delete;
|
|
_State_deleter& operator=(const _State_deleter&) = delete;
|
|
|
|
void _Delete(_Associated_state<_Ty>* _State) noexcept override;
|
|
|
|
_Alloc _My_alloc;
|
|
};
|
|
|
|
template <class _Ty>
|
|
union _Result_holder {
|
|
_Result_holder() noexcept {}
|
|
~_Result_holder() noexcept {}
|
|
|
|
_Ty _Held_value;
|
|
};
|
|
|
|
template <class _Ty>
|
|
class _Associated_state { // class for managing associated synchronous state
|
|
public:
|
|
using _State_type = _Ty;
|
|
using _Mydel = _Deleter_base<_Ty>;
|
|
|
|
// TRANSITION, incorrectly default constructs _Result when _Ty is default constructible
|
|
_Associated_state(_Mydel* _Dp = nullptr)
|
|
: _Refs(1), // non-atomic initialization
|
|
_Result(), _Exception(), _Retrieved(false), _Ready(false), _Ready_at_thread_exit(false),
|
|
_Has_stored_result(false), _Running(false), _Deleter(_Dp) {}
|
|
|
|
virtual ~_Associated_state() noexcept {
|
|
if (_Already_has_stored_result() && !_Ready) { // registered for release at thread exit
|
|
_Cond._Unregister(_Mtx);
|
|
}
|
|
|
|
if constexpr (!is_default_constructible_v<_Ty>) {
|
|
if (_Has_stored_result) {
|
|
_Result._Held_value.~_Ty();
|
|
}
|
|
}
|
|
}
|
|
|
|
void _Retain() noexcept { // increment reference count
|
|
_MT_INCR(_Refs);
|
|
}
|
|
|
|
void _Release() noexcept { // decrement reference count and destroy when zero
|
|
if (_MT_DECR(_Refs) == 0) {
|
|
_Delete_this();
|
|
}
|
|
}
|
|
|
|
private:
|
|
_Atomic_counter_t _Refs;
|
|
|
|
public:
|
|
virtual void _Wait() { // wait for signal
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
_Maybe_run_deferred_function(_Lock);
|
|
while (!_Ready) {
|
|
_Cond.wait(_Lock);
|
|
}
|
|
}
|
|
|
|
struct _Test_ready { // wraps _Associated_state
|
|
_Test_ready(const _Associated_state* _St) : _State(_St) {}
|
|
|
|
bool operator()() const { // test state
|
|
return _State->_Ready != 0;
|
|
}
|
|
const _Associated_state* _State;
|
|
};
|
|
|
|
template <class _Rep, class _Per>
|
|
future_status _Wait_for(const chrono::duration<_Rep, _Per>& _Rel_time) { // wait for duration
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
if (_Has_deferred_function()) {
|
|
return future_status::deferred;
|
|
}
|
|
|
|
if (_Cond.wait_for(_Lock, _Rel_time, _Test_ready(this))) {
|
|
return future_status::ready;
|
|
}
|
|
|
|
return future_status::timeout;
|
|
}
|
|
|
|
template <class _Clock, class _Dur>
|
|
future_status _Wait_until(const chrono::time_point<_Clock, _Dur>& _Abs_time) { // wait until time point
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
if (_Has_deferred_function()) {
|
|
return future_status::deferred;
|
|
}
|
|
|
|
if (_Cond.wait_until(_Lock, _Abs_time, _Test_ready(this))) {
|
|
return future_status::ready;
|
|
}
|
|
|
|
return future_status::timeout;
|
|
}
|
|
|
|
virtual _Ty& _Get_value(bool _Get_only_once) {
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
if (_Get_only_once && _Retrieved) {
|
|
_Throw_future_error2(future_errc::future_already_retrieved);
|
|
}
|
|
|
|
if (_Exception) {
|
|
_STD rethrow_exception(_Exception);
|
|
}
|
|
|
|
// TRANSITION: `_Retrieved` should be assigned before `_Exception` is thrown so that a `future::get`
|
|
// that throws a stored exception invalidates the future (N4950 [futures.unique.future]/17)
|
|
_Retrieved = true;
|
|
_Maybe_run_deferred_function(_Lock);
|
|
while (!_Ready) {
|
|
_Cond.wait(_Lock);
|
|
}
|
|
|
|
if (_Exception) {
|
|
_STD rethrow_exception(_Exception);
|
|
}
|
|
|
|
if constexpr (is_default_constructible_v<_Ty>) {
|
|
return _Result;
|
|
} else {
|
|
return _Result._Held_value;
|
|
}
|
|
}
|
|
|
|
template <class _Ty2>
|
|
void _Emplace_result(_Ty2&& _Val) {
|
|
// TRANSITION, incorrectly assigns _Result when _Ty is default constructible
|
|
if constexpr (is_default_constructible_v<_Ty>) {
|
|
_Result = _STD forward<_Ty2>(_Val);
|
|
} else {
|
|
::new (static_cast<void*>(_STD addressof(_Result._Held_value))) _Ty(_STD forward<_Ty2>(_Val));
|
|
_Has_stored_result = true;
|
|
}
|
|
}
|
|
|
|
void _Set_value(const _Ty& _Val, bool _At_thread_exit) { // store a result
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
_Set_value_raw(_Val, &_Lock, _At_thread_exit);
|
|
}
|
|
|
|
void _Set_value_raw(const _Ty& _Val, unique_lock<mutex>* _Lock, bool _At_thread_exit) {
|
|
// store a result while inside a locked block
|
|
if (_Already_has_stored_result()) {
|
|
_Throw_future_error2(future_errc::promise_already_satisfied);
|
|
}
|
|
|
|
_Emplace_result(_Val);
|
|
_Do_notify(_Lock, _At_thread_exit);
|
|
}
|
|
|
|
void _Set_value(_Ty&& _Val, bool _At_thread_exit) { // store a result
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
_Set_value_raw(_STD forward<_Ty>(_Val), &_Lock, _At_thread_exit);
|
|
}
|
|
|
|
void _Set_value_raw(_Ty&& _Val, unique_lock<mutex>* _Lock, bool _At_thread_exit) {
|
|
// store a result while inside a locked block
|
|
if (_Already_has_stored_result()) {
|
|
_Throw_future_error2(future_errc::promise_already_satisfied);
|
|
}
|
|
|
|
_Emplace_result(_STD forward<_Ty>(_Val));
|
|
_Do_notify(_Lock, _At_thread_exit);
|
|
}
|
|
|
|
void _Set_exception(exception_ptr _Exc, bool _At_thread_exit) { // store a result
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
_Set_exception_raw(_Exc, &_Lock, _At_thread_exit);
|
|
}
|
|
|
|
void _Set_exception_raw(exception_ptr _Exc, unique_lock<mutex>* _Lock, bool _At_thread_exit) {
|
|
// store a result while inside a locked block
|
|
if (_Already_has_stored_result()) {
|
|
_Throw_future_error2(future_errc::promise_already_satisfied);
|
|
}
|
|
|
|
_STL_ASSERT(_Exc != nullptr, "promise<T>::set_exception/set_exception_at_thread_exit called with a null "
|
|
"std::exception_ptr, which is invalid per N4950 [futures.promise]/20");
|
|
_Exception = _Exc;
|
|
_Do_notify(_Lock, _At_thread_exit);
|
|
}
|
|
|
|
bool _Is_ready() const noexcept {
|
|
return _Ready != 0;
|
|
}
|
|
|
|
bool _Already_has_stored_result() const noexcept { // Has a result or an exception
|
|
if constexpr (is_default_constructible_v<_Ty>) {
|
|
return _Has_stored_result;
|
|
} else {
|
|
return _Has_stored_result || _Exception;
|
|
}
|
|
}
|
|
|
|
bool _Already_retrieved() const noexcept {
|
|
return _Retrieved;
|
|
}
|
|
|
|
void _Abandon() { // abandon shared state
|
|
unique_lock<mutex> _Lock(_Mtx);
|
|
if (!_Already_has_stored_result()) { // queue exception
|
|
future_error _Fut(_STD make_error_code(future_errc::broken_promise));
|
|
_Set_exception_raw(_STD make_exception_ptr(_Fut), &_Lock, false);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
void _Maybe_run_deferred_function(unique_lock<mutex>& _Lock) { // run a deferred function if not already done
|
|
if (!_Running) { // run the function
|
|
_Running = true;
|
|
_Run_deferred_function(_Lock);
|
|
}
|
|
}
|
|
|
|
public:
|
|
conditional_t<is_default_constructible_v<_Ty>, _Ty, _Result_holder<_Ty>> _Result;
|
|
exception_ptr _Exception;
|
|
mutex _Mtx;
|
|
condition_variable _Cond;
|
|
bool _Retrieved;
|
|
int _Ready;
|
|
bool _Ready_at_thread_exit; // TRANSITION, ABI
|
|
bool _Has_stored_result;
|
|
bool _Running;
|
|
|
|
private:
|
|
virtual bool _Has_deferred_function() const noexcept { // overridden by _Deferred_async_state
|
|
return false;
|
|
}
|
|
|
|
virtual void _Run_deferred_function(unique_lock<mutex>&) {} // do nothing
|
|
|
|
virtual void _Do_notify(unique_lock<mutex>* _Lock, bool _At_thread_exit) { // notify waiting threads
|
|
// TRANSITION, ABI: This is virtual, but never overridden.
|
|
if constexpr (is_default_constructible_v<_Ty>) {
|
|
_Has_stored_result = true;
|
|
}
|
|
|
|
if (_At_thread_exit) { // notify at thread exit
|
|
_Cond._Register(*_Lock, &_Ready);
|
|
} else { // notify immediately
|
|
_Ready = true;
|
|
_Cond.notify_all();
|
|
}
|
|
}
|
|
|
|
void _Delete_this() noexcept { // delete this object
|
|
if (_Deleter) {
|
|
_Deleter->_Delete(this);
|
|
} else {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
_Mydel* _Deleter;
|
|
|
|
public:
|
|
_Associated_state(const _Associated_state&) = delete;
|
|
_Associated_state& operator=(const _Associated_state&) = delete;
|
|
};
|
|
|
|
template <class _Ty, class _Derived, class _Alloc>
|
|
void _State_deleter<_Ty, _Derived, _Alloc>::_Delete(_Associated_state<_Ty>* _State) noexcept {
|
|
// delete _State and this using stored allocator
|
|
using _State_allocator = _Rebind_alloc_t<_Alloc, _Derived>;
|
|
_State_allocator _St_alloc(_My_alloc);
|
|
|
|
using _Deleter_allocator = _Rebind_alloc_t<_Alloc, _State_deleter>;
|
|
_Deleter_allocator _Del_alloc(_My_alloc);
|
|
|
|
_Derived* _Ptr = static_cast<_Derived*>(_State);
|
|
|
|
_STD _Delete_plain_internal(_St_alloc, _Ptr);
|
|
_STD _Delete_plain_internal(_Del_alloc, this);
|
|
}
|
|
|
|
template <class _Fret>
|
|
struct _P_arg_type { // type for functions returning T
|
|
using type = _Fret;
|
|
};
|
|
|
|
template <class _Fret>
|
|
struct _P_arg_type<_Fret&> { // type for functions returning reference to T
|
|
using type = _Fret*;
|
|
};
|
|
|
|
template <>
|
|
struct _P_arg_type<void> { // type for functions returning void
|
|
using type = int;
|
|
};
|
|
|
|
template <class>
|
|
class _Packaged_state;
|
|
|
|
// class for managing associated asynchronous state for packaged_task
|
|
template <class _Ret, class... _ArgTypes>
|
|
class _Packaged_state<_Ret(_ArgTypes...)> : public _Associated_state<typename _P_arg_type<_Ret>::type> {
|
|
public:
|
|
using _Mybase = _Associated_state<typename _P_arg_type<_Ret>::type>;
|
|
using _Mydel = typename _Mybase::_Mydel;
|
|
using _Function_type = function<_Ret(_ArgTypes...)>; // TRANSITION, ABI, should not use std::function
|
|
|
|
explicit _Packaged_state(const _Function_type& _Fnarg) : _Fn(_Fnarg) {}
|
|
|
|
explicit _Packaged_state(_Function_type&& _Fnarg) noexcept : _Fn(_STD move(_Fnarg)) {}
|
|
|
|
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0>
|
|
explicit _Packaged_state(_Fty2&& _Fnarg) : _Fn(_Secret_copyability_ignoring_tag{}, _STD forward<_Fty2>(_Fnarg)) {}
|
|
|
|
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
template <class _Alloc>
|
|
_Packaged_state(const _Function_type& _Fnarg, const _Alloc& _Al, _Mydel* _Dp)
|
|
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _Fnarg) {}
|
|
|
|
template <class _Alloc>
|
|
_Packaged_state(_Function_type&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp)
|
|
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD move(_Fnarg)) {}
|
|
|
|
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0>
|
|
_Packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp)
|
|
: _Mybase(_Dp), _Fn(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {}
|
|
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
|
|
void _Call_deferred(_ArgTypes... _Args) { // set deferred call
|
|
_TRY_BEGIN
|
|
// call function object and catch exceptions
|
|
if constexpr (is_same_v<_Ret, void>) {
|
|
_Fn(_STD forward<_ArgTypes>(_Args)...);
|
|
this->_Set_value(1, true);
|
|
} else if constexpr (is_lvalue_reference_v<_Ret>) {
|
|
this->_Set_value(_STD addressof(_Fn(_STD forward<_ArgTypes>(_Args)...)), true);
|
|
} else {
|
|
this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), true);
|
|
}
|
|
_CATCH_ALL
|
|
// function object threw exception; record result
|
|
this->_Set_exception(_STD current_exception(), true);
|
|
_CATCH_END
|
|
}
|
|
|
|
void _Call_immediate(_ArgTypes... _Args) { // call function object
|
|
_TRY_BEGIN
|
|
// call function object and catch exceptions
|
|
if constexpr (is_same_v<_Ret, void>) {
|
|
_Fn(_STD forward<_ArgTypes>(_Args)...);
|
|
this->_Set_value(1, false);
|
|
} else if constexpr (is_lvalue_reference_v<_Ret>) {
|
|
this->_Set_value(_STD addressof(_Fn(_STD forward<_ArgTypes>(_Args)...)), false);
|
|
} else {
|
|
this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false);
|
|
}
|
|
_CATCH_ALL
|
|
// function object threw exception; record result
|
|
this->_Set_exception(_STD current_exception(), false);
|
|
_CATCH_END
|
|
}
|
|
|
|
const auto& _Get_fn() const& {
|
|
return _Fn;
|
|
}
|
|
auto&& _Get_fn() && noexcept {
|
|
return _STD move(_Fn);
|
|
}
|
|
|
|
private:
|
|
_Function_type _Fn;
|
|
};
|
|
|
|
template <class _Ty, class _Alloc>
|
|
_Associated_state<_Ty>* _Make_associated_state(const _Alloc& _Al) {
|
|
// construct an _Associated_state object with an allocator
|
|
using _Delty = _State_deleter<_Ty, _Associated_state<_Ty>, _Alloc>;
|
|
using _Aldelty = _Rebind_alloc_t<_Alloc, _Delty>;
|
|
using _Alstate = _Rebind_alloc_t<_Alloc, _Associated_state<_Ty>>;
|
|
|
|
_Aldelty _Del_alloc(_Al);
|
|
_Alstate _State_alloc(_Al);
|
|
auto _Del = _STD _Make_unique_alloc(_Del_alloc, _Al);
|
|
auto _Res = _STD _Make_unique_alloc(_State_alloc, _STD _Unfancy(_Del.get()));
|
|
(void) _Del.release(); // ownership of _Del.get() now transferred to _Res
|
|
return _STD _Unfancy(_Res.release()); // ownership transferred to caller
|
|
}
|
|
|
|
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
template <class _Pack_state, class _Fty2, class _Alloc>
|
|
_Pack_state* _Make_packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al) {
|
|
// construct a _Packaged_state object with an allocator from an rvalue function object
|
|
using _Delty = _State_deleter<typename _Pack_state::_Mybase::_State_type, _Pack_state, _Alloc>;
|
|
using _Aldelty = _Rebind_alloc_t<_Alloc, _Delty>;
|
|
using _Alstate = _Rebind_alloc_t<_Alloc, _Pack_state>;
|
|
|
|
_Aldelty _Del_alloc(_Al);
|
|
_Alstate _State_alloc(_Al);
|
|
auto _Del = _STD _Make_unique_alloc(_Del_alloc, _Al);
|
|
auto _Res = _STD _Make_unique_alloc(_State_alloc, _STD forward<_Fty2>(_Fnarg), _Al, _STD _Unfancy(_Del.get()));
|
|
(void) _Del.release(); // ownership of _Del.get() now transferred to _Res
|
|
return _STD _Unfancy(_Res.release()); // ownership transferred to caller
|
|
}
|
|
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
|
|
template <class _Rx>
|
|
class _Deferred_async_state : public _Packaged_state<_Rx()> {
|
|
// class for managing associated synchronous state for deferred execution from async
|
|
public:
|
|
template <class _Fty2>
|
|
_Deferred_async_state(const _Fty2& _Fnarg) : _Packaged_state<_Rx()>(_Fnarg) {}
|
|
|
|
template <class _Fty2>
|
|
_Deferred_async_state(_Fty2&& _Fnarg) : _Packaged_state<_Rx()>(_STD forward<_Fty2>(_Fnarg)) {}
|
|
|
|
private:
|
|
bool _Has_deferred_function() const noexcept override {
|
|
// this function is considered to be deferred until it's invoked
|
|
return !this->_Running;
|
|
}
|
|
|
|
void _Run_deferred_function(unique_lock<mutex>& _Lock) override { // run the deferred function
|
|
_Lock.unlock();
|
|
_Packaged_state<_Rx()>::_Call_immediate();
|
|
_Lock.lock();
|
|
}
|
|
};
|
|
|
|
template <class _Rx>
|
|
class _Task_async_state : public _Packaged_state<_Rx()> {
|
|
// class for managing associated synchronous state for asynchronous execution from async
|
|
public:
|
|
using _Mybase = _Packaged_state<_Rx()>;
|
|
using _State_type = typename _Mybase::_State_type;
|
|
|
|
template <class _Fty2>
|
|
_Task_async_state(_Fty2&& _Fnarg) : _Mybase(_STD forward<_Fty2>(_Fnarg)) {
|
|
_Task = ::Concurrency::create_task([this]() { // do it now
|
|
this->_Call_immediate();
|
|
});
|
|
|
|
this->_Running = true;
|
|
}
|
|
|
|
~_Task_async_state() noexcept override {
|
|
_Wait();
|
|
}
|
|
|
|
void _Wait() override { // wait for completion
|
|
_Task.wait();
|
|
}
|
|
|
|
_State_type& _Get_value(bool _Get_only_once) override {
|
|
// return the stored result or throw stored exception
|
|
_Task.wait();
|
|
return _Mybase::_Get_value(_Get_only_once);
|
|
}
|
|
|
|
private:
|
|
::Concurrency::task<void> _Task;
|
|
};
|
|
|
|
template <class _Ty>
|
|
class _State_manager {
|
|
// class for managing possibly non-existent associated asynchronous state object
|
|
public:
|
|
_State_manager() = default; // construct with no associated asynchronous state object
|
|
|
|
_State_manager(_Associated_state<_Ty>* _New_state, bool _Get_once) noexcept
|
|
: _Assoc_state(_New_state), _Get_only_once(_Get_once) {} // construct with _New_state
|
|
|
|
_State_manager(const _State_manager& _Other, bool _Get_once = false) noexcept
|
|
: _Assoc_state(_Other._Assoc_state), _Get_only_once(_Get_once) {
|
|
if (_Assoc_state) { // do the copy
|
|
_Assoc_state->_Retain();
|
|
}
|
|
}
|
|
|
|
_State_manager(_State_manager&& _Other, bool _Get_once = false) noexcept
|
|
: _Assoc_state(_STD exchange(_Other._Assoc_state, nullptr)), _Get_only_once(_Get_once) {}
|
|
|
|
~_State_manager() noexcept {
|
|
if (_Assoc_state) {
|
|
_Assoc_state->_Release();
|
|
}
|
|
}
|
|
|
|
_State_manager& operator=(const _State_manager& _Other) noexcept {
|
|
// copy stored associated asynchronous state object from _Other
|
|
if (_Other._Assoc_state) {
|
|
_Other._Assoc_state->_Retain();
|
|
}
|
|
|
|
if (_Assoc_state) {
|
|
_Assoc_state->_Release();
|
|
}
|
|
|
|
_Assoc_state = _Other._Assoc_state;
|
|
_Get_only_once = _Other._Get_only_once;
|
|
return *this;
|
|
}
|
|
|
|
_State_manager& operator=(_State_manager&& _Other) noexcept {
|
|
// move stored associated asynchronous state object from _Other
|
|
if (this != _STD addressof(_Other)) {
|
|
if (_Assoc_state) {
|
|
_Assoc_state->_Release();
|
|
}
|
|
|
|
_Assoc_state = _STD exchange(_Other._Assoc_state, nullptr);
|
|
_Get_only_once = _Other._Get_only_once;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
_NODISCARD bool valid() const noexcept {
|
|
return _Assoc_state && !(_Get_only_once && _Assoc_state->_Already_retrieved());
|
|
}
|
|
|
|
void wait() const { // wait for signal
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
_Assoc_state->_Wait();
|
|
}
|
|
|
|
template <class _Rep, class _Per>
|
|
future_status wait_for(const chrono::duration<_Rep, _Per>& _Rel_time) const { // wait for duration
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
return _Assoc_state->_Wait_for(_Rel_time);
|
|
}
|
|
|
|
template <class _Clock, class _Dur>
|
|
future_status wait_until(const chrono::time_point<_Clock, _Dur>& _Abs_time) const { // wait until time point
|
|
static_assert(chrono::_Is_clock_v<_Clock>, "Clock type required");
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
return _Assoc_state->_Wait_until(_Abs_time);
|
|
}
|
|
|
|
_Ty& _Get_value() const {
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
return _Assoc_state->_Get_value(_Get_only_once);
|
|
}
|
|
|
|
void _Set_value(const _Ty& _Val, bool _Defer) { // store a result
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
_Assoc_state->_Set_value(_Val, _Defer);
|
|
}
|
|
|
|
void _Set_value(_Ty&& _Val, bool _Defer) { // store a result
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
_Assoc_state->_Set_value(_STD forward<_Ty>(_Val), _Defer);
|
|
}
|
|
|
|
void _Abandon() { // abandon shared state
|
|
if (_Assoc_state) {
|
|
_Assoc_state->_Abandon();
|
|
}
|
|
}
|
|
|
|
void _Set_exception(exception_ptr _Exc, bool _Defer) { // store a result
|
|
if (!valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
_Assoc_state->_Set_exception(_Exc, _Defer);
|
|
}
|
|
|
|
void _Swap(_State_manager& _Other) noexcept { // exchange with _Other
|
|
_STD swap(_Assoc_state, _Other._Assoc_state);
|
|
}
|
|
|
|
_Associated_state<_Ty>* _Ptr() const noexcept {
|
|
return _Assoc_state;
|
|
}
|
|
|
|
bool _Is_ready() const noexcept {
|
|
return _Assoc_state && _Assoc_state->_Is_ready();
|
|
}
|
|
|
|
private:
|
|
_Associated_state<_Ty>* _Assoc_state = nullptr;
|
|
bool _Get_only_once = false;
|
|
};
|
|
|
|
_EXPORT_STD template <class _Ty>
|
|
class shared_future;
|
|
|
|
_EXPORT_STD template <class _Ty>
|
|
class future : public _State_manager<_Ty> {
|
|
// class that defines a non-copyable asynchronous return object that holds a value
|
|
private:
|
|
using _Mybase = _State_manager<_Ty>;
|
|
|
|
public:
|
|
static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>,
|
|
"T in future<T> must meet the Cpp17Destructible requirements (N4950 [futures.unique.future]/4).");
|
|
|
|
future() = default;
|
|
|
|
future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {}
|
|
|
|
future& operator=(future&&) = default;
|
|
|
|
future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {}
|
|
|
|
_Ty get() {
|
|
// block until ready then return the stored result or throw the stored exception
|
|
future _Local{_STD move(*this)};
|
|
return _STD move(_Local._Get_value());
|
|
}
|
|
|
|
_NODISCARD shared_future<_Ty> share() noexcept {
|
|
return shared_future<_Ty>(_STD move(*this));
|
|
}
|
|
|
|
future(const future&) = delete;
|
|
future& operator=(const future&) = delete;
|
|
};
|
|
|
|
template <class _Ty>
|
|
class future<_Ty&> : public _State_manager<_Ty*> {
|
|
// class that defines a non-copyable asynchronous return object that holds a reference
|
|
private:
|
|
using _Mybase = _State_manager<_Ty*>;
|
|
|
|
public:
|
|
future() = default;
|
|
|
|
future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {}
|
|
|
|
future& operator=(future&&) = default;
|
|
|
|
future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {}
|
|
|
|
_Ty& get() {
|
|
// block until ready then return the stored result or throw the stored exception
|
|
future _Local{_STD move(*this)};
|
|
return *_Local._Get_value();
|
|
}
|
|
|
|
_NODISCARD shared_future<_Ty&> share() noexcept {
|
|
return shared_future<_Ty&>(_STD move(*this));
|
|
}
|
|
|
|
future(const future&) = delete;
|
|
future& operator=(const future&) = delete;
|
|
};
|
|
|
|
template <>
|
|
class future<void> : public _State_manager<int> {
|
|
// class that defines a non-copyable asynchronous return object that does not hold a value
|
|
private:
|
|
using _Mybase = _State_manager<int>;
|
|
|
|
public:
|
|
future() = default;
|
|
|
|
future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {}
|
|
|
|
future& operator=(future&&) = default;
|
|
|
|
future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {}
|
|
|
|
void get() {
|
|
// block until ready then return or throw the stored exception
|
|
future _Local{_STD move(*this)};
|
|
_Local._Get_value();
|
|
}
|
|
|
|
_NODISCARD shared_future<void> share() noexcept;
|
|
|
|
future(const future&) = delete;
|
|
future& operator=(const future&) = delete;
|
|
};
|
|
|
|
_EXPORT_STD template <class _Ty>
|
|
class shared_future : public _State_manager<_Ty> {
|
|
// class that defines a copyable asynchronous return object that holds a value
|
|
private:
|
|
using _Mybase = _State_manager<_Ty>;
|
|
|
|
public:
|
|
static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>,
|
|
"T in shared_future<T> must meet the Cpp17Destructible requirements (N4950 [futures.shared.future]/4).");
|
|
|
|
shared_future() = default;
|
|
|
|
shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) {}
|
|
|
|
shared_future& operator=(const shared_future&) = default;
|
|
|
|
shared_future(future<_Ty>&& _Other) noexcept : _Mybase(static_cast<_Mybase&&>(_Other)) {}
|
|
|
|
shared_future(shared_future&& _Other) noexcept : _Mybase(_STD move(_Other)) {}
|
|
|
|
shared_future& operator=(shared_future&&) = default;
|
|
|
|
const _Ty& get() const {
|
|
// block until ready then return the stored result or throw the stored exception
|
|
return this->_Get_value();
|
|
}
|
|
};
|
|
|
|
template <class _Ty>
|
|
class shared_future<_Ty&> : public _State_manager<_Ty*> {
|
|
// class that defines a copyable asynchronous return object that holds a reference
|
|
private:
|
|
using _Mybase = _State_manager<_Ty*>;
|
|
|
|
public:
|
|
shared_future() = default;
|
|
|
|
shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) {}
|
|
|
|
shared_future& operator=(const shared_future&) = default;
|
|
|
|
shared_future(future<_Ty&>&& _Other) noexcept : _Mybase(static_cast<_Mybase&&>(_Other)) {}
|
|
|
|
shared_future(shared_future&& _Other) noexcept : _Mybase(_STD move(_Other)) {}
|
|
|
|
shared_future& operator=(shared_future&&) = default;
|
|
|
|
_Ty& get() const {
|
|
// block until ready then return the stored result or throw the stored exception
|
|
return *this->_Get_value();
|
|
}
|
|
};
|
|
|
|
template <>
|
|
class shared_future<void> : public _State_manager<int> {
|
|
// class that defines a copyable asynchronous return object that does not hold a value
|
|
private:
|
|
using _Mybase = _State_manager<int>;
|
|
|
|
public:
|
|
shared_future() noexcept {}
|
|
|
|
shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) {}
|
|
|
|
shared_future& operator=(const shared_future&) = default;
|
|
|
|
shared_future(shared_future&& _Other) noexcept : _Mybase(_STD move(_Other)) {}
|
|
|
|
shared_future(future<void>&& _Other) noexcept : _Mybase(static_cast<_Mybase&&>(_Other)) {}
|
|
|
|
shared_future& operator=(shared_future&&) = default;
|
|
|
|
void get() const { // block until ready then return or throw the stored exception
|
|
this->_Get_value();
|
|
}
|
|
};
|
|
|
|
_NODISCARD inline shared_future<void> future<void>::share() noexcept {
|
|
return shared_future<void>(_STD move(*this));
|
|
}
|
|
|
|
template <class _Ty>
|
|
class _Promise {
|
|
public:
|
|
_Promise(_Associated_state<_Ty>* _State_ptr) noexcept : _State(_State_ptr, false), _Future_retrieved(false) {}
|
|
|
|
_Promise(_Promise&&) = default;
|
|
|
|
_Promise& operator=(_Promise&&) = default;
|
|
|
|
void _Swap(_Promise& _Other) noexcept {
|
|
_State._Swap(_Other._State);
|
|
_STD swap(_Future_retrieved, _Other._Future_retrieved);
|
|
}
|
|
|
|
const _State_manager<_Ty>& _Get_state() const noexcept {
|
|
return _State;
|
|
}
|
|
_State_manager<_Ty>& _Get_state() noexcept {
|
|
return _State;
|
|
}
|
|
|
|
_State_manager<_Ty>& _Get_state_for_set() {
|
|
if (!_State.valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
return _State;
|
|
}
|
|
|
|
_State_manager<_Ty>& _Get_state_for_future() {
|
|
if (!_State.valid()) {
|
|
_Throw_future_error2(future_errc::no_state);
|
|
}
|
|
|
|
if (_Future_retrieved) {
|
|
_Throw_future_error2(future_errc::future_already_retrieved);
|
|
}
|
|
|
|
_Future_retrieved = true;
|
|
return _State;
|
|
}
|
|
|
|
bool _Is_valid() const noexcept {
|
|
return _State.valid();
|
|
}
|
|
|
|
bool _Is_ready() const noexcept {
|
|
return _State._Is_ready();
|
|
}
|
|
|
|
_Promise(const _Promise&) = delete;
|
|
_Promise& operator=(const _Promise&) = delete;
|
|
|
|
private:
|
|
_State_manager<_Ty> _State;
|
|
bool _Future_retrieved;
|
|
};
|
|
|
|
_EXPORT_STD template <class _Ty>
|
|
class promise { // class that defines an asynchronous provider that holds a value
|
|
public:
|
|
static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>,
|
|
"T in promise<T> must meet the Cpp17Destructible requirements (N4950 [futures.promise]/1).");
|
|
|
|
promise() : _MyPromise(new _Associated_state<_Ty>) {}
|
|
|
|
template <class _Alloc>
|
|
promise(allocator_arg_t, const _Alloc& _Al) : _MyPromise(_Make_associated_state<_Ty>(_Al)) {}
|
|
|
|
promise(promise&&) = default;
|
|
|
|
promise& operator=(promise&& _Other) noexcept {
|
|
promise(_STD move(_Other)).swap(*this);
|
|
return *this;
|
|
}
|
|
|
|
~promise() noexcept {
|
|
if (_MyPromise._Is_valid() && !_MyPromise._Is_ready()) {
|
|
// exception if destroyed before function object returns
|
|
future_error _Fut(_STD make_error_code(future_errc::broken_promise));
|
|
_MyPromise._Get_state()._Set_exception(_STD make_exception_ptr(_Fut), false);
|
|
}
|
|
}
|
|
|
|
void swap(promise& _Other) noexcept {
|
|
_MyPromise._Swap(_Other._MyPromise);
|
|
}
|
|
|
|
_NODISCARD_GET_FUTURE future<_Ty> get_future() {
|
|
return future<_Ty>(_From_raw_state_tag{}, _MyPromise._Get_state_for_future());
|
|
}
|
|
|
|
void set_value(const _Ty& _Val) {
|
|
_MyPromise._Get_state_for_set()._Set_value(_Val, false);
|
|
}
|
|
|
|
void set_value_at_thread_exit(const _Ty& _Val) {
|
|
_MyPromise._Get_state_for_set()._Set_value(_Val, true);
|
|
}
|
|
|
|
void set_value(_Ty&& _Val) {
|
|
_MyPromise._Get_state_for_set()._Set_value(_STD forward<_Ty>(_Val), false);
|
|
}
|
|
|
|
void set_value_at_thread_exit(_Ty&& _Val) {
|
|
_MyPromise._Get_state_for_set()._Set_value(_STD forward<_Ty>(_Val), true);
|
|
}
|
|
|
|
void set_exception(exception_ptr _Exc) {
|
|
_MyPromise._Get_state_for_set()._Set_exception(_Exc, false);
|
|
}
|
|
|
|
void set_exception_at_thread_exit(exception_ptr _Exc) {
|
|
_MyPromise._Get_state_for_set()._Set_exception(_Exc, true);
|
|
}
|
|
|
|
promise(const promise&) = delete;
|
|
promise& operator=(const promise&) = delete;
|
|
|
|
private:
|
|
_Promise<_Ty> _MyPromise;
|
|
};
|
|
|
|
template <class _Ty>
|
|
class promise<_Ty&> { // class that defines an asynchronous provider that holds a reference
|
|
public:
|
|
promise() : _MyPromise(new _Associated_state<_Ty*>) {}
|
|
|
|
template <class _Alloc>
|
|
promise(allocator_arg_t, const _Alloc& _Al) : _MyPromise(_Make_associated_state<_Ty*>(_Al)) {}
|
|
|
|
promise(promise&&) = default;
|
|
|
|
promise& operator=(promise&& _Other) noexcept {
|
|
promise(_STD move(_Other)).swap(*this);
|
|
return *this;
|
|
}
|
|
|
|
~promise() noexcept {
|
|
if (_MyPromise._Is_valid() && !_MyPromise._Is_ready()) {
|
|
// exception if destroyed before function object returns
|
|
future_error _Fut(_STD make_error_code(future_errc::broken_promise));
|
|
_MyPromise._Get_state()._Set_exception(_STD make_exception_ptr(_Fut), false);
|
|
}
|
|
}
|
|
|
|
void swap(promise& _Other) noexcept {
|
|
_MyPromise._Swap(_Other._MyPromise);
|
|
}
|
|
|
|
_NODISCARD_GET_FUTURE future<_Ty&> get_future() {
|
|
return future<_Ty&>(_From_raw_state_tag{}, _MyPromise._Get_state_for_future());
|
|
}
|
|
|
|
void set_value(_Ty& _Val) {
|
|
_MyPromise._Get_state_for_set()._Set_value(_STD addressof(_Val), false);
|
|
}
|
|
|
|
void set_value_at_thread_exit(_Ty& _Val) {
|
|
_MyPromise._Get_state_for_set()._Set_value(_STD addressof(_Val), true);
|
|
}
|
|
|
|
void set_exception(exception_ptr _Exc) {
|
|
_MyPromise._Get_state_for_set()._Set_exception(_Exc, false);
|
|
}
|
|
|
|
void set_exception_at_thread_exit(exception_ptr _Exc) {
|
|
_MyPromise._Get_state_for_set()._Set_exception(_Exc, true);
|
|
}
|
|
|
|
promise(const promise&) = delete;
|
|
promise& operator=(const promise&) = delete;
|
|
|
|
private:
|
|
_Promise<_Ty*> _MyPromise;
|
|
};
|
|
|
|
template <>
|
|
class promise<void> { // defines an asynchronous provider that does not hold a value
|
|
public:
|
|
promise() : _MyPromise(new _Associated_state<int>) {}
|
|
|
|
template <class _Alloc>
|
|
promise(allocator_arg_t, const _Alloc& _Al) : _MyPromise(_Make_associated_state<int>(_Al)) {}
|
|
|
|
promise(promise&&) = default;
|
|
|
|
promise& operator=(promise&& _Other) noexcept {
|
|
promise(_STD move(_Other)).swap(*this);
|
|
return *this;
|
|
}
|
|
|
|
~promise() noexcept {
|
|
if (_MyPromise._Is_valid() && !_MyPromise._Is_ready()) {
|
|
// exception if destroyed before function object returns
|
|
future_error _Fut(_STD make_error_code(future_errc::broken_promise));
|
|
_MyPromise._Get_state()._Set_exception(_STD make_exception_ptr(_Fut), false);
|
|
}
|
|
}
|
|
|
|
void swap(promise& _Other) noexcept {
|
|
_MyPromise._Swap(_Other._MyPromise);
|
|
}
|
|
|
|
_NODISCARD_GET_FUTURE future<void> get_future() {
|
|
return future<void>(_From_raw_state_tag{}, _MyPromise._Get_state_for_future());
|
|
}
|
|
|
|
void set_value() {
|
|
_MyPromise._Get_state_for_set()._Set_value(1, false);
|
|
}
|
|
|
|
void set_value_at_thread_exit() {
|
|
_MyPromise._Get_state_for_set()._Set_value(1, true);
|
|
}
|
|
|
|
void set_exception(exception_ptr _Exc) {
|
|
_MyPromise._Get_state_for_set()._Set_exception(_Exc, false);
|
|
}
|
|
|
|
void set_exception_at_thread_exit(exception_ptr _Exc) {
|
|
_MyPromise._Get_state_for_set()._Set_exception(_Exc, true);
|
|
}
|
|
|
|
promise(const promise&) = delete;
|
|
promise& operator=(const promise&) = delete;
|
|
|
|
private:
|
|
_Promise<int> _MyPromise;
|
|
};
|
|
|
|
template <class _Ty, class _Alloc>
|
|
struct uses_allocator<promise<_Ty>, _Alloc> : true_type {};
|
|
|
|
_EXPORT_STD template <class _Ty>
|
|
void swap(promise<_Ty>& _Left, promise<_Ty>& _Right) noexcept {
|
|
_Left.swap(_Right);
|
|
}
|
|
|
|
_EXPORT_STD template <class>
|
|
class packaged_task; // not defined
|
|
|
|
template <class _Ret, class... _ArgTypes>
|
|
class packaged_task<_Ret(_ArgTypes...)> {
|
|
// class that defines an asynchronous provider that returns the result of a call to a function object
|
|
private:
|
|
using _Ptype = typename _P_arg_type<_Ret>::type;
|
|
using _MyPromiseType = _Promise<_Ptype>;
|
|
using _MyStateManagerType = _State_manager<_Ptype>;
|
|
using _MyStateType = _Packaged_state<_Ret(_ArgTypes...)>;
|
|
|
|
public:
|
|
packaged_task() = default;
|
|
|
|
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0>
|
|
explicit packaged_task(_Fty2&& _Fnarg) : _MyPromise(new _MyStateType(_STD forward<_Fty2>(_Fnarg))) {
|
|
static_assert(_Is_invocable_r<_Ret, decay_t<_Fty2>&, _ArgTypes...>::value, // per LWG-4154
|
|
"The function object must be callable with _ArgTypes... and return _Ret (N4988 [futures.task.members]/3).");
|
|
}
|
|
|
|
packaged_task(packaged_task&&) noexcept = default;
|
|
|
|
packaged_task& operator=(packaged_task&&) noexcept = default;
|
|
|
|
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0>
|
|
packaged_task(allocator_arg_t, const _Alloc& _Al, _Fty2&& _Fnarg)
|
|
: _MyPromise(_STD _Make_packaged_state<_MyStateType>(_STD forward<_Fty2>(_Fnarg), _Al)) {
|
|
static_assert(_Is_invocable_r<_Ret, decay_t<_Fty2>&, _ArgTypes...>::value, // per LWG-4154
|
|
"The function object must be callable with _ArgTypes... and return _Ret (N4140 [futures.task.members]/2).");
|
|
}
|
|
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
|
|
~packaged_task() noexcept {
|
|
_MyPromise._Get_state()._Abandon();
|
|
}
|
|
|
|
void swap(packaged_task& _Other) noexcept {
|
|
_STD swap(_MyPromise, _Other._MyPromise);
|
|
}
|
|
|
|
_NODISCARD bool valid() const noexcept {
|
|
return _MyPromise._Is_valid();
|
|
}
|
|
|
|
_NODISCARD_GET_FUTURE future<_Ret> get_future() {
|
|
return future<_Ret>(_From_raw_state_tag{}, _MyPromise._Get_state_for_future());
|
|
}
|
|
|
|
void operator()(_ArgTypes... _Args) {
|
|
if (_MyPromise._Is_ready()) {
|
|
_Throw_future_error2(future_errc::promise_already_satisfied);
|
|
}
|
|
|
|
_MyStateManagerType& _State = _MyPromise._Get_state_for_set();
|
|
_MyStateType* _Ptr = static_cast<_MyStateType*>(_State._Ptr());
|
|
_Ptr->_Call_immediate(_STD forward<_ArgTypes>(_Args)...);
|
|
}
|
|
|
|
void make_ready_at_thread_exit(_ArgTypes... _Args) {
|
|
if (_MyPromise._Is_ready()) {
|
|
_Throw_future_error2(future_errc::promise_already_satisfied);
|
|
}
|
|
|
|
_MyStateManagerType& _State = _MyPromise._Get_state_for_set();
|
|
if (_State._Ptr()->_Already_has_stored_result()) {
|
|
_Throw_future_error2(future_errc::promise_already_satisfied);
|
|
}
|
|
|
|
_MyStateType* _Ptr = static_cast<_MyStateType*>(_State._Ptr());
|
|
_Ptr->_Call_deferred(_STD forward<_ArgTypes>(_Args)...);
|
|
}
|
|
|
|
void reset() { // reset to newly constructed state
|
|
_MyStateManagerType& _State_mgr = _MyPromise._Get_state_for_set();
|
|
_MyStateType& _MyState = *static_cast<_MyStateType*>(_State_mgr._Ptr());
|
|
_MyPromiseType _New_promise(new _MyStateType(_STD move(_MyState)._Get_fn()));
|
|
_MyPromise._Get_state()._Abandon();
|
|
_MyPromise._Swap(_New_promise);
|
|
}
|
|
|
|
packaged_task(const packaged_task&) = delete;
|
|
packaged_task& operator=(const packaged_task&) = delete;
|
|
|
|
private:
|
|
_MyPromiseType _MyPromise{nullptr};
|
|
};
|
|
|
|
#if _HAS_CXX17
|
|
#define _PACKAGED_TASK_DEDUCTION_GUIDE(CALL_OPT, X1, X2, X3) \
|
|
template <class _Ret, class... _Types> \
|
|
packaged_task(_Ret(CALL_OPT*)(_Types...)) -> packaged_task<_Ret(_Types...)>; // intentionally discards CALL_OPT
|
|
|
|
_NON_MEMBER_CALL(_PACKAGED_TASK_DEDUCTION_GUIDE, X1, X2, X3)
|
|
#undef _PACKAGED_TASK_DEDUCTION_GUIDE
|
|
|
|
template <class _Fx>
|
|
packaged_task(_Fx) -> packaged_task<typename _Deduce_signature<_Fx>::type>;
|
|
#endif // _HAS_CXX17
|
|
|
|
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
template <class _Ty, class _Alloc>
|
|
struct uses_allocator<packaged_task<_Ty>, _Alloc> : true_type {};
|
|
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
|
|
|
|
_EXPORT_STD template <class _Ty>
|
|
void swap(packaged_task<_Ty>& _Left, packaged_task<_Ty>& _Right) noexcept {
|
|
_Left.swap(_Right);
|
|
}
|
|
|
|
template <class... _Types, size_t... _Indices>
|
|
decltype(auto) _Invoke_stored_explicit(
|
|
tuple<_Types...>&& _Tuple, index_sequence<_Indices...>) { // invoke() a tuple with explicit parameter ordering
|
|
return _STD invoke(_STD get<_Indices>(_STD move(_Tuple))...);
|
|
}
|
|
|
|
template <class... _Types>
|
|
decltype(auto) _Invoke_stored(tuple<_Types...>&& _Tuple) { // invoke() a tuple
|
|
return _Invoke_stored_explicit(_STD move(_Tuple), index_sequence_for<_Types...>{});
|
|
}
|
|
|
|
template <class... _Types>
|
|
class _Fake_no_copy_callable_adapter {
|
|
// async() is built on packaged_task internals which incorrectly use
|
|
// std::function, which requires that things be copyable. We can't fix this in an
|
|
// update, so this adapter turns copies into abort(). When VSO-153581 is
|
|
// fixed, remove this adapter.
|
|
private:
|
|
using _Storaget = tuple<decay_t<_Types>...>;
|
|
|
|
public:
|
|
explicit _Fake_no_copy_callable_adapter(_Types&&... _Vals) : _Storage(_STD forward<_Types>(_Vals)...) {
|
|
// Initializes _Fake_no_copy_callable_adapter with a decayed callable object and arguments
|
|
}
|
|
|
|
[[noreturn]] _Fake_no_copy_callable_adapter(const _Fake_no_copy_callable_adapter& _Other)
|
|
: _Storage(_STD move(_Other._Storage)) {
|
|
_CSTD abort(); // shouldn't be called
|
|
}
|
|
|
|
_Fake_no_copy_callable_adapter(_Fake_no_copy_callable_adapter&& _Other) = default;
|
|
_Fake_no_copy_callable_adapter& operator=(const _Fake_no_copy_callable_adapter&) = delete;
|
|
_Fake_no_copy_callable_adapter& operator=(_Fake_no_copy_callable_adapter&&) = delete;
|
|
|
|
decltype(auto) operator()() {
|
|
return _Invoke_stored(_STD move(_Storage));
|
|
}
|
|
|
|
private:
|
|
mutable _Storaget _Storage;
|
|
};
|
|
|
|
template <class _Ret, class _Fty>
|
|
_Associated_state<typename _P_arg_type<_Ret>::type>* _Get_associated_state(launch _Psync, _Fty&& _Fnarg) {
|
|
// construct associated asynchronous state object for the launch type
|
|
switch (_Psync) { // select launch type
|
|
case launch::deferred:
|
|
return new _Deferred_async_state<_Ret>(_STD forward<_Fty>(_Fnarg));
|
|
case launch::async: // TRANSITION, fixed in vMajorNext, should create a new thread here
|
|
default:
|
|
return new _Task_async_state<_Ret>(_STD forward<_Fty>(_Fnarg));
|
|
}
|
|
}
|
|
|
|
_EXPORT_STD template <class _Fty, class... _ArgTypes>
|
|
_NODISCARD_ASYNC future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async(
|
|
launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) {
|
|
// manages a callable object launched with supplied policy
|
|
using _Ret = _Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>;
|
|
using _Ptype = typename _P_arg_type<_Ret>::type;
|
|
_Promise<_Ptype> _Pr(
|
|
_Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>(
|
|
_STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...)));
|
|
|
|
return future<_Ret>(_From_raw_state_tag{}, _Pr._Get_state_for_future());
|
|
}
|
|
|
|
_EXPORT_STD template <class _Fty, class... _ArgTypes>
|
|
_NODISCARD_ASYNC future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async(
|
|
_Fty&& _Fnarg, _ArgTypes&&... _Args) {
|
|
// manages a callable object launched with default policy
|
|
return _STD async(launch::async | launch::deferred, _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...);
|
|
}
|
|
|
|
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
|
|
// Experimental coroutine support for std::future. Subject to change/removal!
|
|
namespace experimental {
|
|
template <class _Ty, class... _ArgTypes>
|
|
struct coroutine_traits<future<_Ty>, _ArgTypes...> {
|
|
// defines resumable traits for functions returning future<_Ty>
|
|
struct promise_type {
|
|
promise<_Ty> _MyPromise;
|
|
|
|
future<_Ty> get_return_object() {
|
|
return _MyPromise.get_future();
|
|
}
|
|
|
|
suspend_never initial_suspend() const noexcept {
|
|
return {};
|
|
}
|
|
|
|
suspend_never final_suspend() const noexcept {
|
|
return {};
|
|
}
|
|
|
|
template <class _Ut>
|
|
void return_value(_Ut&& _Value) {
|
|
_MyPromise.set_value(_STD forward<_Ut>(_Value));
|
|
}
|
|
|
|
void unhandled_exception() {
|
|
_MyPromise.set_exception(_STD current_exception());
|
|
}
|
|
};
|
|
};
|
|
|
|
template <class... _ArgTypes>
|
|
struct coroutine_traits<future<void>, _ArgTypes...> {
|
|
// defines resumable traits for functions returning future<void>
|
|
struct promise_type {
|
|
promise<void> _MyPromise;
|
|
|
|
future<void> get_return_object() {
|
|
return _MyPromise.get_future();
|
|
}
|
|
|
|
suspend_never initial_suspend() const noexcept {
|
|
return {};
|
|
}
|
|
|
|
suspend_never final_suspend() const noexcept {
|
|
return {};
|
|
}
|
|
|
|
void return_void() {
|
|
_MyPromise.set_value();
|
|
}
|
|
|
|
void unhandled_exception() {
|
|
_MyPromise.set_exception(_STD current_exception());
|
|
}
|
|
};
|
|
};
|
|
|
|
template <class _Ty>
|
|
struct _Future_awaiter {
|
|
future<_Ty>& _Fut;
|
|
|
|
bool await_ready() const {
|
|
return _Fut._Is_ready();
|
|
}
|
|
|
|
void await_suspend(experimental::coroutine_handle<> _ResumeCb) {
|
|
// TRANSITION, change to .then if and when future gets .then
|
|
thread _WaitingThread([&_Fut = _Fut, _ResumeCb]() mutable {
|
|
_Fut.wait();
|
|
_ResumeCb();
|
|
});
|
|
_WaitingThread.detach();
|
|
}
|
|
|
|
decltype(auto) await_resume() {
|
|
return _Fut.get();
|
|
}
|
|
};
|
|
|
|
} // namespace experimental
|
|
|
|
// Coroutines TS and C++20 coroutine adapter.
|
|
template <class _Ty>
|
|
auto operator co_await(future<_Ty>&& _Fut) {
|
|
return experimental::_Future_awaiter<_Ty>{_Fut};
|
|
}
|
|
|
|
template <class _Ty>
|
|
auto operator co_await(future<_Ty>& _Fut) {
|
|
return experimental::_Future_awaiter<_Ty>{_Fut};
|
|
}
|
|
#endif // _RESUMABLE_FUNCTIONS_SUPPORTED
|
|
|
|
_STD_END
|
|
|
|
#pragma pop_macro("new")
|
|
_STL_RESTORE_CLANG_WARNINGS
|
|
#pragma warning(pop)
|
|
#pragma pack(pop)
|
|
#endif // _STL_COMPILER_PREPROCESSOR
|
|
#endif // _FUTURE_
|