@@ -31,7 +31,11 @@ | |||
#include <thread> | |||
#include <iostream> | |||
#include <assert.h> | |||
#if defined(__clang__) && _WIN32 | |||
#include "src/unix/coroutine.h" //编译器内建的协程函数,MSVC和clang不一样 | |||
#else | |||
#include <experimental/coroutine> | |||
#endif | |||
#include "src/def.h" | |||
#include "src/spinlock.h" | |||
@@ -40,7 +44,6 @@ | |||
#include "src/future.h" | |||
#include "src/promise.h" | |||
#include "src/awaitable.h" | |||
#include "src/switch_scheduler.h" | |||
#include "src/rf_task.h" | |||
#include "src/utils.h" | |||
@@ -50,6 +53,7 @@ | |||
#include "src/promise.inl" | |||
#include "src/state.inl" | |||
#include "src/switch_scheduler.h" | |||
#include "src/_awaker.h" | |||
#include "src/event.h" | |||
#include "src/mutex.h" |
@@ -35,18 +35,7 @@ RESUMEF_NS | |||
return future_type{ this->_state }; | |||
} | |||
mutable counted_ptr<state_type> _state = _Alloc_state(); | |||
private: | |||
static state_type* _Alloc_state() | |||
{ | |||
_Alloc_char _Al; | |||
size_t _Size = sizeof(state_type); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << "awaitable_t::alloc, size=" << _Size << std::endl; | |||
#endif | |||
char * _Ptr = _Al.allocate(_Size); | |||
return new(_Ptr) state_type(true); | |||
} | |||
mutable counted_ptr<state_type> _state = state_future_t::_Alloc_state<state_type>(true); | |||
}; | |||
template<class _Ty> |
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#define LIB_RESUMEF_VERSION 20201 // 2.2.1 | |||
#define LIB_RESUMEF_VERSION 20300 // 2.3.0 | |||
#if defined(RESUMEF_MODULE_EXPORT) | |||
#define RESUMEF_NS export namespace resumef | |||
@@ -8,12 +8,20 @@ | |||
#define RESUMEF_NS namespace resumef | |||
#endif | |||
//如果不清楚context frame的内存布局的情况下,该值设置为0 | |||
#if defined(__clang__) || defined(_MSC_VER) | |||
#define RESUMEF_INLINE_STATE 1 | |||
#else | |||
#define RESUMEF_INLINE_STATE 0 | |||
#endif | |||
RESUMEF_NS | |||
{ | |||
struct scheduler_t; | |||
template<class _Ty = void> | |||
struct future_t; | |||
using future_vt [[deprecated]] = future_t<>; | |||
template <typename _Ty = std::nullptr_t, typename _Alloc = std::allocator<char>> | |||
@@ -33,6 +41,8 @@ RESUMEF_NS | |||
struct state_base_t; | |||
struct switch_scheduler_t; | |||
template<class... _Mutexes> | |||
using scoped_lock = std::scoped_lock<_Mutexes...>; | |||
@@ -109,9 +109,7 @@ RESUMEF_NS | |||
promise_type() | |||
{ | |||
state_type* st = get_state(); | |||
new(st) state_type(coroutine_handle<promise_type>::from_promise(*this)); | |||
st->lock(); | |||
get_state()->set_initial_suspend(coroutine_handle<promise_type>::from_promise(*this)); | |||
} | |||
promise_type(promise_type&& _Right) noexcept = default; | |||
promise_type& operator = (promise_type&& _Right) noexcept = default; | |||
@@ -123,19 +121,20 @@ RESUMEF_NS | |||
return generator_t{ *this }; | |||
} | |||
bool initial_suspend() | |||
std::experimental::suspend_always initial_suspend() | |||
{ | |||
return true; | |||
return {}; | |||
} | |||
bool final_suspend() | |||
std::experimental::suspend_always final_suspend() | |||
{ | |||
return true; | |||
return {}; | |||
} | |||
void yield_value(_Ty const& _Value) | |||
std::experimental::suspend_always yield_value(_Ty const& _Value) | |||
{ | |||
_CurrentValue = std::addressof(_Value); | |||
return {}; | |||
} | |||
//template<class = std::enable_if_t<!std::is_same_v<_Ty, void>, _Ty>> | |||
@@ -170,9 +169,21 @@ RESUMEF_NS | |||
state_type* get_state() | |||
{ | |||
#if RESUMEF_INLINE_STATE | |||
size_t _State_size = _Align_size<state_type>(); | |||
#if defined(__clang__) | |||
auto h = coroutine_handle<promise_type>::from_promise(*this); | |||
char* ptr = reinterpret_cast<char*>(h.address()) - _State_size; | |||
return reinterpret_cast<state_type*>(ptr); | |||
#elif defined(_MSC_VER) | |||
char* ptr = reinterpret_cast<char*>(this) - _State_size; | |||
return reinterpret_cast<state_type*>(ptr); | |||
#else | |||
#error "Unknown compiler" | |||
#endif | |||
#else | |||
return _state.get(); | |||
#endif | |||
} | |||
using _Alloc_char = typename std::allocator_traits<_Alloc>::template rebind_alloc<char>; | |||
@@ -183,27 +194,41 @@ RESUMEF_NS | |||
void* operator new(size_t _Size) | |||
{ | |||
_Alloc_char _Al; | |||
#if RESUMEF_INLINE_STATE | |||
size_t _State_size = _Align_size<state_type>(); | |||
assert(_Size >= sizeof(uint32_t) && _Size < (std::numeric_limits<uint32_t>::max)() - sizeof(_State_size)); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << "generator_promise::new, size=" << (_Size + _State_size) << std::endl; | |||
#endif | |||
_Alloc_char _Al; | |||
char* ptr = _Al.allocate(_Size + _State_size); | |||
char* _Rptr = ptr + _State_size; | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << " generator_promise::new, alloc size=" << (_Size + _State_size) << std::endl; | |||
std::cout << " generator_promise::new, alloc ptr=" << (void*)ptr << std::endl; | |||
std::cout << " generator_promise::new, return ptr=" << (void*)_Rptr << std::endl; | |||
#endif | |||
//ÔÚ³õʼµØÖ·ÉϹ¹Ôìstate | |||
{ | |||
state_type* st = new(ptr) state_type(coroutine_handle<promise_type>::from_promise(*(promise_type *)ptr)); | |||
state_type* st = new(ptr) state_type(); | |||
st->lock(); | |||
*reinterpret_cast<uint32_t*>(ptr + _State_size) = static_cast<uint32_t>(_Size + _State_size); | |||
} | |||
return ptr + _State_size; | |||
return _Rptr; | |||
#else | |||
char* ptr = _Al.allocate(_Size); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << " generator_promise::new, alloc size=" << _Size << std::endl; | |||
std::cout << " generator_promise::new, alloc ptr=" << (void*)ptr << std::endl; | |||
std::cout << " generator_promise::new, return ptr=" << (void*)ptr << std::endl; | |||
#endif | |||
return ptr; | |||
#endif | |||
} | |||
void operator delete(void* _Ptr, size_t _Size) | |||
{ | |||
#if RESUMEF_INLINE_STATE | |||
size_t _State_size = _Align_size<state_type>(); | |||
assert(_Size >= sizeof(uint32_t) && _Size < (std::numeric_limits<uint32_t>::max)() - sizeof(_State_size)); | |||
@@ -211,7 +236,15 @@ RESUMEF_NS | |||
state_type* st = reinterpret_cast<state_type*>(static_cast<char*>(_Ptr) - _State_size); | |||
st->unlock(); | |||
#else | |||
_Alloc_char _Al; | |||
return _Al.deallocate(reinterpret_cast<char *>(_Ptr), _Size); | |||
#endif | |||
} | |||
#if !RESUMEF_INLINE_STATE | |||
private: | |||
counted_ptr<state_type> _state = state_generator_t::_Alloc_state(); | |||
#endif | |||
}; | |||
typedef generator_iterator<_Ty, promise_type> iterator; |
@@ -22,13 +22,7 @@ RESUMEF_NS | |||
promise_impl_t(const promise_impl_t&) = delete; | |||
promise_impl_t& operator = (const promise_impl_t&) = delete; | |||
state_type* get_state() | |||
{ | |||
size_t _State_size = _Align_size<state_type>(); | |||
char* ptr = reinterpret_cast<char*>(this) - _State_size; | |||
return reinterpret_cast<state_type*>(ptr); | |||
} | |||
auto get_state()->state_type*; | |||
suspend_on_initial initial_suspend() noexcept; | |||
suspend_on_final final_suspend() noexcept; | |||
void set_exception(std::exception_ptr e); | |||
@@ -39,42 +33,12 @@ RESUMEF_NS | |||
void cancellation_requested(); | |||
using _Alloc_char = std::allocator<char>; | |||
void* operator new(size_t _Size) | |||
{ | |||
size_t _State_size = _Align_size<state_type>(); | |||
assert(_Size >= sizeof(uint32_t) && _Size < (std::numeric_limits<uint32_t>::max)() - sizeof(_State_size)); | |||
_Alloc_char _Al; | |||
/*If allocation fails, the coroutine throws std::bad_alloc, | |||
unless the Promise type defines the member function Promise::get_return_object_on_allocation_failure(). | |||
If that member function is defined, allocation uses the nothrow form of operator new and on allocation failure, | |||
the coroutine immediately returns the object obtained from Promise::get_return_object_on_allocation_failure() to the caller. | |||
*/ | |||
char* ptr = _Al.allocate(_Size + _State_size); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << " future_promise::new, alloc size=" << (_Size + _State_size) << std::endl; | |||
std::cout << " future_promise::new, alloc ptr=" << (void*)ptr << std::endl; | |||
std::cout << " future_promise::new, return ptr=" << (void*)(ptr + _State_size) << std::endl; | |||
void* operator new(size_t _Size); | |||
void operator delete(void* _Ptr, size_t _Size); | |||
#if !RESUMEF_INLINE_STATE | |||
private: | |||
counted_ptr<state_type> _state = state_future_t::_Alloc_state<state_type>(false); | |||
#endif | |||
//在初始地址上构造state | |||
{ | |||
state_type* st = new(ptr) state_type(_Size + _State_size); | |||
st->lock(); | |||
} | |||
return ptr + _State_size; | |||
} | |||
void operator delete(void* _Ptr, size_t _Size) | |||
{ | |||
(void)_Size; | |||
size_t _State_size = _Align_size<state_type>(); | |||
assert(_Size >= sizeof(uint32_t) && _Size < (std::numeric_limits<uint32_t>::max)() - sizeof(_State_size)); | |||
state_type* st = reinterpret_cast<state_type*>(static_cast<char*>(_Ptr) - _State_size); | |||
st->unlock(); | |||
} | |||
}; | |||
template<class _Ty> | |||
@@ -86,7 +50,7 @@ RESUMEF_NS | |||
template<class U> | |||
void return_value(U&& val); //co_return val | |||
template<class U> | |||
void yield_value(U&& val); | |||
std::experimental::suspend_always yield_value(U&& val); | |||
}; | |||
template<> | |||
@@ -95,7 +59,7 @@ RESUMEF_NS | |||
using promise_impl_t<void>::get_return_object; | |||
void return_void(); //co_return; | |||
void yield_value(); | |||
std::experimental::suspend_always yield_value(); | |||
}; | |||
} |
@@ -24,6 +24,7 @@ RESUMEF_NS | |||
{ | |||
} | |||
}; | |||
struct suspend_on_final | |||
{ | |||
inline bool await_ready() noexcept | |||
@@ -80,6 +81,80 @@ RESUMEF_NS | |||
} | |||
template <typename _Ty> | |||
auto promise_impl_t<_Ty>::get_state() -> state_type* | |||
{ | |||
#if RESUMEF_INLINE_STATE | |||
size_t _State_size = _Align_size<state_type>(); | |||
#if defined(__clang__) | |||
auto h = coroutine_handle<promise_type>::from_promise(*reinterpret_cast<promise_type *>(this)); | |||
char* ptr = reinterpret_cast<char*>(h.address()) - _State_size; | |||
return reinterpret_cast<state_type*>(ptr); | |||
#elif defined(_MSC_VER) | |||
char* ptr = reinterpret_cast<char*>(this) - _State_size; | |||
return reinterpret_cast<state_type*>(ptr); | |||
#else | |||
#error "Unknown compiler" | |||
#endif | |||
#else | |||
return _state.get(); | |||
#endif | |||
} | |||
template <typename _Ty> | |||
void* promise_impl_t<_Ty>::operator new(size_t _Size) | |||
{ | |||
_Alloc_char _Al; | |||
#if RESUMEF_INLINE_STATE | |||
size_t _State_size = _Align_size<state_type>(); | |||
assert(_Size >= sizeof(uint32_t) && _Size < (std::numeric_limits<uint32_t>::max)() - sizeof(_State_size)); | |||
/*If allocation fails, the coroutine throws std::bad_alloc, | |||
unless the Promise type defines the member function Promise::get_return_object_on_allocation_failure(). | |||
If that member function is defined, allocation uses the nothrow form of operator new and on allocation failure, | |||
the coroutine immediately returns the object obtained from Promise::get_return_object_on_allocation_failure() to the caller. | |||
*/ | |||
char* ptr = _Al.allocate(_Size + _State_size); | |||
char* _Rptr = ptr + _State_size; | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << " future_promise::new, alloc size=" << (_Size + _State_size) << std::endl; | |||
std::cout << " future_promise::new, alloc ptr=" << (void*)ptr << std::endl; | |||
std::cout << " future_promise::new, return ptr=" << (void*)_Rptr << std::endl; | |||
#endif | |||
//在初始地址上构造state | |||
{ | |||
state_type* st = new(ptr) state_type(_Size + _State_size); | |||
st->lock(); | |||
} | |||
return _Rptr; | |||
#else | |||
char* ptr = _Al.allocate(_Size); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << " future_promise::new, alloc size=" << (_Size) << std::endl; | |||
std::cout << " future_promise::new, alloc ptr=" << (void*)ptr << std::endl; | |||
std::cout << " future_promise::new, return ptr=" << (void*)ptr << std::endl; | |||
#endif | |||
return ptr; | |||
#endif | |||
} | |||
template <typename _Ty> | |||
void promise_impl_t<_Ty>::operator delete(void* _Ptr, size_t _Size) | |||
{ | |||
#if RESUMEF_INLINE_STATE | |||
(void)_Size; | |||
size_t _State_size = _Align_size<state_type>(); | |||
assert(_Size >= sizeof(uint32_t) && _Size < (std::numeric_limits<uint32_t>::max)() - sizeof(_State_size)); | |||
state_type* st = reinterpret_cast<state_type*>(static_cast<char*>(_Ptr) - _State_size); | |||
st->unlock(); | |||
#else | |||
_Alloc_char _Al; | |||
return _Al.deallocate(reinterpret_cast<char*>(_Ptr), _Size); | |||
#endif | |||
} | |||
template<class _Ty> | |||
template<class U> | |||
@@ -90,9 +165,10 @@ RESUMEF_NS | |||
template<class _Ty> | |||
template<class U> | |||
inline void promise_t<_Ty>::yield_value(U&& val) | |||
inline std::experimental::suspend_always promise_t<_Ty>::yield_value(U&& val) | |||
{ | |||
this->get_state()->promise_yield_value(this, std::forward<U>(val)); | |||
return {}; | |||
} | |||
inline void promise_t<void>::return_void() | |||
@@ -100,10 +176,10 @@ RESUMEF_NS | |||
this->get_state()->set_value(); | |||
} | |||
inline void promise_t<void>::yield_value() | |||
inline std::experimental::suspend_always promise_t<void>::yield_value() | |||
{ | |||
this->get_state()->promise_yield_value(this); | |||
return {}; | |||
} | |||
} | |||
@@ -58,12 +58,6 @@ RESUMEF_NS | |||
std::unique_ptr<task_base_t> del_switch(state_base_t* sptr); | |||
void add_switch(std::unique_ptr<task_base_t> task); | |||
switch_scheduler_t operator co_await() | |||
{ | |||
return { this }; | |||
} | |||
friend struct task_base; | |||
friend struct local_scheduler; | |||
protected: | |||
scheduler_t(); |
@@ -21,8 +21,10 @@ RESUMEF_NS | |||
void state_generator_t::destroy_deallocate() | |||
{ | |||
size_t _Size = _Align_size<state_generator_t>(); | |||
#if RESUMEF_INLINE_STATE | |||
char* _Ptr = reinterpret_cast<char*>(this) + _Size; | |||
_Size = *reinterpret_cast<uint32_t*>(_Ptr); | |||
#endif | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << "destroy_deallocate, size=" << _Size << std::endl; | |||
#endif | |||
@@ -34,7 +36,7 @@ RESUMEF_NS | |||
void state_generator_t::resume() | |||
{ | |||
if (_coro != nullptr) | |||
if (_coro) | |||
{ | |||
_coro.resume(); | |||
if (_coro.done()) | |||
@@ -54,12 +56,12 @@ RESUMEF_NS | |||
bool state_generator_t::is_ready() const | |||
{ | |||
return _coro != nullptr && !_coro.done(); | |||
return (bool)_coro && !_coro.done(); | |||
} | |||
bool state_generator_t::has_handler() const | |||
{ | |||
return _coro != nullptr; | |||
return (bool)_coro; | |||
} | |||
bool state_generator_t::switch_scheduler_await_suspend(scheduler_t* sch, coroutine_handle<>) | |||
@@ -90,7 +92,7 @@ RESUMEF_NS | |||
if (_is_initor == initor_type::Initial) | |||
{ | |||
assert(_initor != nullptr); | |||
assert((bool)_initor); | |||
_is_initor = initor_type::None; | |||
__guard.unlock(); | |||
@@ -99,7 +101,7 @@ RESUMEF_NS | |||
return; | |||
} | |||
if (_coro != nullptr) | |||
if (_coro) | |||
{ | |||
coroutine_handle<> handler = _coro; | |||
_coro = nullptr; | |||
@@ -111,6 +113,8 @@ RESUMEF_NS | |||
if (_is_initor == initor_type::Final) | |||
{ | |||
assert((bool)_initor); | |||
_is_initor = initor_type::None; | |||
__guard.unlock(); | |||
@@ -122,7 +126,7 @@ RESUMEF_NS | |||
bool state_future_t::has_handler() const | |||
{ | |||
scoped_lock<lock_type> __guard(_mtx); | |||
return _coro != nullptr || _is_initor != initor_type::None; | |||
return (bool)_coro || _is_initor != initor_type::None; | |||
} | |||
bool state_future_t::is_ready() const | |||
@@ -162,7 +166,7 @@ RESUMEF_NS | |||
if (_parent != nullptr) | |||
_parent->switch_scheduler_await_suspend(sch, nullptr); | |||
if (handler != nullptr) | |||
if (handler) | |||
{ | |||
_coro = handler; | |||
sch->add_generator(this); |
@@ -48,10 +48,6 @@ RESUMEF_NS | |||
struct state_generator_t : public state_base_t | |||
{ | |||
state_generator_t(coroutine_handle<> handler) | |||
{ | |||
_coro = handler; | |||
} | |||
private: | |||
virtual void destroy_deallocate() override; | |||
public: | |||
@@ -60,12 +56,20 @@ RESUMEF_NS | |||
virtual bool is_ready() const override; | |||
virtual bool switch_scheduler_await_suspend(scheduler_t* sch, coroutine_handle<> handler) override; | |||
static state_generator_t * _Alloc_state(coroutine_handle<> handler) | |||
void set_initial_suspend(coroutine_handle<> handler) | |||
{ | |||
_coro = handler; | |||
} | |||
static state_generator_t * _Alloc_state() | |||
{ | |||
_Alloc_char _Al; | |||
size_t _Size = sizeof(state_generator_t); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << "state_generator_t::alloc, size=" << sizeof(state_generator_t) << std::endl; | |||
#endif | |||
return new state_generator_t(handler); | |||
char* _Ptr = _Al.allocate(_Size); | |||
return new(_Ptr) state_generator_t(); | |||
} | |||
}; | |||
@@ -146,11 +150,25 @@ RESUMEF_NS | |||
void promise_initial_suspend(coroutine_handle<_PromiseT> handler); | |||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | |||
void promise_final_suspend(coroutine_handle<_PromiseT> handler); | |||
template<class _Sty> | |||
static _Sty* _Alloc_state(bool awaitor) | |||
{ | |||
_Alloc_char _Al; | |||
size_t _Size = sizeof(_Sty); | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::cout << "state_future_t::alloc, size=" << _Size << std::endl; | |||
#endif | |||
char* _Ptr = _Al.allocate(_Size); | |||
return new(_Ptr) _Sty(awaitor); | |||
} | |||
}; | |||
template <typename _Ty> | |||
struct state_t final : public state_future_t | |||
{ | |||
friend state_future_t; | |||
using state_future_t::lock_type; | |||
using value_type = _Ty; | |||
@@ -162,7 +180,7 @@ RESUMEF_NS | |||
{ | |||
_alloc_size = sizeof(*this); | |||
} | |||
public: | |||
~state_t() | |||
{ | |||
if (_has_value) | |||
@@ -187,6 +205,7 @@ RESUMEF_NS | |||
template<> | |||
struct state_t<void> final : public state_future_t | |||
{ | |||
friend state_future_t; | |||
using state_future_t::lock_type; | |||
explicit state_t(size_t alloc_size) :state_future_t() |
@@ -5,7 +5,7 @@ RESUMEF_NS | |||
void state_future_t::promise_initial_suspend(coroutine_handle<_PromiseT> handler) | |||
{ | |||
assert(this->_scheduler == nullptr); | |||
assert(this->_coro == nullptr); | |||
assert(!this->_coro); | |||
this->_initor = handler; | |||
this->_is_initor = initor_type::Initial; | |||
@@ -38,7 +38,7 @@ RESUMEF_NS | |||
this->_parent = parent_state; | |||
this->_scheduler = sch; | |||
} | |||
if (_coro == nullptr) | |||
if (!_coro) | |||
this->_coro = handler; | |||
if (sch != nullptr) | |||
@@ -55,7 +55,7 @@ RESUMEF_NS | |||
coroutine_handle<_PromiseT> handler = coroutine_handle<_PromiseT>::from_promise(*promise); | |||
if (!handler.done()) | |||
{ | |||
if (this->_coro == nullptr) | |||
if (!this->_coro) | |||
this->_coro = handler; | |||
scheduler_t* sch = this->get_scheduler(); | |||
if (sch != nullptr) |
@@ -33,8 +33,8 @@ RESUMEF_NS | |||
scheduler_t* _scheduler; | |||
}; | |||
inline switch_scheduler_t via(scheduler_t* sch) | |||
inline switch_scheduler_t operator co_await(scheduler_t& sch) | |||
{ | |||
return { sch }; | |||
return { &sch }; | |||
} | |||
} |
@@ -22,4 +22,18 @@ RESUMEF_NS | |||
struct is_generator<generator_t<_Ty, _Alloc>> : std::true_type {}; | |||
template<class _Ty> | |||
constexpr bool is_generator_v = is_generator<remove_cvref_t<_Ty>>::value; | |||
template<class _PromiseT> | |||
struct is_awaitable : std::false_type {}; | |||
template<class _Ty> | |||
struct is_awaitable<awaitable_t<_Ty>> : std::true_type {}; | |||
template<class _Ty> | |||
constexpr bool is_awaitable_v = is_awaitable<remove_cvref_t<_Ty>>::value; | |||
template<class _Ty> | |||
constexpr bool is_await_suspend_v = is_future_v<_Ty> | |||
|| is_generator_v<_Ty> | |||
|| is_awaitable_v<_Ty> | |||
|| std::is_same_v<remove_cvref_t<_Ty>, switch_scheduler_t> | |||
; | |||
} |
@@ -1,91 +1,276 @@ | |||
//#pragma once | |||
//===----------------------------- coroutine -----------------------------===// | |||
// | |||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
// See https://llvm.org/LICENSE.txt for license information. | |||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
// | |||
//===----------------------------------------------------------------------===// | |||
#ifndef _LIBCPP_EXPERIMENTAL_COROUTINE | |||
#define _LIBCPP_EXPERIMENTAL_COROUTINE | |||
/** | |||
experimental/coroutine synopsis | |||
// C++next | |||
namespace std { | |||
namespace experimental { | |||
template <typename R, typename...> struct coroutine_traits { | |||
using promise_type = typename R::promise_type; | |||
}; | |||
template <typename Promise = void> struct coroutine_handle; | |||
template <> struct coroutine_handle<void> { | |||
static coroutine_handle from_address(void *addr) noexcept { | |||
coroutine_handle me; | |||
me.ptr = addr; | |||
return me; | |||
} | |||
void operator()() { resume(); } | |||
void *address() const { return ptr; } | |||
void resume() const { __builtin_coro_resume(ptr); } | |||
void destroy() const { __builtin_coro_destroy(ptr); } | |||
bool done() const { return __builtin_coro_done(ptr); } | |||
coroutine_handle &operator=(decltype(nullptr)) { | |||
ptr = nullptr; | |||
return *this; | |||
} | |||
coroutine_handle(decltype(nullptr)) : ptr(nullptr) {} | |||
coroutine_handle() : ptr(nullptr) {} | |||
// void reset() { ptr = nullptr; } // add to P0057? | |||
explicit operator bool() const { return ptr; } | |||
protected: | |||
void *ptr; | |||
}; | |||
template <typename Promise> struct coroutine_handle : coroutine_handle<> { | |||
static coroutine_handle from_address(void *addr) noexcept { | |||
coroutine_handle me; | |||
me.ptr = addr; | |||
return me; | |||
} | |||
coroutine_handle() {} | |||
coroutine_handle(decltype(nullptr)) {} | |||
coroutine_handle &operator=(decltype(nullptr)) { | |||
ptr = nullptr; | |||
return *this; | |||
} | |||
Promise &promise() const { | |||
return *reinterpret_cast<Promise *>( | |||
__builtin_coro_promise(ptr, alignof(Promise), false)); | |||
} | |||
static coroutine_handle from_promise(Promise &promise) { | |||
coroutine_handle p; | |||
p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true); | |||
return p; | |||
} | |||
}; | |||
template <typename _PromiseT> | |||
bool operator==(coroutine_handle<_PromiseT> const &_Left, | |||
coroutine_handle<_PromiseT> const &_Right) noexcept { | |||
return _Left.address() == _Right.address(); | |||
} | |||
namespace experimental { | |||
inline namespace coroutines_v1 { | |||
template <typename _PromiseT> | |||
bool operator!=(coroutine_handle<_PromiseT> const &_Left, | |||
coroutine_handle<_PromiseT> const &_Right) noexcept { | |||
return !(_Left == _Right); | |||
} | |||
// 18.11.1 coroutine traits | |||
template <typename R, typename... ArgTypes> | |||
class coroutine_traits; | |||
// 18.11.2 coroutine handle | |||
template <typename Promise = void> | |||
class coroutine_handle; | |||
// 18.11.2.7 comparison operators: | |||
bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept; | |||
bool operator!=(coroutine_handle<> x, coroutine_handle<> y) noexcept; | |||
bool operator<(coroutine_handle<> x, coroutine_handle<> y) noexcept; | |||
bool operator<=(coroutine_handle<> x, coroutine_handle<> y) noexcept; | |||
bool operator>=(coroutine_handle<> x, coroutine_handle<> y) noexcept; | |||
bool operator>(coroutine_handle<> x, coroutine_handle<> y) noexcept; | |||
// 18.11.3 trivial awaitables | |||
struct suspend_never; | |||
struct suspend_always; | |||
// 18.11.2.8 hash support: | |||
template <class T> struct hash; | |||
template <class P> struct hash<coroutine_handle<P>>; | |||
struct suspend_always { | |||
bool await_ready() { return false; } | |||
void await_suspend(coroutine_handle<>) {} | |||
void await_resume() {} | |||
}; | |||
struct suspend_never { | |||
bool await_ready() { return true; } | |||
void await_suspend(coroutine_handle<>) {} | |||
void await_resume() {} | |||
}; | |||
struct suspend_if { | |||
bool _Ready; | |||
explicit suspend_if(bool _Condition) : _Ready(!_Condition) {} | |||
bool await_ready() { return _Ready; } | |||
void await_suspend(coroutine_handle<>) {} | |||
void await_resume() {} | |||
}; | |||
} | |||
} // namespace coroutines_v1 | |||
} // namespace experimental | |||
} // namespace std | |||
*/ | |||
#include <new> | |||
#include <type_traits> | |||
#include <functional> | |||
#include <memory> // for hash<T*> | |||
#include <cstddef> | |||
#include <cassert> | |||
#include "clang_builtin.h" | |||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | |||
#pragma GCC system_header | |||
#endif | |||
#ifndef _LIBCPP_HAS_NO_COROUTINES | |||
namespace std { | |||
namespace experimental { | |||
template <class _Tp, class = void> | |||
struct __coroutine_traits_sfinae {}; | |||
template <class _Tp> | |||
struct __coroutine_traits_sfinae<_Tp, void_t<typename _Tp::promise_type>> | |||
{ | |||
using promise_type = typename _Tp::promise_type; | |||
}; | |||
template <typename _Ret, typename... _Args> | |||
struct coroutine_traits : public __coroutine_traits_sfinae<_Ret> | |||
{ | |||
}; | |||
template <typename _Promise = void> | |||
class coroutine_handle; | |||
template <> | |||
class coroutine_handle<void> { | |||
public: | |||
constexpr coroutine_handle() noexcept : __handle_(nullptr) {} | |||
constexpr coroutine_handle(nullptr_t) noexcept : __handle_(nullptr) {} | |||
coroutine_handle& operator=(nullptr_t) noexcept { | |||
__handle_ = nullptr; | |||
return *this; | |||
} | |||
constexpr void* address() const noexcept { return __handle_; } | |||
constexpr explicit operator bool() const noexcept { return __handle_; } | |||
void operator()() { resume(); } | |||
void resume() { | |||
__builtin_coro_resume(__handle_); | |||
} | |||
void destroy() { | |||
__builtin_coro_destroy(__handle_); | |||
} | |||
bool done() const { | |||
return __builtin_coro_done(__handle_); | |||
} | |||
public: | |||
static coroutine_handle from_address(void* __addr) noexcept { | |||
coroutine_handle __tmp; | |||
__tmp.__handle_ = __addr; | |||
return __tmp; | |||
} | |||
// FIXME: Should from_address(nullptr) be allowed? | |||
static coroutine_handle from_address(nullptr_t) noexcept { | |||
return coroutine_handle(nullptr); | |||
} | |||
template <class _Tp, bool _CallIsValid = false> | |||
static coroutine_handle from_address(_Tp*) { | |||
static_assert(_CallIsValid, | |||
"coroutine_handle<void>::from_address cannot be called with " | |||
"non-void pointers"); | |||
} | |||
private: | |||
bool __is_suspended() const noexcept { | |||
// FIXME actually implement a check for if the coro is suspended. | |||
return __handle_; | |||
} | |||
template <class _PromiseT> | |||
friend class coroutine_handle; | |||
void* __handle_; | |||
}; | |||
// 18.11.2.7 comparison operators: | |||
inline bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { | |||
return __x.address() == __y.address(); | |||
} | |||
inline bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { | |||
return !(__x == __y); | |||
} | |||
inline bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { | |||
return less<void*>()(__x.address(), __y.address()); | |||
} | |||
inline bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { | |||
return __y < __x; | |||
} | |||
inline bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { | |||
return !(__x > __y); | |||
} | |||
inline bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { | |||
return !(__x < __y); | |||
} | |||
template <typename _Promise> | |||
class coroutine_handle : public coroutine_handle<> { | |||
using _Base = coroutine_handle<>; | |||
public: | |||
#ifndef _LIBCPP_CXX03_LANG | |||
// 18.11.2.1 construct/reset | |||
using coroutine_handle<>::coroutine_handle; | |||
#else | |||
coroutine_handle() noexcept : _Base() {} | |||
coroutine_handle(nullptr_t) noexcept : _Base(nullptr) {} | |||
#endif | |||
coroutine_handle& operator=(nullptr_t) noexcept { | |||
_Base::operator=(nullptr); | |||
return *this; | |||
} | |||
_Promise& promise() const { | |||
return *static_cast<_Promise*>( | |||
__builtin_coro_promise(this->__handle_, alignof(_Promise), false)); | |||
} | |||
public: | |||
static coroutine_handle from_address(void* __addr) noexcept { | |||
coroutine_handle __tmp; | |||
__tmp.__handle_ = __addr; | |||
return __tmp; | |||
} | |||
// NOTE: this overload isn't required by the standard but is needed so | |||
// the deleted _Promise* overload doesn't make from_address(nullptr) | |||
// ambiguous. | |||
// FIXME: should from_address work with nullptr? | |||
static coroutine_handle from_address(nullptr_t) noexcept { | |||
return coroutine_handle(nullptr); | |||
} | |||
template <class _Tp, bool _CallIsValid = false> | |||
static coroutine_handle from_address(_Tp*) { | |||
static_assert(_CallIsValid, | |||
"coroutine_handle<promise_type>::from_address cannot be called with " | |||
"non-void pointers"); | |||
} | |||
template <bool _CallIsValid = false> | |||
static coroutine_handle from_address(_Promise*) { | |||
static_assert(_CallIsValid, | |||
"coroutine_handle<promise_type>::from_address cannot be used with " | |||
"pointers to the coroutine's promise type; use 'from_promise' instead"); | |||
} | |||
static coroutine_handle from_promise(_Promise& __promise) noexcept { | |||
typedef typename remove_cv<_Promise>::type _RawPromise; | |||
coroutine_handle __tmp; | |||
__tmp.__handle_ = __builtin_coro_promise( | |||
std::addressof(const_cast<_RawPromise&>(__promise)), | |||
alignof(_Promise), true); | |||
return __tmp; | |||
} | |||
}; | |||
#if __has_builtin(__builtin_coro_noop) | |||
struct noop_coroutine_promise {}; | |||
template <> | |||
class coroutine_handle<noop_coroutine_promise> | |||
: public coroutine_handle<> { | |||
using _Base = coroutine_handle<>; | |||
using _Promise = noop_coroutine_promise; | |||
public: | |||
_Promise& promise() const { | |||
return *static_cast<_Promise*>( | |||
__builtin_coro_promise(this->__handle_, alignof(_Promise), false)); | |||
} | |||
constexpr explicit operator bool() const noexcept { return true; } | |||
constexpr bool done() const noexcept { return false; } | |||
constexpr void operator()() const noexcept {} | |||
constexpr void resume() const noexcept {} | |||
constexpr void destroy() const noexcept {} | |||
private: | |||
friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept; | |||
coroutine_handle() noexcept { | |||
this->__handle_ = __builtin_coro_noop(); | |||
} | |||
}; | |||
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>; | |||
inline noop_coroutine_handle noop_coroutine() noexcept { | |||
return noop_coroutine_handle(); | |||
} | |||
#endif // __has_builtin(__builtin_coro_noop) | |||
struct suspend_never { | |||
bool await_ready() const noexcept { return true; } | |||
void await_suspend(coroutine_handle<>) const noexcept {} | |||
void await_resume() const noexcept {} | |||
}; | |||
struct suspend_always { | |||
bool await_ready() const noexcept { return false; } | |||
void await_suspend(coroutine_handle<>) const noexcept {} | |||
void await_resume() const noexcept {} | |||
}; | |||
} | |||
template <class _Tp> | |||
struct hash<experimental::coroutine_handle<_Tp> > { | |||
using __arg_type = experimental::coroutine_handle<_Tp>; | |||
size_t operator()(__arg_type const& __v) const noexcept | |||
{ | |||
return hash<void*>()(__v.address()); | |||
} | |||
}; | |||
} | |||
#endif // !defined(_LIBCPP_HAS_NO_COROUTINES) | |||
#endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */ |
@@ -19,7 +19,9 @@ future_t<> test_channel_read(const channel_t<std::string> & c) | |||
for (size_t i = 0; i < 10; ++i) | |||
{ | |||
#ifndef __clang__ | |||
try | |||
#endif | |||
{ | |||
auto val = co_await c.read(); | |||
//auto val = co_await c; //第二种从channel读出数据的方法。利用重载operator co_await(),而不是c是一个awaitable_t。 | |||
@@ -31,11 +33,13 @@ future_t<> test_channel_read(const channel_t<std::string> & c) | |||
#endif | |||
std::cout << std::endl; | |||
} | |||
#ifndef __clang__ | |||
catch (resumef::channel_exception& e) | |||
{ | |||
//MAX_CHANNEL_QUEUE=0,并且先读后写,会触发read_before_write异常 | |||
std::cout << e.what() << std::endl; | |||
} | |||
#endif | |||
co_await sleep_for(50ms); | |||
} |
@@ -22,7 +22,9 @@ future_t<> test_channel_consumer(const channel_t<std::string> & c, size_t cnt) | |||
{ | |||
for (size_t i = 0; i < cnt; ++i) | |||
{ | |||
#ifndef __clang__ | |||
try | |||
#endif | |||
{ | |||
auto val = co_await c.read(); | |||
++gcounter; | |||
@@ -33,12 +35,14 @@ future_t<> test_channel_consumer(const channel_t<std::string> & c, size_t cnt) | |||
} | |||
#endif | |||
} | |||
#ifndef __clang__ | |||
catch (channel_exception& e) | |||
{ | |||
//MAX_CHANNEL_QUEUE=0,并且先读后写,会触发read_before_write异常 | |||
scoped_lock<std::mutex> __lock(cout_mutex); | |||
std::cout << e.what() << std::endl; | |||
} | |||
#endif | |||
#if OUTPUT_DEBUG | |||
co_await sleep_for(50ms); | |||
@@ -109,5 +113,4 @@ void resumable_main_channel_mult_thread() | |||
write_th.join(); | |||
std::cout << "OK: counter = " << gcounter.load() << std::endl; | |||
(void)_getch(); | |||
} |
@@ -53,11 +53,14 @@ future_t<> test_signal_exception() | |||
{ | |||
for (intptr_t i = 10; i >= 0; --i) | |||
{ | |||
#ifndef __clang__ | |||
try | |||
#endif | |||
{ | |||
auto r = co_await async_signal_exception2(i); | |||
std::cout << "result is " << r << std::endl; | |||
} | |||
#ifndef __clang__ | |||
catch (const std::exception& e) | |||
{ | |||
std::cout << "exception signal : " << e.what() << std::endl; | |||
@@ -66,6 +69,7 @@ future_t<> test_signal_exception() | |||
{ | |||
std::cout << "exception signal : who knows?" << std::endl; | |||
} | |||
#endif | |||
} | |||
} | |||
@@ -394,7 +394,9 @@ void resumable_main_modern_cb() | |||
//支持librf的用法 | |||
GO | |||
{ | |||
#ifndef __clang__ | |||
try | |||
#endif | |||
{ | |||
int val = co_await add_async(1, 2, use_librf); | |||
std::cout << val << std::endl; | |||
@@ -409,6 +411,7 @@ void resumable_main_modern_cb() | |||
std::cout << result << std::endl; | |||
} | |||
#ifndef __clang__ | |||
catch (const std::exception& e) | |||
{ | |||
std::cout << "exception signal : " << e.what() << std::endl; | |||
@@ -417,6 +420,7 @@ void resumable_main_modern_cb() | |||
{ | |||
std::cout << "exception signal : who knows?" << std::endl; | |||
} | |||
#endif | |||
}; | |||
resumef::this_scheduler()->run_until_notask(); |
@@ -30,7 +30,7 @@ auto yield_switch(intptr_t coro) -> resumef::generator_t<intptr_t> | |||
{ | |||
for (intptr_t i = 0; i < N / coro; ++i) | |||
co_yield i; | |||
return N / coro; | |||
co_return N / coro; | |||
} | |||
void resumable_switch(intptr_t coro, size_t idx) |
@@ -59,7 +59,7 @@ static future_t<> resumable_get_long(int64_t val, channel_t<bool> & c_done) | |||
{ | |||
std::cout << "thread = " << std::this_thread::get_id() << ", value = " << val << std::endl; | |||
co_await via(sch_in_thread); | |||
co_await *sch_in_thread; | |||
val = co_await async_get_long(val); | |||
std::cout << "thread = " << std::this_thread::get_id() << ", value = " << val << std::endl; | |||
@@ -9,7 +9,7 @@ | |||
using namespace resumef; | |||
auto test_yield_int() -> generator_t<int> | |||
generator_t<int> test_yield_int() | |||
{ | |||
std::cout << "1 will yield return" << std::endl; | |||
co_yield 1; |
@@ -1,7 +1,5 @@ | |||
| |||
#include "librf.h" | |||
#include <experimental/resumable> | |||
#include <experimental/generator> | |||
#include <optional> | |||
extern void resumable_main_yield_return(); | |||
@@ -34,33 +32,36 @@ int main(int argc, const char* argv[]) | |||
(void)argc; | |||
(void)argv; | |||
resumable_main_layout(); | |||
return 0; | |||
//if (argc > 1) | |||
// resumable_main_benchmark_asio_client(atoi(argv[1])); | |||
//else | |||
// resumable_main_benchmark_asio_server(); | |||
//resumable_main_cb(); | |||
//resumable_main_layout(); | |||
//resumable_main_modern_cb(); | |||
//resumable_main_suspend_always(); | |||
//resumable_main_yield_return(); | |||
resumable_main_cb(); | |||
resumable_main_layout(); | |||
resumable_main_modern_cb(); | |||
resumable_main_suspend_always(); | |||
resumable_main_yield_return(); | |||
//resumable_main_resumable(); | |||
//resumable_main_routine(); | |||
//resumable_main_exception(); | |||
//resumable_main_dynamic_go(); | |||
resumable_main_routine(); | |||
resumable_main_exception(); | |||
resumable_main_dynamic_go(); | |||
//resumable_main_multi_thread(); | |||
//resumable_main_timer(); | |||
resumable_main_timer(); | |||
//resumable_main_benchmark_mem(); | |||
//resumable_main_mutex(); | |||
//resumable_main_event(); | |||
//resumable_main_event_timeout(); | |||
//resumable_main_channel(); | |||
//resumable_main_channel_mult_thread(); | |||
//resumable_main_sleep(); | |||
//resumable_main_when_all(); | |||
//resumable_main_switch_scheduler(); | |||
resumable_main_mutex(); | |||
resumable_main_event(); | |||
resumable_main_event_timeout(); | |||
resumable_main_channel(); | |||
resumable_main_channel_mult_thread(); | |||
resumable_main_sleep(); | |||
resumable_main_when_all(); | |||
resumable_main_switch_scheduler(); | |||
//benchmark_main_channel_passing_next(); | |||
std::cout << "ALL OK!" << std::endl; | |||
return 0; | |||
} |
@@ -46,7 +46,7 @@ | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> | |||
<ConfigurationType>Application</ConfigurationType> | |||
<UseDebugLibraries>false</UseDebugLibraries> | |||
<PlatformToolset>v142</PlatformToolset> | |||
<PlatformToolset>ClangCL</PlatformToolset> | |||
<WholeProgramOptimization>true</WholeProgramOptimization> | |||
<CharacterSet>NotSet</CharacterSet> | |||
</PropertyGroup> | |||
@@ -170,7 +170,10 @@ | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemGroup> | |||
<ClCompile Include="..\benchmark\benchmark_asio_echo.cpp" /> | |||
<ClCompile Include="..\benchmark\benchmark_asio_echo.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\benchmark\benchmark_async_mem.cpp" /> | |||
<ClCompile Include="..\benchmark\benchmark_channel_passing_next.cpp" /> | |||
<ClCompile Include="..\librf\src\event.cpp" /> | |||
@@ -181,24 +184,58 @@ | |||
<ClCompile Include="..\librf\src\state.cpp" /> | |||
<ClCompile Include="..\librf\src\timer.cpp" /> | |||
<ClCompile Include="..\librf\src\when.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_cb.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_channel.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_channel_mult_thread.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_dynamic_go.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_event.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_event_timeout.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_exception.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_cb.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_channel.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_channel_mult_thread.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_dynamic_go.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_event.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_event_timeout.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_exception.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_memory_layout.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_modern_cb.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_multi_thread.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_mutex.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_resumable.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_routine.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_sleep.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_suspend_always.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_switch_scheduler.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_timer.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_when_all.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_modern_cb.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_multi_thread.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_mutex.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_resumable.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_routine.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_sleep.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_suspend_always.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_switch_scheduler.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_timer.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_when_all.cpp"> | |||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_yield_return.cpp" /> | |||
<ClCompile Include="librf.cpp"> | |||
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Default</BasicRuntimeChecks> | |||
@@ -226,6 +263,7 @@ | |||
<ClInclude Include="..\librf\src\state.h" /> | |||
<ClInclude Include="..\librf\src\switch_scheduler.h" /> | |||
<ClInclude Include="..\librf\src\timer.h" /> | |||
<ClInclude Include="..\librf\src\unix\clang_builtin.h" /> | |||
<ClInclude Include="..\librf\src\unix\coroutine.h" /> | |||
<ClInclude Include="..\librf\src\utils.h" /> | |||
<ClInclude Include="..\librf\src\when.h" /> |
@@ -186,6 +186,9 @@ | |||
<ClInclude Include="..\librf\src\switch_scheduler.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\unix\clang_builtin.h"> | |||
<Filter>librf\src\unix</Filter> | |||
</ClInclude> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="..\librf\src\asio_task_1.12.0.inl"> |