@@ -186,7 +186,7 @@ | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
Copyright {yyyy} {name of copyright owner} | |||
Copyright 2017 lanzhengpeng | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. |
@@ -1 +1,24 @@ | |||
# librf | |||
# librf | |||
### librf - 协程库 | |||
librf是一个基于C++ Coroutines提案 ‘Stackless Resumable Functions’编写的非对称stackless协程库。 | |||
目前仅支持: | |||
Windows (使用VS2015/2016编译) | |||
librf有以下特点: | |||
* 1.基于C++17提案'Stackless Resumable Functions'编写的非对称stackless协程库,可以以同步的方式编写简单的代码,同时获得异步的性能 | |||
* 2.理论上支持海量协程, 创建100万个协程只需使用<待测试>物理内存 | |||
* 3.提供协程锁(mutex), 定时器, channel等特性, 帮助用户更加容易地编写程序 | |||
* 4.可以很好的跟asio,libuv等库结合,能更现有的callback范式的异步/延迟代码结合 | |||
* 5.目前还处于实验状态,不对今后正式的C++ Coroutines支持有任何正式的承诺 | |||
* 如果你发现了任何bug、有好的建议、或使用上有不明之处,可以提交到issue,也可以直接联系作者: | |||
email: tearshark@163.net QQ交流群: 296561497 | |||
* **doc目录下有作者搜集的一些关于C++协程的资料** | |||
* **tutorial目录下有针对每个特性的范例代码,让用户可以循序渐进的了解librf库的特性** |
@@ -0,0 +1,23 @@ | |||
/* | |||
*Copyright 2017 lanzhengpeng | |||
* | |||
*Licensed under the Apache License, Version 2.0 (the "License"); | |||
*you may not use this file except in compliance with the License. | |||
*You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
*Unless required by applicable law or agreed to in writing, software | |||
*distributed under the License is distributed on an "AS IS" BASIS, | |||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
*See the License for the specific language governing permissions and | |||
*limitations under the License. | |||
*/ | |||
#pragma once | |||
#include "src/event.h" | |||
#include "src/mutex.h" | |||
#include "src/channel.h" | |||
#include "src/scheduler.h" | |||
#include "src/sleep.h" |
@@ -0,0 +1,59 @@ | |||
#pragma once | |||
#include "spinlock.h" | |||
#include "future.h" | |||
namespace resumef | |||
{ | |||
namespace detail | |||
{ | |||
template<class _Ety, class... _Types> | |||
struct _awaker | |||
{ | |||
//如果超时 | |||
// e 始终为nullptr | |||
// 不关心返回值 | |||
//如果不是超时, | |||
// e 指向当前触发的事件,用于实现wait_any | |||
// 返回true表示成功触发了事件,event内部减小一次事件计数,并删除此awaker | |||
// 返回false表示此事件已经无效,event内部只删除此awaker | |||
typedef std::function<bool(_Ety * e, _Types...)> callee_type; | |||
private: | |||
spinlock _lock; | |||
callee_type _callee; | |||
std::atomic<intptr_t> _counter; | |||
public: | |||
_awaker(callee_type && callee_, intptr_t init_count_ = 0) | |||
: _callee(std::forward<callee_type>(callee_)) | |||
, _counter(init_count_) | |||
{ | |||
} | |||
//调用一次后,_callee就被置nullptr,下次再调用,必然返回false | |||
//第一次调用,返回调用_callee的返回值 | |||
//超时通过传入nullptr来调用 | |||
bool awake(_Ety * e, intptr_t count_, const _Types&... args) | |||
{ | |||
assert(count_ > 0); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
if ((this->_counter.fetch_sub(count_) - count_) <= 0) | |||
{ | |||
if (this->_callee) | |||
{ | |||
callee_type callee_ = std::move(this->_callee); | |||
return callee_(e, args...); | |||
} | |||
return false; | |||
} | |||
return true; | |||
} | |||
private: | |||
_awaker(const _awaker &) = delete; | |||
_awaker(_awaker &&) = delete; | |||
_awaker & operator = (const _awaker &) = delete; | |||
_awaker & operator = (_awaker &&) = delete; | |||
}; | |||
} | |||
} |
@@ -0,0 +1,150 @@ | |||
| |||
#pragma once | |||
#include "future.h" | |||
#include "asio/detail/config.hpp" | |||
#include <memory> | |||
#include "asio/detail/push_options.hpp" | |||
#include "asio/async_result.hpp" | |||
#include "asio/error_code.hpp" | |||
#include "asio/handler_type.hpp" | |||
#include "asio/system_error.hpp" | |||
namespace asio { | |||
template<typename Allocator = std::allocator<void> > | |||
class rf_task_t | |||
{ | |||
public: | |||
typedef Allocator allocator_type; | |||
constexpr rf_task_t() {} | |||
explicit rf_task_t(const Allocator& allocator) : allocator_(allocator) {} | |||
template<typename OtherAllocator> | |||
rf_task_t<OtherAllocator> operator[](const OtherAllocator& allocator) const { | |||
return rf_task_t<OtherAllocator>(allocator); | |||
} | |||
allocator_type get_allocator() const { return allocator_; } | |||
private: | |||
Allocator allocator_; | |||
}; | |||
//constexpr rf_task_t<> rf_task; | |||
#pragma warning(push) | |||
#pragma warning(disable : 4592) | |||
__declspec(selectany) rf_task_t<> rf_task; | |||
#pragma warning(pop) | |||
namespace detail { | |||
template<typename T> | |||
class promise_handler | |||
{ | |||
public: | |||
using result_type_t = T; | |||
using state_type = resumef::state_t<result_type_t>; | |||
template<typename Allocator> | |||
promise_handler(const rf_task_t<Allocator> &) | |||
: state_(resumef::make_counted<state_type>()) | |||
{ | |||
} | |||
void operator()(T t) const | |||
{ | |||
state_->set_value(std::move(t)); | |||
} | |||
void operator()(const asio::error_code& ec, T t) const | |||
{ | |||
if (!ec) | |||
{ | |||
state_->set_value(std::move(t)); | |||
} | |||
else | |||
{ | |||
state_->set_exception(std::make_exception_ptr(asio::system_error(ec))); | |||
} | |||
} | |||
resumef::counted_ptr<state_type> state_; | |||
}; | |||
template<> | |||
class promise_handler<void> | |||
{ | |||
public: | |||
using result_type_t = void; | |||
using state_type = resumef::state_t<result_type_t>; | |||
template<typename Allocator> | |||
promise_handler(const rf_task_t<Allocator> &) | |||
: state_(resumef::make_counted<state_type>()) | |||
{ | |||
} | |||
void operator()() const | |||
{ | |||
state_->set_value(); | |||
} | |||
void operator()(const asio::error_code& ec) const | |||
{ | |||
if (!ec) | |||
{ | |||
state_->set_value(); | |||
} | |||
else | |||
{ | |||
state_->set_exception(std::make_exception_ptr(asio::system_error(ec))); | |||
} | |||
} | |||
resumef::counted_ptr<state_type> state_; | |||
}; | |||
} // namespace detail | |||
template<typename T> | |||
class async_result<detail::promise_handler<T> > | |||
{ | |||
public: | |||
typedef resumef::future_t<T> type; | |||
explicit async_result(detail::promise_handler<T> & h) | |||
: task_(std::move(h.state_)) | |||
{ } | |||
resumef::future_t<T> get() { return std::move(task_); } | |||
private: | |||
resumef::future_t<T> task_; | |||
}; | |||
// Handler type specialisation for zero arg. | |||
template<typename Allocator, typename ReturnType> | |||
struct handler_type<rf_task_t<Allocator>, ReturnType()> { | |||
typedef detail::promise_handler<void> type; | |||
}; | |||
// Handler type specialisation for one arg. | |||
template<typename Allocator, typename ReturnType, typename Arg1> | |||
struct handler_type<rf_task_t<Allocator>, ReturnType(Arg1)> { | |||
typedef detail::promise_handler<Arg1> type; | |||
}; | |||
// Handler type specialisation for two arg. | |||
template<typename Allocator, typename ReturnType, typename Arg2> | |||
struct handler_type<rf_task_t<Allocator>, ReturnType(asio::error_code, Arg2)> { | |||
typedef detail::promise_handler<Arg2> type; | |||
}; | |||
template<typename Allocator, typename ReturnType> | |||
struct handler_type<rf_task_t<Allocator>, ReturnType(asio::error_code)> { | |||
typedef detail::promise_handler<void> type; | |||
}; | |||
} // namespace asio | |||
#include "asio/detail/pop_options.hpp" |
@@ -0,0 +1,260 @@ | |||
#pragma once | |||
#include "_awaker.h" | |||
namespace resumef | |||
{ | |||
namespace detail | |||
{ | |||
template<class _Ty> | |||
struct channel_impl : public std::enable_shared_from_this<channel_impl<_Ty>> | |||
{ | |||
typedef _awaker<channel_impl<_Ty>, _Ty *, future_error> channel_read_awaker; | |||
typedef std::shared_ptr<channel_read_awaker> channel_read_awaker_ptr; | |||
typedef _awaker<channel_impl<_Ty>> channel_write_awaker; | |||
typedef std::shared_ptr<channel_write_awaker> channel_write_awaker_ptr; | |||
typedef std::pair<channel_write_awaker_ptr, _Ty> write_tuple_type; | |||
private: | |||
spinlock _lock; //保证访问本对象是线程安全的 | |||
const size_t _max_counter; //数据队列的容量上限 | |||
std::deque<_Ty> _values; //数据队列 | |||
std::list<channel_read_awaker_ptr> _read_awakes; //读队列 | |||
std::list<write_tuple_type> _write_awakes; //写队列 | |||
public: | |||
channel_impl(size_t max_counter_) | |||
:_max_counter(max_counter_) | |||
{ | |||
} | |||
#if _DEBUG | |||
const std::deque<_Ty> & debug_queue() const | |||
{ | |||
return _values; | |||
} | |||
#endif | |||
template<class callee_t, class = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, channel_read_awaker_ptr>::value>> | |||
auto read(callee_t && awaker) | |||
{ | |||
return read_(std::make_shared<channel_read_awaker>(std::forward<callee_t>(awaker))); | |||
} | |||
template<class callee_t, class = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, channel_write_awaker_ptr>::value>> | |||
auto write(callee_t && awaker, const _Ty& val) | |||
{ | |||
return write_(std::make_shared<channel_write_awaker>(std::forward<callee_t>(awaker)), val); | |||
} | |||
template<class callee_t, class = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, channel_write_awaker_ptr>::value>> | |||
auto write(callee_t && awaker, _Ty&& val) | |||
{ | |||
return write_(std::make_shared<channel_write_awaker>(std::forward<callee_t>(awaker)), std::forward<_Ty>(val)); | |||
} | |||
//如果已经触发了awaker,则返回true | |||
bool read_(channel_read_awaker_ptr && r_awaker) | |||
{ | |||
assert(r_awaker); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
bool ret_value; | |||
if (_values.size() > 0) | |||
{ | |||
//如果数据队列有数据,则可以直接读数据 | |||
auto val = std::move(_values.front()); | |||
_values.pop_front(); | |||
r_awaker->awake(this, 1, &val, future_error::none); | |||
ret_value = true; | |||
} | |||
else | |||
{ | |||
//否则,将“读等待”放入“读队列” | |||
_read_awakes.push_back(r_awaker); | |||
ret_value = false; | |||
} | |||
//如果已有写队列,则唤醒一个“写等待” | |||
awake_one_writer_(); | |||
return ret_value; | |||
} | |||
void write_(channel_write_awaker_ptr && w_awaker, const _Ty& val) | |||
{ | |||
assert(w_awaker); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
//如果满了,则不添加到数据队列,而是将“写等待”和值,放入“写队列” | |||
bool is_full = _values.size() >= _max_counter; | |||
if (is_full) | |||
_write_awakes.push_back(std::make_pair(std::forward<channel_write_awaker_ptr>(w_awaker), val)); | |||
else | |||
_values.push_back(val); | |||
//如果已有读队列,则唤醒一个“读等待” | |||
awake_one_reader_(); | |||
//触发 没有放入“写队列”的“写等待” | |||
if (!is_full) w_awaker->awake(this, 1); | |||
} | |||
void write_(channel_write_awaker_ptr && w_awaker, _Ty&& val) | |||
{ | |||
assert(w_awaker); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
//如果满了,则不添加到数据队列,而是将“写等待”和值,放入“写队列” | |||
bool is_full = _values.size() >= _max_counter; | |||
if (is_full) | |||
_write_awakes.push_back(std::make_pair(std::forward<channel_write_awaker_ptr>(w_awaker), std::forward<_Ty>(val))); | |||
else | |||
_values.push_back(std::forward<_Ty>(val)); | |||
//如果已有读队列,则唤醒一个“读等待” | |||
awake_one_reader_(); | |||
//触发 没有放入“写队列”的“写等待” | |||
if(!is_full) w_awaker->awake(this, 1); | |||
} | |||
private: | |||
void awake_one_reader_() | |||
{ | |||
//assert(!(_read_awakes.size() >= 0 && _values.size() == 0)); | |||
for (auto iter = _read_awakes.begin(); iter != _read_awakes.end(); ) | |||
{ | |||
auto r_awaker = *iter; | |||
iter = _read_awakes.erase(iter); | |||
if (r_awaker->awake(this, 1, _values.size() ? &_values.front() : nullptr, future_error::read_before_write)) | |||
{ | |||
if(_values.size()) _values.pop_front(); | |||
//唤醒一个“读等待”后,尝试唤醒一个“写等待”,以处理“数据队列”满后的“写等待” | |||
awake_one_writer_(); | |||
break; | |||
} | |||
} | |||
} | |||
void awake_one_writer_() | |||
{ | |||
for (auto iter = _write_awakes.begin(); iter != _write_awakes.end(); ) | |||
{ | |||
auto w_awaker = std::move(*iter); | |||
iter = _write_awakes.erase(iter); | |||
if (w_awaker.first->awake(this, 1)) | |||
{ | |||
//一个“写等待”唤醒后,将“写等待”绑定的值,放入“数据队列” | |||
_values.push_back(std::move(w_awaker.second)); | |||
break; | |||
} | |||
} | |||
} | |||
channel_impl(const channel_impl &) = delete; | |||
channel_impl(channel_impl &&) = delete; | |||
channel_impl & operator = (const channel_impl &) = delete; | |||
channel_impl & operator = (channel_impl &&) = delete; | |||
}; | |||
} | |||
template<class _Ty> | |||
struct channel_t | |||
{ | |||
typedef detail::channel_impl<_Ty> channel_impl_type; | |||
typedef typename channel_impl_type::channel_read_awaker channel_read_awaker; | |||
typedef typename channel_impl_type::channel_write_awaker channel_write_awaker; | |||
typedef std::shared_ptr<channel_impl_type> channel_impl_ptr; | |||
typedef std::weak_ptr<channel_impl_type> channel_impl_wptr; | |||
typedef std::chrono::system_clock clock_type; | |||
private: | |||
channel_impl_ptr _chan; | |||
public: | |||
channel_t(size_t max_counter = 0) | |||
:_chan(std::make_shared<channel_impl_type>(max_counter)) | |||
{ | |||
} | |||
awaitable_t<bool> write(_Ty&& val) const | |||
{ | |||
awaitable_t<bool> awaitable; | |||
auto awaker = std::make_shared<channel_write_awaker>( | |||
[st = awaitable._state](channel_impl_type * chan) -> bool | |||
{ | |||
st->set_value(chan ? true : false); | |||
return true; | |||
}); | |||
_chan->write_(std::move(awaker), std::forward<_Ty>(val)); | |||
return awaitable; | |||
} | |||
awaitable_t<bool> write(const _Ty& val) const | |||
{ | |||
awaitable_t<bool> awaitable; | |||
auto awaker = std::make_shared<channel_write_awaker>( | |||
[st = awaitable._state](channel_impl_type * chan) -> bool | |||
{ | |||
st->set_value(chan ? true : false); | |||
return true; | |||
}); | |||
_chan->write_(std::move(awaker), val); | |||
return awaitable; | |||
} | |||
awaitable_t<_Ty> read() const | |||
{ | |||
awaitable_t<_Ty> awaitable; | |||
auto awaker = std::make_shared<channel_read_awaker>( | |||
[st = awaitable._state](channel_impl_type *, _Ty * val, future_error fe) -> bool | |||
{ | |||
if(val) | |||
st->set_value(std::move(*val)); | |||
else | |||
st->throw_exception(channel_exception{ fe }); | |||
return true; | |||
}); | |||
_chan->read_(std::move(awaker)); | |||
return awaitable; | |||
} | |||
awaitable_t<bool> operator << (_Ty&& val) const | |||
{ | |||
return std::move(write(std::forward<_Ty>(val))); | |||
} | |||
awaitable_t<bool> operator << (const _Ty& val) const | |||
{ | |||
return std::move(write(val)); | |||
} | |||
awaitable_t<_Ty> operator co_await () const | |||
{ | |||
return read(); | |||
} | |||
#if _DEBUG | |||
const auto & debug_queue() const | |||
{ | |||
return _chan->debug_queue(); | |||
} | |||
#endif | |||
channel_t(const channel_t &) = default; | |||
channel_t(channel_t &&) = default; | |||
channel_t & operator = (const channel_t &) = default; | |||
channel_t & operator = (channel_t &&) = default; | |||
}; | |||
typedef channel_t<bool> semaphore_t; | |||
} |
@@ -0,0 +1,107 @@ | |||
#pragma once | |||
#include <yvals.h> | |||
#include <atomic> | |||
#include <memory> | |||
#include <chrono> | |||
#include <thread> | |||
#include <list> | |||
#include <vector> | |||
#include <deque> | |||
#include <mutex> | |||
#include <functional> | |||
#include <map> | |||
#include <unordered_map> | |||
#include <unordered_set> | |||
#include <assert.h> | |||
#include <experimental/coroutine> | |||
//#include <experimental/generator> | |||
//替代<experimental/generator>,因为VS2015/VS2017的generator<>未实现return_value,导致yield后不能return | |||
#include "generator.h" | |||
#if defined(RESUMEF_DLL_EXPORT) | |||
# define RF_API __declspec(dllexport) | |||
#elif defined(RESUMEF_DLL_IMPORT) | |||
# define RF_API __declspec(dllimport) | |||
#else | |||
# define RF_API | |||
#endif | |||
#if RESUMEF_DEBUG_COUNTER | |||
extern std::atomic<intptr_t> g_resumef_state_count; | |||
extern std::atomic<intptr_t> g_resumef_task_count; | |||
extern std::atomic<intptr_t> g_resumef_evtctx_count; | |||
#endif | |||
namespace resumef | |||
{ | |||
#if _HAS_CXX17 | |||
template<class... _Mutexes> | |||
using scoped_lock = std::scoped_lock<_Mutexes...>; | |||
#else | |||
template<class... _Mutexes> | |||
using scoped_lock = std::lock_guard<_Mutexes...>; | |||
#endif | |||
enum struct future_error | |||
{ | |||
none, | |||
not_ready, // get_value called when value not available | |||
already_acquired, // attempt to get another future | |||
unlock_more, // unlock 次数多余lock次数 | |||
read_before_write, // 0容量的channel,先读后写 | |||
max__ | |||
}; | |||
const char * get_error_string(future_error fe, const char * classname); | |||
//const char * future_error_string[size_t(future_error::max__)]; | |||
struct future_exception : std::exception | |||
{ | |||
future_error _error; | |||
future_exception(future_error fe) | |||
: exception(get_error_string(fe, "future_exception")) | |||
, _error(fe) | |||
{ | |||
} | |||
}; | |||
struct lock_exception : std::exception | |||
{ | |||
future_error _error; | |||
lock_exception(future_error fe) | |||
: exception(get_error_string(fe, "lock_exception")) | |||
, _error(fe) | |||
{ | |||
} | |||
}; | |||
struct channel_exception : std::exception | |||
{ | |||
future_error _error; | |||
channel_exception(future_error fe) | |||
: exception(get_error_string(fe, "channel_exception")) | |||
, _error(fe) | |||
{ | |||
} | |||
}; | |||
struct scheduler; | |||
struct state_base; | |||
//获得当前线程下的调度器 | |||
extern scheduler * this_scheduler(); | |||
//获得当前线程下,正在由调度器调度的协程 | |||
//extern state_base * this_coroutine(); | |||
//namespace detail | |||
//{ | |||
// extern state_base * current_coroutine(); | |||
//} | |||
} | |||
#define co_yield_void co_yield nullptr | |||
#define co_return_void co_return nullptr |
@@ -0,0 +1,356 @@ | |||
#include "event.h" | |||
#include <assert.h> | |||
#include <mutex> | |||
#include "scheduler.h" | |||
namespace resumef | |||
{ | |||
namespace detail | |||
{ | |||
event_impl::event_impl(intptr_t initial_counter_) | |||
: _counter(initial_counter_) | |||
{ | |||
} | |||
void event_impl::signal() | |||
{ | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
++this->_counter; | |||
for (auto iter = this->_awakes.begin(); iter != this->_awakes.end(); ) | |||
{ | |||
auto awaker = *iter; | |||
iter = this->_awakes.erase(iter); | |||
if (awaker->awake(this, 1)) | |||
{ | |||
if (--this->_counter == 0) | |||
break; | |||
} | |||
} | |||
} | |||
void event_impl::reset() | |||
{ | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
this->_awakes.clear(); | |||
this->_counter = 0; | |||
} | |||
bool event_impl::wait_(const event_awaker_ptr & awaker) | |||
{ | |||
assert(awaker); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
if (this->_counter > 0) | |||
{ | |||
if(awaker->awake(this, 1)) | |||
{ | |||
--this->_counter; | |||
return true; | |||
} | |||
} | |||
else | |||
{ | |||
this->_awakes.push_back(awaker); | |||
} | |||
return false; | |||
} | |||
} | |||
event_t::event_t(intptr_t initial_counter_) | |||
: _event(std::make_shared<detail::event_impl>(initial_counter_)) | |||
{ | |||
} | |||
awaitable_t<bool> event_t::wait() const | |||
{ | |||
awaitable_t<bool> awaitable; | |||
auto awaker = std::make_shared<detail::event_awaker>( | |||
[st = awaitable._state](detail::event_impl * e) -> bool | |||
{ | |||
st->set_value(e ? true : false); | |||
return true; | |||
}); | |||
_event->wait_(awaker); | |||
return awaitable; | |||
} | |||
awaitable_t<bool> event_t::wait_until_(const clock_type::time_point & tp) const | |||
{ | |||
awaitable_t<bool> awaitable; | |||
auto awaker = std::make_shared<detail::event_awaker>( | |||
[st = awaitable._state](detail::event_impl * e) -> bool | |||
{ | |||
st->set_value(e ? true : false); | |||
return true; | |||
}); | |||
_event->wait_(awaker); | |||
g_scheduler.timer()->add(tp, | |||
[awaker](bool bValue) | |||
{ | |||
awaker->awake(nullptr, 1); | |||
}); | |||
return awaitable; | |||
} | |||
struct wait_any_awaker | |||
{ | |||
typedef state_t<intptr_t> state_type; | |||
counted_ptr<state_type> st; | |||
std::vector<detail::event_impl *> evts; | |||
wait_any_awaker(const counted_ptr<state_type> & st_, std::vector<detail::event_impl *> && evts_) | |||
: st(st_) | |||
, evts(std::forward<std::vector<detail::event_impl *>>(evts_)) | |||
{} | |||
wait_any_awaker(const wait_any_awaker &) = delete; | |||
wait_any_awaker(wait_any_awaker &&) = default; | |||
bool operator()(detail::event_impl * e) const | |||
{ | |||
if (e) | |||
{ | |||
for (auto i = evts.begin(); i != evts.end(); ++i) | |||
{ | |||
if (e == (*i)) | |||
{ | |||
st->set_value((intptr_t)(i - evts.begin())); | |||
return true; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
st->set_value(-1); | |||
} | |||
return false; | |||
} | |||
}; | |||
awaitable_t<intptr_t> event_t::wait_any_(std::vector<event_impl_ptr> && evts) | |||
{ | |||
awaitable_t<intptr_t> awaitable; | |||
if (evts.size() <= 0) | |||
{ | |||
awaitable._state->set_value(-1); | |||
return awaitable; | |||
} | |||
auto awaker = std::make_shared<detail::event_awaker>( | |||
[st = awaitable._state, evts](detail::event_impl * e) -> bool | |||
{ | |||
if (e) | |||
{ | |||
for (auto i = evts.begin(); i != evts.end(); ++i) | |||
{ | |||
if (e == (*i).get()) | |||
{ | |||
st->set_value((intptr_t)(i - evts.begin())); | |||
return true; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
st->set_value(-1); | |||
} | |||
return false; | |||
}); | |||
for (auto e : evts) | |||
{ | |||
e->wait_(awaker); | |||
} | |||
return awaitable; | |||
} | |||
awaitable_t<intptr_t> event_t::wait_any_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts) | |||
{ | |||
awaitable_t<intptr_t> awaitable; | |||
auto awaker = std::make_shared<detail::event_awaker>( | |||
[st = awaitable._state, evts](detail::event_impl * e) -> bool | |||
{ | |||
if (e) | |||
{ | |||
for (auto i = evts.begin(); i != evts.end(); ++i) | |||
{ | |||
if (e == (*i).get()) | |||
{ | |||
st->set_value((intptr_t)(i - evts.begin())); | |||
return true; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
st->set_value(-1); | |||
} | |||
return false; | |||
}); | |||
for (auto e : evts) | |||
{ | |||
e->wait_(awaker); | |||
} | |||
g_scheduler.timer()->add(tp, | |||
[awaker](bool bValue) | |||
{ | |||
awaker->awake(nullptr, 1); | |||
}); | |||
return awaitable; | |||
} | |||
awaitable_t<bool> event_t::wait_all_(std::vector<event_impl_ptr> && evts) | |||
{ | |||
awaitable_t<bool> awaitable; | |||
if (evts.size() <= 0) | |||
{ | |||
awaitable._state->set_value(false); | |||
return awaitable; | |||
} | |||
auto awaker = std::make_shared<detail::event_awaker>( | |||
[st = awaitable._state](detail::event_impl * e) -> bool | |||
{ | |||
st->set_value(e ? true : false); | |||
return true; | |||
}, | |||
evts.size()); | |||
for (auto e : evts) | |||
{ | |||
e->wait_(awaker); | |||
} | |||
return awaitable; | |||
} | |||
struct event_t::wait_all_ctx | |||
{ | |||
counted_ptr<state_t<bool>> st; | |||
std::vector<event_impl_ptr> evts; | |||
std::vector<event_impl_ptr> evts_waited; | |||
timer_handler th; | |||
spinlock _lock; | |||
wait_all_ctx() | |||
{ | |||
#if RESUMEF_DEBUG_COUNTER | |||
++g_resumef_evtctx_count; | |||
#endif | |||
} | |||
~wait_all_ctx() | |||
{ | |||
th.stop(); | |||
#if RESUMEF_DEBUG_COUNTER | |||
--g_resumef_evtctx_count; | |||
#endif | |||
} | |||
bool awake(detail::event_impl * eptr) | |||
{ | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
//如果st为nullptr,则说明之前已经返回过值了。本环境无效了。 | |||
if (!st.get()) | |||
return false; | |||
if (eptr) | |||
{ | |||
//记录已经等到的事件 | |||
evts_waited.emplace_back(eptr->shared_from_this()); | |||
//已经等到的事件达到预期 | |||
if (evts_waited.size() == evts.size()) | |||
{ | |||
evts_waited.clear(); | |||
//返回true表示等待成功 | |||
st->set_value(true); | |||
//丢弃st,以便于还有其它持有的ctx返回false | |||
st.reset(); | |||
//取消定时器 | |||
th.stop(); | |||
} | |||
} | |||
else | |||
{ | |||
//超时后,恢复已经等待的事件计数 | |||
for (auto sptr : evts_waited) | |||
{ | |||
sptr->signal(); | |||
} | |||
evts_waited.clear(); | |||
//返回true表示等待失败 | |||
st->set_value(false); | |||
//丢弃st,以便于还有其它持有的ctx返回false | |||
st.reset(); | |||
//定时器句柄已经无意义了 | |||
th.reset(); | |||
} | |||
return true; | |||
} | |||
}; | |||
//等待所有的事件 | |||
//超时后的行为应该表现为: | |||
//要么所有的事件计数减一,要么所有事件计数不动 | |||
//则需要超时后,恢复已经等待的事件计数 | |||
awaitable_t<bool> event_t::wait_all_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts) | |||
{ | |||
awaitable_t<bool> awaitable; | |||
if (evts.size() <= 0) | |||
{ | |||
g_scheduler.timer()->add_handler(tp, | |||
[st = awaitable._state](bool bValue) | |||
{ | |||
st->set_value(false); | |||
}); | |||
return awaitable; | |||
} | |||
auto ctx = std::make_shared<wait_all_ctx>(); | |||
ctx->st = awaitable._state; | |||
ctx->evts_waited.reserve(evts.size()); | |||
ctx->evts = std::move(evts); | |||
ctx->th = std::move(g_scheduler.timer()->add_handler(tp, | |||
[ctx](bool bValue) | |||
{ | |||
ctx->awake(nullptr); | |||
})); | |||
for (auto e : ctx->evts) | |||
{ | |||
auto awaker = std::make_shared<detail::event_awaker>( | |||
[ctx](detail::event_impl * eptr) -> bool | |||
{ | |||
return ctx->awake(eptr); | |||
}); | |||
e->wait_(awaker); | |||
} | |||
return awaitable; | |||
} | |||
} |
@@ -0,0 +1,207 @@ | |||
#pragma once | |||
#include "_awaker.h" | |||
namespace resumef | |||
{ | |||
namespace detail | |||
{ | |||
struct event_impl; | |||
typedef _awaker<event_impl> event_awaker; | |||
typedef std::shared_ptr<event_awaker> event_awaker_ptr; | |||
struct event_impl : public std::enable_shared_from_this<event_impl> | |||
{ | |||
private: | |||
std::list<event_awaker_ptr> _awakes; | |||
intptr_t _counter; | |||
spinlock _lock; | |||
public: | |||
RF_API event_impl(intptr_t initial_counter_); | |||
RF_API void signal(); | |||
RF_API void reset(); | |||
//如果已经触发了awaker,则返回true | |||
RF_API bool wait_(const event_awaker_ptr & awaker); | |||
template<class callee_t, class dummy_t = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, event_awaker_ptr>::value>> | |||
auto wait(callee_t && awaker, dummy_t * dummy_ = nullptr) | |||
{ | |||
return wait_(std::make_shared<event_awaker>(std::forward<callee_t>(awaker))); | |||
} | |||
event_impl(const event_impl &) = delete; | |||
event_impl(event_impl &&) = delete; | |||
event_impl & operator = (const event_impl &) = delete; | |||
event_impl & operator = (event_impl &&) = delete; | |||
}; | |||
} | |||
struct event_t | |||
{ | |||
typedef std::shared_ptr<detail::event_impl> event_impl_ptr; | |||
typedef std::weak_ptr<detail::event_impl> event_impl_wptr; | |||
typedef std::chrono::system_clock clock_type; | |||
private: | |||
event_impl_ptr _event; | |||
struct wait_all_ctx; | |||
public: | |||
RF_API event_t(intptr_t initial_counter_ = 0); | |||
void signal() const | |||
{ | |||
_event->signal(); | |||
} | |||
void reset() const | |||
{ | |||
_event->reset(); | |||
} | |||
RF_API awaitable_t<bool> | |||
wait() const; | |||
template<class _Rep, class _Period> | |||
awaitable_t<bool> | |||
wait_for(const std::chrono::duration<_Rep, _Period> & dt) const | |||
{ | |||
return wait_for_(std::chrono::duration_cast<clock_type::duration>(dt)); | |||
} | |||
template<class _Clock, class _Duration> | |||
awaitable_t<bool> | |||
wait_until(const std::chrono::time_point<_Clock, _Duration> & tp) const | |||
{ | |||
return wait_until_(std::chrono::time_point_cast<clock_type::duration>(tp)); | |||
} | |||
template<class _Iter> | |||
static awaitable_t<intptr_t> | |||
wait_any(_Iter begin_, _Iter end_) | |||
{ | |||
return wait_any_(make_event_vector(begin_, end_)); | |||
} | |||
template<class _Cont> | |||
static awaitable_t<intptr_t> | |||
wait_any(const _Cont & cnt_) | |||
{ | |||
return wait_any_(make_event_vector(std::begin(cnt_), std::end(cnt_))); | |||
} | |||
template<class _Rep, class _Period, class _Iter> | |||
static awaitable_t<intptr_t> | |||
wait_any_for(const std::chrono::duration<_Rep, _Period> & dt, _Iter begin_, _Iter end_) | |||
{ | |||
return wait_any_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(begin_, end_)); | |||
} | |||
template<class _Rep, class _Period, class _Cont> | |||
static awaitable_t<intptr_t> | |||
wait_any_for(const std::chrono::duration<_Rep, _Period> & dt, const _Cont & cnt_) | |||
{ | |||
return wait_any_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(std::begin(cnt_), std::end(cnt_))); | |||
} | |||
template<class _Clock, class _Duration, class _Iter> | |||
static awaitable_t<intptr_t> | |||
wait_any_until(const std::chrono::time_point<_Clock, _Duration> & tp, _Iter begin_, _Iter end_) | |||
{ | |||
return wait_any_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(begin_, end_)); | |||
} | |||
template<class _Clock, class _Duration, class _Cont> | |||
static awaitable_t<intptr_t> | |||
wait_any_until(const std::chrono::time_point<_Clock, _Duration> & tp, const _Cont & cnt_) | |||
{ | |||
return wait_any_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(std::begin(cnt_), std::end(cnt_))); | |||
} | |||
template<class _Iter> | |||
static awaitable_t<bool> | |||
wait_all(_Iter begin_, _Iter end_) | |||
{ | |||
return wait_all_(make_event_vector(begin_, end_)); | |||
} | |||
template<class _Cont> | |||
static awaitable_t<bool> | |||
wait_all(const _Cont & cnt_) | |||
{ | |||
return wait_all(std::begin(cnt_), std::end(cnt_)); | |||
} | |||
template<class _Rep, class _Period, class _Iter> | |||
static awaitable_t<bool> | |||
wait_all_for(const std::chrono::duration<_Rep, _Period> & dt, _Iter begin_, _Iter end_) | |||
{ | |||
return wait_all_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(begin_, end_)); | |||
} | |||
template<class _Rep, class _Period, class _Cont> | |||
static awaitable_t<bool> | |||
wait_all_for(const std::chrono::duration<_Rep, _Period> & dt, const _Cont & cnt_) | |||
{ | |||
return wait_all_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(std::begin(cnt_), std::end(cnt_))); | |||
} | |||
template<class _Clock, class _Duration, class _Iter> | |||
static awaitable_t<bool> | |||
wait_all_until(const std::chrono::time_point<_Clock, _Duration> & tp, _Iter begin_, _Iter end_) | |||
{ | |||
return wait_all_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(begin_, end_)); | |||
} | |||
template<class _Clock, class _Duration, class _Cont> | |||
static awaitable_t<bool> | |||
wait_all_until(const std::chrono::time_point<_Clock, _Duration> & tp, const _Cont & cnt_) | |||
{ | |||
return wait_all_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(std::begin(cnt_), std::end(cnt_))); | |||
} | |||
RF_API event_t(const event_t &) = default; | |||
RF_API event_t(event_t &&) = default; | |||
RF_API event_t & operator = (const event_t &) = default; | |||
RF_API event_t & operator = (event_t &&) = default; | |||
private: | |||
template<class _Iter> | |||
static std::vector<event_impl_ptr> make_event_vector(_Iter begin_, _Iter end_) | |||
{ | |||
std::vector<event_impl_ptr> evts; | |||
evts.reserve(std::distance(begin_, end_)); | |||
for (auto i = begin_; i != end_; ++i) | |||
evts.push_back((*i)._event); | |||
return std::move(evts); | |||
} | |||
inline awaitable_t<bool> wait_for_(const clock_type::duration & dt) const | |||
{ | |||
return wait_until_(clock_type::now() + dt); | |||
} | |||
RF_API awaitable_t<bool> wait_until_(const clock_type::time_point & tp) const; | |||
RF_API static awaitable_t<intptr_t> wait_any_(std::vector<event_impl_ptr> && evts); | |||
inline static awaitable_t<intptr_t> wait_any_for_(const clock_type::duration & dt, std::vector<event_impl_ptr> && evts) | |||
{ | |||
return wait_any_until_(clock_type::now() + dt, std::forward<std::vector<event_impl_ptr>>(evts)); | |||
} | |||
RF_API static awaitable_t<intptr_t> wait_any_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts); | |||
RF_API static awaitable_t<bool> wait_all_(std::vector<event_impl_ptr> && evts); | |||
inline static awaitable_t<bool> wait_all_for_(const clock_type::duration & dt, std::vector<event_impl_ptr> && evts) | |||
{ | |||
return wait_all_until_(clock_type::now() + dt, std::forward<std::vector<event_impl_ptr>>(evts)); | |||
} | |||
RF_API static awaitable_t<bool> wait_all_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts); | |||
}; | |||
} |
@@ -0,0 +1,345 @@ | |||
| |||
#pragma once | |||
#include "state.h" | |||
namespace resumef | |||
{ | |||
template <typename T = void> | |||
struct promise_t; | |||
template <typename T> | |||
struct future_impl_t | |||
{ | |||
typedef promise_t<T> promise_type; | |||
typedef state_t<T> state_type; | |||
counted_ptr<state_type> _state; | |||
future_impl_t(const counted_ptr<state_type>& state) : _state(state) | |||
{ | |||
_state->_future_acquired = true; | |||
} | |||
// movable, but not copyable | |||
future_impl_t() = default; | |||
future_impl_t(future_impl_t&& f) = default; | |||
future_impl_t & operator = (future_impl_t&& f) = default; | |||
future_impl_t(const future_impl_t&) = delete; | |||
future_impl_t & operator = (const future_impl_t&) = delete; | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
bool await_ready() | |||
{ | |||
return _state->_ready; | |||
} | |||
void await_suspend(std::experimental::coroutine_handle<> resume_cb) | |||
{ | |||
_state->await_suspend(resume_cb); | |||
} | |||
//return_value, return_void只能由派生类去实现 | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
//if ready, can get value | |||
bool ready() | |||
{ | |||
return _state->_ready; | |||
} | |||
auto & get_value() | |||
{ | |||
return _state->get_value(); | |||
} | |||
bool is_suspend() const | |||
{ | |||
return _state->is_suspend(); | |||
} | |||
bool cancellation_requested() const | |||
{ | |||
return _state->cancellation_requested(); | |||
} | |||
void set_exception(std::exception_ptr e_) | |||
{ | |||
_state->set_exception(std::move(e_)); | |||
} | |||
void cancel() | |||
{ | |||
_state->cancel(); | |||
} | |||
}; | |||
template <typename T = void> | |||
struct future_t : public future_impl_t<T> | |||
{ | |||
future_t(const counted_ptr<state_type>& state) | |||
: future_impl_t<T>(state) | |||
{ | |||
} | |||
// movable, but not copyable | |||
future_t(const future_t&) = delete; | |||
future_t(future_t&& f) = default; | |||
future_t() = default; | |||
future_t & operator = (const future_t&) = delete; | |||
future_t & operator = (future_t&& f) = default; | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
T await_resume() | |||
{ | |||
_state->rethrow_if_exception(); | |||
return std::move(_state->get_value()); | |||
} | |||
void return_value(const T& val) | |||
{ | |||
_state->set_value(val); | |||
} | |||
void return_value(T&& val) | |||
{ | |||
_state->set_value(std::forward<T>(val)); | |||
} | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
template <> | |||
struct future_t<void> : public future_impl_t<void> | |||
{ | |||
future_t(const counted_ptr<state_type>& state) | |||
: future_impl_t<void>(state) | |||
{ | |||
} | |||
// movable, but not copyable | |||
future_t(const future_t&) = delete; | |||
future_t(future_t&& f) = default; | |||
future_t() = default; | |||
future_t & operator = (const future_t&) = delete; | |||
future_t & operator = (future_t&& f) = default; | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
void await_resume() | |||
{ | |||
_state->rethrow_if_exception(); | |||
return _state->get_value(); | |||
} | |||
void return_void() | |||
{ | |||
_state->set_value(); | |||
} | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
using future_vt = future_t<void>; | |||
template <typename T> | |||
struct promise_impl_t | |||
{ | |||
typedef future_t<T> future_type; | |||
typedef state_t<T> state_type; | |||
counted_ptr<state_type> _state; | |||
// movable not copyable | |||
promise_impl_t() | |||
: _state(make_counted<state_type>()) | |||
{ | |||
} | |||
promise_impl_t(promise_impl_t&&) = default; | |||
promise_impl_t & operator = (promise_impl_t&&) = default; | |||
promise_impl_t(const promise_impl_t&) = delete; | |||
promise_impl_t & operator = (const promise_impl_t&) = delete; | |||
//这个函数,用于callback里,返回关联的future对象 | |||
//callback里,不应该调用 get_return_object() | |||
future_type get_future() | |||
{ | |||
if (_state->_future_acquired) | |||
throw future_exception{ future_error::already_acquired }; | |||
return future_type(_state); | |||
} | |||
// Most functions don't need this but timers and reads from streams | |||
// cause multiple callbacks. | |||
future_type next_future() | |||
{ | |||
// reset and return another future | |||
if (_state->_future_acquired) | |||
_state->reset(); | |||
return future_type(_state); | |||
} | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
//如果在协程外启动一个resumable function,则: | |||
// 1、即将交给调度器调度 | |||
// 2、手工获取结果(将来支持) | |||
// 无论哪种情况,都返回未准备好。则编译器调用await_suspend()获得继续运行的resume_cb | |||
//如果在协程内启动一个resumable function,则: | |||
// 1、即将交给调度器调度 | |||
// 2、通过await启动另外一个子函数 | |||
// (1)情况下,无法区分是否已经拥有的resume_cb,可以特殊处理 | |||
// (2)情况下,返回准备好了,让编译器继续运行 | |||
std::experimental::suspend_never initial_suspend() noexcept | |||
{ | |||
return {}; | |||
/* | |||
struct AWaitor | |||
{ | |||
counted_ptr<state_tt> _state; | |||
bool await_ready() _NOEXCEPT | |||
{ | |||
return false; | |||
} | |||
void await_suspend(std::experimental::coroutine_handle<> resume_cb) _NOEXCEPT | |||
{ | |||
_state->await_suspend(resume_cb); | |||
_state->run_in_coroutine(this_coroutine()); | |||
} | |||
void await_resume() _NOEXCEPT | |||
{ | |||
} | |||
}; | |||
return AWaitor{ _state }; | |||
*/ | |||
} | |||
//这在一个协程被销毁之时调用。 | |||
//我们选择不挂起协程,只是通知state的对象,本协程已经准备好了删除了 | |||
std::experimental::suspend_never final_suspend() noexcept | |||
{ | |||
_state->final_suspend(); | |||
return{}; | |||
} | |||
//返回与之关联的future对象 | |||
future_type get_return_object() | |||
{ | |||
return future_type(_state); | |||
} | |||
void set_exception(std::exception_ptr e_) | |||
{ | |||
_state->set_exception(std::move(e_)); | |||
} | |||
bool cancellation_requested() | |||
{ | |||
return _state->cancellation_requested(); | |||
} | |||
//return_value/return_void 只能由派生类去实现 | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
template <typename T> | |||
struct promise_t : public promise_impl_t<T> | |||
{ | |||
typedef promise_t<T> promise_type; | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
void return_value(const T& val) | |||
{ | |||
_state->set_value(val); | |||
} | |||
void return_value(T&& val) | |||
{ | |||
_state->set_value(std::forward<T>(val)); | |||
} | |||
void yield_value(const T& val) | |||
{ | |||
_state->set_value(val); | |||
} | |||
void yield_value(T&& val) | |||
{ | |||
_state->set_value(std::forward<T>(val)); | |||
} | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
template <> | |||
struct promise_t<void> : public promise_impl_t<void> | |||
{ | |||
typedef promise_t<void> promise_type; | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
void return_void() | |||
{ | |||
_state->set_value(); | |||
} | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
using promise_vt = promise_t<void>; | |||
template <typename T = void> | |||
struct awaitable_t | |||
{ | |||
typedef state_t<T> state_type; | |||
counted_ptr<state_type> _state; | |||
// movable not copyable | |||
awaitable_t() | |||
: _state(make_counted<state_type>()) | |||
{ | |||
} | |||
// movable, but not copyable | |||
awaitable_t(const awaitable_t&) = delete; | |||
awaitable_t(awaitable_t&& f) = default; | |||
awaitable_t & operator = (const awaitable_t&) = delete; | |||
awaitable_t & operator = (awaitable_t&& f) = default; | |||
//------------------------------------------------------------------------------------------ | |||
//以下是与编译器生成的resumable function交互的接口 | |||
bool await_ready() | |||
{ | |||
return _state->_ready; | |||
} | |||
void await_suspend(std::experimental::coroutine_handle<> resume_cb) | |||
{ | |||
_state->await_suspend(resume_cb); | |||
} | |||
T await_resume() | |||
{ | |||
_state->rethrow_if_exception(); | |||
return _state->get_value(); | |||
} | |||
//以上是与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
using awaitable_vt = awaitable_t<void>; | |||
} | |||
@@ -0,0 +1,239 @@ | |||
/*** | |||
*generator | |||
* | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* | |||
* Purpose: Library support of coroutines. generator class | |||
* http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0057r0.pdf | |||
* | |||
* [Public] | |||
* | |||
****/ | |||
#pragma once | |||
#ifndef _EXPERIMENTAL_GENERATOR_ | |||
#define _EXPERIMENTAL_GENERATOR_ | |||
#ifndef RC_INVOKED | |||
#ifndef _RESUMABLE_FUNCTIONS_SUPPORTED | |||
#error <experimental/generator> requires /await compiler option | |||
#endif /* _RESUMABLE_FUNCTIONS_SUPPORTED */ | |||
#include <experimental/resumable> | |||
#pragma pack(push, _CRT_PACKING) | |||
#pragma push_macro("new") | |||
#undef new | |||
_STD_BEGIN | |||
namespace experimental { | |||
template <typename _Ty, typename promise_type> | |||
struct generator_iterator; | |||
template<typename promise_type> | |||
struct generator_iterator<void, promise_type> | |||
{ | |||
typedef _STD input_iterator_tag iterator_category; | |||
typedef ptrdiff_t difference_type; | |||
coroutine_handle<promise_type> _Coro; | |||
generator_iterator(nullptr_t) : _Coro(nullptr) | |||
{ | |||
} | |||
generator_iterator(coroutine_handle<promise_type> _CoroArg) : _Coro(_CoroArg) | |||
{ | |||
} | |||
generator_iterator &operator++() | |||
{ | |||
if (_Coro.done()) | |||
_Coro = nullptr; | |||
else | |||
_Coro.resume(); | |||
return *this; | |||
} | |||
generator_iterator operator++(int) = delete; | |||
// generator iterator current_value | |||
// is a reference to a temporary on the coroutine frame | |||
// implementing postincrement will require storing a copy | |||
// of the value in the iterator. | |||
//{ | |||
// auto _Result = *this; | |||
// ++(*this); | |||
// return _Result; | |||
//} | |||
bool operator==(generator_iterator const &right_) const | |||
{ | |||
return _Coro == right_._Coro; | |||
} | |||
bool operator!=(generator_iterator const &right_) const | |||
{ | |||
return !(*this == right_); | |||
} | |||
}; | |||
template <typename promise_type> | |||
struct generator_iterator<nullptr_t, promise_type> : public generator_iterator<void, promise_type> | |||
{ | |||
generator_iterator(nullptr_t) : generator_iterator<void, promise_type>(nullptr) | |||
{ | |||
} | |||
generator_iterator(coroutine_handle<promise_type> _CoroArg) : generator_iterator<void, promise_type>(_CoroArg) | |||
{ | |||
} | |||
}; | |||
template <typename _Ty, typename promise_type> | |||
struct generator_iterator : public generator_iterator<void, promise_type> | |||
{ | |||
generator_iterator(nullptr_t) : generator_iterator<void, promise_type>(nullptr) | |||
{ | |||
} | |||
generator_iterator(coroutine_handle<promise_type> _CoroArg) : generator_iterator<void, promise_type>(_CoroArg) | |||
{ | |||
} | |||
_Ty const &operator*() const | |||
{ | |||
return *_Coro.promise()._CurrentValue; | |||
} | |||
_Ty const *operator->() const | |||
{ | |||
return _STD addressof(operator*()); | |||
} | |||
}; | |||
template <typename _Ty, typename _Alloc = allocator<char>> | |||
struct generator | |||
{ | |||
struct promise_type | |||
{ | |||
_Ty const *_CurrentValue; | |||
promise_type &get_return_object() | |||
{ | |||
return *this; | |||
} | |||
bool initial_suspend() | |||
{ | |||
return (true); | |||
} | |||
bool final_suspend() | |||
{ | |||
return (true); | |||
} | |||
void yield_value(_Ty const &_Value) | |||
{ | |||
_CurrentValue = _STD addressof(_Value); | |||
} | |||
template<class = _STD enable_if_t<!is_same_v<_Ty, void>, _Ty>> | |||
void return_value(_Ty const &_Value) | |||
{ | |||
_CurrentValue = _STD addressof(_Value); | |||
} | |||
template<class = _STD enable_if_t<is_same_v<_Ty, void>, _Ty>> | |||
void return_value() | |||
{ | |||
_CurrentValue = nullptr; | |||
} | |||
template <typename _Uty> | |||
_Uty && await_transform(_Uty &&_Whatever) | |||
{ | |||
static_assert(_Always_false<_Uty>::value, | |||
"co_await is not supported in coroutines of type std::experiemental::generator"); | |||
return _STD forward<_Uty>(_Whatever); | |||
} | |||
using _Alloc_traits = allocator_traits<_Alloc>; | |||
using _Alloc_of_char_type = | |||
typename _Alloc_traits::template rebind_alloc<char>; | |||
void *operator new(size_t _Size) | |||
{ | |||
_Alloc_of_char_type _Al; | |||
return _Al.allocate(_Size); | |||
} | |||
void operator delete(void *_Ptr, size_t _Size)_NOEXCEPT | |||
{ | |||
_Alloc_of_char_type _Al; | |||
return _Al.deallocate(static_cast<char *>(_Ptr), _Size); | |||
} | |||
}; | |||
typedef generator_iterator<_Ty, promise_type> iterator; | |||
iterator begin() | |||
{ | |||
if (_Coro) | |||
{ | |||
if (_Coro.done()) | |||
return{ nullptr }; | |||
_Coro.resume(); | |||
} | |||
return { _Coro }; | |||
} | |||
iterator end() | |||
{ | |||
return{ nullptr }; | |||
} | |||
explicit generator(promise_type &_Prom) | |||
: _Coro(coroutine_handle<promise_type>::from_promise( | |||
_Prom)) | |||
{ | |||
} | |||
generator() = default; | |||
generator(generator const &) = delete; | |||
generator &operator=(generator const &) = delete; | |||
generator(generator &&right_) : _Coro(right_._Coro) | |||
{ | |||
right_._Coro = nullptr; | |||
} | |||
generator &operator=(generator &&right_) | |||
{ | |||
if (&right_ != this) { | |||
_Coro = right_._Coro; | |||
right_._Coro = nullptr; | |||
} | |||
return *this; | |||
} | |||
~generator() | |||
{ | |||
if (_Coro) { | |||
_Coro.destroy(); | |||
} | |||
} | |||
private: | |||
coroutine_handle<promise_type> _Coro = nullptr; | |||
}; | |||
} // namespace experimental | |||
_STD_END | |||
#pragma pop_macro("new") | |||
#pragma pack(pop) | |||
#endif /* RC_INVOKED */ | |||
#endif /* _EXPERIMENTAL_GENERATOR_ */ |
@@ -0,0 +1,126 @@ | |||
#include "mutex.h" | |||
#include <assert.h> | |||
#include "scheduler.h" | |||
namespace resumef | |||
{ | |||
namespace detail | |||
{ | |||
mutex_impl::mutex_impl() | |||
{ | |||
} | |||
void mutex_impl::unlock() | |||
{ | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
if (_owner != nullptr) | |||
{ | |||
for (auto iter = _awakes.begin(); iter != _awakes.end(); ) | |||
{ | |||
auto awaker = *iter; | |||
iter = _awakes.erase(iter); | |||
if (awaker->awake(this, 1)) | |||
{ | |||
_owner = awaker; | |||
break; | |||
} | |||
} | |||
if (_awakes.size() == 0) | |||
{ | |||
_owner = nullptr; | |||
} | |||
} | |||
} | |||
bool mutex_impl::lock_(const mutex_awaker_ptr & awaker) | |||
{ | |||
assert(awaker); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
if (_owner == nullptr) | |||
{ | |||
_owner = awaker; | |||
awaker->awake(this, 1); | |||
return true; | |||
} | |||
else | |||
{ | |||
_awakes.push_back(awaker); | |||
return false; | |||
} | |||
} | |||
bool mutex_impl::try_lock_(const mutex_awaker_ptr & awaker) | |||
{ | |||
assert(awaker); | |||
scoped_lock<spinlock> lock_(this->_lock); | |||
if (_owner == nullptr) | |||
{ | |||
_owner = awaker; | |||
return true; | |||
} | |||
else | |||
{ | |||
return false; | |||
} | |||
} | |||
} | |||
mutex_t::mutex_t() | |||
: _locker(std::make_shared<detail::mutex_impl>()) | |||
{ | |||
} | |||
awaitable_t<bool> mutex_t::lock() const | |||
{ | |||
awaitable_t<bool> awaitable; | |||
auto awaker = std::make_shared<detail::mutex_awaker>( | |||
[st = awaitable._state](detail::mutex_impl * e) -> bool | |||
{ | |||
st->set_value(e ? true : false); | |||
return true; | |||
}); | |||
_locker->lock_(awaker); | |||
return awaitable; | |||
} | |||
bool mutex_t::try_lock() const | |||
{ | |||
auto dummy_awaker = std::make_shared<detail::mutex_awaker>( | |||
[](detail::mutex_impl * ) -> bool | |||
{ | |||
return true; | |||
}); | |||
return _locker->try_lock_(dummy_awaker); | |||
} | |||
awaitable_t<bool> mutex_t::try_lock_until_(const clock_type::time_point & tp) const | |||
{ | |||
awaitable_t<bool> awaitable; | |||
auto awaker = std::make_shared<detail::mutex_awaker>( | |||
[st = awaitable._state](detail::mutex_impl * e) -> bool | |||
{ | |||
st->set_value(e ? true : false); | |||
return true; | |||
}); | |||
_locker->lock_(awaker); | |||
g_scheduler.timer()->add(tp, | |||
[awaker](bool bValue) | |||
{ | |||
awaker->awake(nullptr, 1); | |||
}); | |||
return awaitable; | |||
} | |||
} |
@@ -0,0 +1,92 @@ | |||
#pragma once | |||
#include "_awaker.h" | |||
namespace resumef | |||
{ | |||
namespace detail | |||
{ | |||
struct mutex_impl; | |||
typedef _awaker<mutex_impl> mutex_awaker; | |||
typedef std::shared_ptr<mutex_awaker> mutex_awaker_ptr; | |||
struct mutex_impl : public std::enable_shared_from_this<mutex_impl> | |||
{ | |||
private: | |||
std::list<mutex_awaker_ptr> _awakes; | |||
mutex_awaker_ptr _owner; | |||
spinlock _lock; | |||
public: | |||
RF_API mutex_impl(); | |||
//如果已经触发了awaker,则返回true | |||
RF_API bool lock_(const mutex_awaker_ptr & awaker); | |||
RF_API bool try_lock_(const mutex_awaker_ptr & awaker); | |||
RF_API void unlock(); | |||
template<class callee_t, class dummy_t = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, mutex_awaker_ptr>::value>> | |||
auto lock(callee_t && awaker, dummy_t * dummy_ = nullptr) | |||
{ | |||
return lock_(std::make_shared<mutex_awaker>(std::forward<callee_t>(awaker))); | |||
} | |||
private: | |||
mutex_impl(const mutex_impl &) = delete; | |||
mutex_impl(mutex_impl &&) = delete; | |||
mutex_impl & operator = (const mutex_impl &) = delete; | |||
mutex_impl & operator = (mutex_impl &&) = delete; | |||
}; | |||
} | |||
struct mutex_t | |||
{ | |||
typedef std::shared_ptr<detail::mutex_impl> lock_impl_ptr; | |||
typedef std::weak_ptr<detail::mutex_impl> lock_impl_wptr; | |||
typedef std::chrono::system_clock clock_type; | |||
private: | |||
lock_impl_ptr _locker; | |||
public: | |||
RF_API mutex_t(); | |||
void unlock() const | |||
{ | |||
_locker->unlock(); | |||
} | |||
RF_API awaitable_t<bool> | |||
lock() const; | |||
RF_API bool | |||
try_lock() const; | |||
/* | |||
template<class _Rep, class _Period> | |||
awaitable_t<bool> | |||
try_lock_for(const std::chrono::duration<_Rep, _Period> & dt) const | |||
{ | |||
return try_lock_for_(std::chrono::duration_cast<clock_type::duration>(dt)); | |||
} | |||
template<class _Clock, class _Duration> | |||
awaitable_t<bool> | |||
try_lock_until(const std::chrono::time_point<_Clock, _Duration> & tp) const | |||
{ | |||
return try_lock_until_(std::chrono::time_point_cast<clock_type::duration>(tp)); | |||
} | |||
*/ | |||
RF_API mutex_t(const mutex_t &) = default; | |||
RF_API mutex_t(mutex_t &&) = default; | |||
RF_API mutex_t & operator = (const mutex_t &) = default; | |||
RF_API mutex_t & operator = (mutex_t &&) = default; | |||
private: | |||
inline awaitable_t<bool> try_lock_for_(const clock_type::duration & dt) const | |||
{ | |||
return try_lock_until_(clock_type::now() + dt); | |||
} | |||
RF_API awaitable_t<bool> try_lock_until_(const clock_type::time_point & tp) const; | |||
}; | |||
#define resumf_guard_lock(lker) (lker).lock(); resumef::scoped_lock<resumef::mutex_t> __resumf_guard##lker##__((lker), std::adopt_lock) | |||
} |
@@ -0,0 +1,78 @@ | |||
| |||
#include "rf_task.h" | |||
#include "scheduler.h" | |||
#include <assert.h> | |||
namespace resumef | |||
{ | |||
task_base::task_base() | |||
{ | |||
#if RESUMEF_DEBUG_COUNTER | |||
++g_resumef_task_count; | |||
#endif | |||
} | |||
task_base::~task_base() | |||
{ | |||
#if RESUMEF_DEBUG_COUNTER | |||
--g_resumef_task_count; | |||
#endif | |||
} | |||
template<class state_tt> | |||
struct awaitable_task_t : public task_base | |||
{ | |||
counted_ptr<state_tt> _state; | |||
awaitable_task_t() {} | |||
awaitable_task_t(state_tt * state) | |||
: _state(state) | |||
{ | |||
} | |||
virtual bool is_suspend() override | |||
{ | |||
return !_state->ready(); | |||
} | |||
virtual bool go_next(scheduler * schdler) override | |||
{ | |||
_state->resume(); | |||
return false; | |||
} | |||
virtual void cancel() override | |||
{ | |||
_state->cancel(); | |||
} | |||
virtual void * get_id() override | |||
{ | |||
return _state.get(); | |||
} | |||
}; | |||
state_base::~state_base() | |||
{ | |||
#if RESUMEF_DEBUG_COUNTER | |||
--g_resumef_state_count; | |||
#endif | |||
} | |||
void state_base::set_value_none_lock() | |||
{ | |||
// Set all members first as calling coroutine may reset stuff here. | |||
_ready = true; | |||
this_scheduler()->push_task_internal(new awaitable_task_t<state_base>(this)); | |||
} | |||
void state_base::set_exception(std::exception_ptr && e_) | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
_exception = std::move(e_); | |||
// Set all members first as calling coroutine may reset stuff here. | |||
_ready = true; | |||
this_scheduler()->push_task_internal(new awaitable_task_t<state_base>(this)); | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
#pragma once | |||
#include "def.h" | |||
#include "future.h" | |||
namespace resumef | |||
{ | |||
struct task_base; | |||
struct task_base | |||
{ | |||
RF_API task_base(); | |||
RF_API virtual ~task_base(); | |||
//如果返回true,则不会调用go_next() | |||
virtual bool is_suspend() = 0; | |||
//返回true,表示任务还未完成,后续还需要继续执行 | |||
//否则,任务从调度器里删除 | |||
virtual bool go_next(scheduler *) = 0; | |||
virtual void cancel() = 0; | |||
virtual void * get_id() = 0; | |||
}; | |||
//---------------------------------------------------------------------------------------------- | |||
template<class _Ty> | |||
struct task_t; | |||
//co_task接受的是一个experimental::generator<_Ty>类型,是调用一个支持异步的函数后返回的结果 | |||
template<class _Ty> | |||
struct task_t<std::experimental::generator<_Ty> > : public task_base | |||
{ | |||
typedef std::experimental::generator<_Ty> future_type; | |||
typedef typename future_type::iterator iterator_type; | |||
future_type _future; | |||
iterator_type _iterator; | |||
bool _ready; | |||
task_t() | |||
: _iterator(nullptr) | |||
, _ready(false) | |||
{ | |||
} | |||
task_t(future_type && f) | |||
: _future(std::forward<future_type>(f)) | |||
, _iterator(nullptr) | |||
, _ready(false) | |||
{ | |||
} | |||
virtual bool is_suspend() override | |||
{ | |||
return false; | |||
} | |||
virtual bool go_next(scheduler *) override | |||
{ | |||
if (!this->_ready) | |||
{ | |||
this->_iterator = this->_future.begin(); | |||
this->_ready = true; | |||
} | |||
if (this->_iterator != this->_future.end()) | |||
{ | |||
return (++this->_iterator) != this->_future.end(); | |||
} | |||
return false; | |||
} | |||
virtual void cancel() override | |||
{ | |||
} | |||
virtual void * get_id() override | |||
{ | |||
return nullptr; | |||
} | |||
}; | |||
template<class _Ty> | |||
struct task_t<future_t<_Ty> > : public task_base | |||
{ | |||
typedef future_t<_Ty> future_type; | |||
future_type _future; | |||
task_t() = default; | |||
task_t(future_type && f) | |||
: _future(std::forward<future_type>(f)) | |||
{ | |||
} | |||
//如果返回true,则不会调用go_next() | |||
// | |||
//如果第一次运行,则state应该有: | |||
// _coro != nullptr | |||
// _ready == false | |||
//运行一次后,则state应该是: | |||
// _coro == nullptr | |||
// _ready == false | |||
//最后一次运行,则state应该是: | |||
// _coro == nullptr | |||
// _ready == true | |||
virtual bool is_suspend() override | |||
{ | |||
auto * _state = _future._state.get(); | |||
return _state->is_suspend(); | |||
} | |||
//返回true,表示任务还未完成,后续还需要继续执行 | |||
//否则,任务从调度器里删除 | |||
virtual bool go_next(scheduler * schdler) override | |||
{ | |||
auto * _state = _future._state.get(); | |||
_state->resume(); | |||
return !_state->ready() && !_state->_done; | |||
} | |||
virtual void cancel() override | |||
{ | |||
_future.cancel(); | |||
} | |||
virtual void * get_id() override | |||
{ | |||
return _future._state.get(); | |||
} | |||
}; | |||
//---------------------------------------------------------------------------------------------- | |||
//co_task_with_ctx接受的是一个'函数对象' | |||
//这个'函数对象'被调用后,返回generator<_Ty>/future_t<_Ty>类型 | |||
//然后'函数对象'作为异步执行的上下文状态保存起来 | |||
template<class _Ctx> | |||
struct ctx_task_t : public task_t<typename std::result_of<_Ctx()>::type> | |||
{ | |||
typedef _Ctx context_type; | |||
context_type _context; | |||
ctx_task_t(context_type && ctx) | |||
: _context(std::forward<context_type>(ctx)) | |||
{ | |||
_future = std::move(_context()); | |||
} | |||
}; | |||
} |
@@ -0,0 +1,175 @@ | |||
#include "scheduler.h" | |||
#include <assert.h> | |||
#if RESUMEF_DEBUG_COUNTER | |||
std::atomic<intptr_t> g_resumef_state_count = 0; | |||
std::atomic<intptr_t> g_resumef_task_count = 0; | |||
std::atomic<intptr_t> g_resumef_evtctx_count = 0; | |||
#endif | |||
namespace resumef | |||
{ | |||
static const char * future_error_string[(size_t)future_error::max__] | |||
{ | |||
"none", | |||
"not_ready", | |||
"already_acquired", | |||
"unlock_more", | |||
"read_before_write", | |||
}; | |||
static char sz_future_error_buffer[256]; | |||
const char * get_error_string(future_error fe, const char * classname) | |||
{ | |||
if (classname) | |||
{ | |||
sprintf_s(sz_future_error_buffer, "%s, code=%s", classname, future_error_string[(size_t)(fe)]); | |||
return sz_future_error_buffer; | |||
} | |||
return future_error_string[(size_t)(fe)]; | |||
} | |||
//获得当前线程下的调度器 | |||
scheduler * this_scheduler() | |||
{ | |||
return &g_scheduler; | |||
} | |||
//获得当前线程下,正在由调度器调度的协程 | |||
/* | |||
namespace detail | |||
{ | |||
state_base * current_coroutine() | |||
{ | |||
scheduler * schdler = this_scheduler(); | |||
if (schdler->current_state) | |||
return schdler->current_state; | |||
return schdler->top_state(); | |||
} | |||
} | |||
*/ | |||
scheduler::scheduler() | |||
: _task() | |||
, _ready_task() | |||
, _timer(std::make_shared<timer_manager>()) | |||
{ | |||
} | |||
scheduler::~scheduler() | |||
{ | |||
cancel_all_task_(); | |||
} | |||
scheduler::scheduler(scheduler && right_) | |||
{ | |||
this->swap(right_); | |||
} | |||
scheduler & scheduler::operator = (scheduler && right_) | |||
{ | |||
this->swap(right_); | |||
return *this; | |||
} | |||
void scheduler::new_task(task_base * task) | |||
{ | |||
if (task) | |||
{ | |||
scoped_lock<std::recursive_mutex> __guard(_mtx_ready); | |||
this->_ready_task.push_back(task); | |||
} | |||
} | |||
void scheduler::cancel_all_task_() | |||
{ | |||
{ | |||
scoped_lock<std::recursive_mutex> __guard(_mtx_ready); | |||
for (auto task : this->_ready_task) | |||
{ | |||
task->cancel(); | |||
delete task; | |||
} | |||
this->_ready_task.clear(); | |||
} | |||
{ | |||
scoped_lock<std::recursive_mutex> __guard(_mtx_task); | |||
for (auto task : this->_task) | |||
{ | |||
task->cancel(); | |||
delete task; | |||
} | |||
this->_task.clear(); | |||
} | |||
} | |||
void scheduler::break_all() | |||
{ | |||
cancel_all_task_(); | |||
scoped_lock<std::recursive_mutex> __guard(_mtx_task); | |||
this->_timer->clear(); | |||
} | |||
void scheduler::run_one_batch() | |||
{ | |||
{ | |||
scoped_lock<std::recursive_mutex> __guard(_mtx_task); | |||
this->_timer->update(); | |||
using namespace std::chrono; | |||
for (auto iter = this->_task.begin(); iter != this->_task.end(); ) | |||
{ | |||
#if _DEBUG | |||
#define MAX_TIME_COST 10000us | |||
#else | |||
#define MAX_TIME_COST 1000us | |||
#endif | |||
// time_cost_evaluation<microseconds> eva(MAX_TIME_COST); | |||
auto task = *iter; | |||
if (task->is_suspend() || task->go_next(this)) | |||
{ | |||
// eva.add("coscheduler"); | |||
++iter; | |||
continue; | |||
} | |||
iter = this->_task.erase(iter); | |||
delete task; | |||
} | |||
} | |||
{ | |||
scoped_lock<std::recursive_mutex> __guard(_mtx_ready); | |||
if (this->_ready_task.size() > 0) | |||
{ | |||
this->_task.insert(this->_task.end(), this->_ready_task.begin(), this->_ready_task.end()); | |||
this->_ready_task.clear(); | |||
} | |||
} | |||
} | |||
void scheduler::run_until_notask() | |||
{ | |||
while (!this->empty()) | |||
this->run_one_batch(); | |||
} | |||
void scheduler::swap(scheduler & right_) | |||
{ | |||
if (this != &right_) | |||
{ | |||
scoped_lock<std::recursive_mutex, std::recursive_mutex, std::recursive_mutex, std::recursive_mutex> | |||
__guard(this->_mtx_ready, this->_mtx_task, right_._mtx_ready, right_._mtx_task); | |||
std::swap(this->_ready_task, right_._ready_task); | |||
std::swap(this->_task, right_._task); | |||
std::swap(this->_timer, right_._timer); | |||
} | |||
} | |||
scheduler g_scheduler; | |||
} |
@@ -0,0 +1,94 @@ | |||
| |||
#pragma once | |||
#include <array> | |||
#include <vector> | |||
#include <yvals.h> | |||
#include "rf_task.h" | |||
#include "utils.h" | |||
#include "timer.h" | |||
namespace resumef | |||
{ | |||
struct scheduler : public std::enable_shared_from_this<scheduler> | |||
{ | |||
private: | |||
mutable std::recursive_mutex _mtx_ready; | |||
std::deque<task_base *> _ready_task; | |||
mutable std::recursive_mutex _mtx_task; | |||
std::list<task_base *> _task; | |||
timer_mgr_ptr _timer; | |||
RF_API void new_task(task_base * task); | |||
void cancel_all_task_(); | |||
public: | |||
RF_API void run_one_batch(); | |||
RF_API void run_until_notask(); | |||
template<class _Ty> | |||
inline void operator + (_Ty && t_) | |||
{ | |||
typedef typename std::conditional< | |||
decltype(std::_IsCallable(t_, 0))::value, | |||
ctx_task_t<_Ty>, | |||
task_t<_Ty> >::type task_type; | |||
return new_task(new task_type(std::forward<_Ty>(t_))); | |||
} | |||
inline void push_task_internal(task_base * t_) | |||
{ | |||
return new_task(t_); | |||
} | |||
inline bool empty() const | |||
{ | |||
scoped_lock<std::recursive_mutex, std::recursive_mutex> __guard(_mtx_ready, _mtx_task); | |||
return | |||
(this->_task.size() + this->_ready_task.size()) == 0 && | |||
this->_timer->empty(); | |||
} | |||
RF_API void break_all(); | |||
RF_API void swap(scheduler & right_); | |||
inline timer_manager * timer() const | |||
{ | |||
return _timer.get(); | |||
} | |||
friend struct task_base; | |||
RF_API scheduler(); | |||
RF_API ~scheduler(); | |||
RF_API scheduler(scheduler && right_); | |||
RF_API scheduler & operator = (scheduler && right_); | |||
scheduler(const scheduler &) = delete; | |||
scheduler & operator = (const scheduler &) = delete; | |||
}; | |||
//-------------------------------------------------------------------------------------------------- | |||
extern scheduler g_scheduler; | |||
#if !defined(_DISABLE_RESUMEF_GO_MACRO) | |||
#define go (*::resumef::this_scheduler()) + | |||
#define GO (*::resumef::this_scheduler()) + [=]()->resumef::future_vt | |||
#endif | |||
//-------------------------------------------------------------------------------------------------- | |||
} | |||
namespace std | |||
{ | |||
inline void swap(resumef::scheduler & _Left, resumef::scheduler & right_) | |||
{ | |||
_Left.swap(right_); | |||
} | |||
} | |||
@@ -0,0 +1,20 @@ | |||
#include "scheduler.h" | |||
#include "sleep.h" | |||
namespace resumef | |||
{ | |||
awaitable_t<bool> sleep_until_(const std::chrono::system_clock::time_point& tp_, scheduler & scheduler_) | |||
{ | |||
resumef::awaitable_t<bool> awaitable; | |||
scheduler_.timer()->add(tp_, | |||
[st = awaitable._state](bool cancellation_requested) | |||
{ | |||
st->set_value(cancellation_requested); | |||
}); | |||
return awaitable; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
#pragma once | |||
namespace resumef | |||
{ | |||
struct scheduler; | |||
RF_API awaitable_t<bool> sleep_until_(const std::chrono::system_clock::time_point& tp_, scheduler & scheduler_); | |||
inline awaitable_t<bool> sleep_for_(const std::chrono::system_clock::duration& dt_, scheduler & scheduler_) | |||
{ | |||
return std::move(sleep_until_(std::chrono::system_clock::now() + dt_, scheduler_)); | |||
} | |||
template<class _Rep, class _Period> | |||
awaitable_t<bool> sleep_for(const std::chrono::duration<_Rep, _Period>& dt_, scheduler & scheduler_) | |||
{ | |||
return std::move(sleep_for_(std::chrono::duration_cast<std::chrono::system_clock::duration>(dt_), scheduler_)); | |||
} | |||
template<class _Rep, class _Period> | |||
awaitable_t<bool> sleep_for(const std::chrono::duration<_Rep, _Period>& dt_) | |||
{ | |||
return std::move(sleep_for_(std::chrono::duration_cast<std::chrono::system_clock::duration>(dt_), g_scheduler)); | |||
} | |||
template<class _Clock, class _Duration = typename _Clock::duration> | |||
awaitable_t<bool> sleep_until(const std::chrono::time_point<_Clock, _Duration>& tp_, scheduler & scheduler_) | |||
{ | |||
return std::move(sleep_until_(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp_), scheduler_)); | |||
} | |||
template<class _Clock, class _Duration> | |||
awaitable_t<bool> sleep_until(const std::chrono::time_point<_Clock, _Duration>& tp_) | |||
{ | |||
return std::move(sleep_until_(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp_), g_scheduler)); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
| |||
//用于内部实现的循环锁 | |||
#pragma once | |||
#include "def.h" | |||
namespace resumef | |||
{ | |||
struct spinlock | |||
{ | |||
volatile std::atomic_flag lck; | |||
spinlock() | |||
{ | |||
lck.clear(); | |||
} | |||
void lock() | |||
{ | |||
if (std::atomic_flag_test_and_set_explicit(&lck, std::memory_order_acquire)) | |||
{ | |||
using namespace std::chrono; | |||
auto dt = 1ms; | |||
while (std::atomic_flag_test_and_set_explicit(&lck, std::memory_order_acquire)) | |||
{ | |||
std::this_thread::sleep_for(dt); | |||
dt *= 2; | |||
} | |||
} | |||
} | |||
bool try_lock() | |||
{ | |||
bool ret = !std::atomic_flag_test_and_set_explicit(&lck, std::memory_order_acquire); | |||
return ret; | |||
} | |||
void unlock() | |||
{ | |||
std::atomic_flag_clear_explicit(&lck, std::memory_order_release); | |||
} | |||
}; | |||
} |
@@ -0,0 +1,284 @@ | |||
| |||
#pragma once | |||
#include "def.h" | |||
#include <iostream> | |||
namespace resumef | |||
{ | |||
struct state_base | |||
{ | |||
protected: | |||
std::mutex _mtx; //for value, _exception | |||
RF_API void set_value_none_lock(); | |||
public: | |||
std::experimental::coroutine_handle<> _coro; | |||
std::atomic<intptr_t> _count = 0; // tracks reference count of state object | |||
std::exception_ptr _exception; | |||
bool _ready = false; | |||
bool _future_acquired = false; | |||
bool _cancellation = false; | |||
bool _done = false; | |||
state_base() | |||
{ | |||
#if RESUMEF_DEBUG_COUNTER | |||
++g_resumef_state_count; | |||
#endif | |||
} | |||
//某个地方的代码,发生过截断,导致了内存泄漏。还是加上虚析构函数吧。 | |||
virtual ~state_base(); | |||
state_base(state_base&&) = delete; | |||
state_base(const state_base&) = delete; | |||
state_base & operator = (state_base&&) = delete; | |||
state_base & operator = (const state_base&) = delete; | |||
RF_API void set_exception(std::exception_ptr && e_); | |||
template<class _Exp> | |||
void throw_exception(const _Exp & e_) | |||
{ | |||
set_exception(std::make_exception_ptr(e_)); | |||
} | |||
void rethrow_if_exception() | |||
{ | |||
if (_exception) | |||
std::rethrow_exception(_exception); | |||
} | |||
bool ready() const | |||
{ | |||
return _ready; | |||
} | |||
void reset_none_lock() | |||
{ | |||
_coro = nullptr; | |||
_ready = false; | |||
_future_acquired = false; | |||
} | |||
void cancel() | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
_cancellation = true; | |||
_coro = nullptr; | |||
} | |||
bool is_suspend() const | |||
{ | |||
return !_coro && !(_done || _ready || _cancellation); | |||
} | |||
void resume() | |||
{ | |||
if (_coro) | |||
{ | |||
auto coro = _coro; | |||
_coro = nullptr; | |||
//std::cout << "resume from " << coro.address() << " on thread " << std::this_thread::get_id() << std::endl; | |||
coro(); | |||
} | |||
} | |||
state_base* lock() | |||
{ | |||
++_count; | |||
return this; | |||
} | |||
void unlock() | |||
{ | |||
if (--_count == 0) | |||
delete this; | |||
} | |||
//------------------------------------------------------------------------------------------ | |||
//以下是通过future_t/promise_t, 与编译器生成的resumable function交互的接口 | |||
bool await_ready() | |||
{ | |||
return _ready; | |||
} | |||
void await_suspend(std::experimental::coroutine_handle<> resume_cb) | |||
{ | |||
_coro = resume_cb; | |||
} | |||
void await_resume() | |||
{ | |||
} | |||
void final_suspend() | |||
{ | |||
_done = true; | |||
} | |||
bool cancellation_requested() const | |||
{ | |||
return _cancellation; | |||
} | |||
//以上是通过future_t/promise_t, 与编译器生成的resumable function交互的接口 | |||
//------------------------------------------------------------------------------------------ | |||
}; | |||
template <typename _Ty> | |||
struct state_t : public state_base | |||
{ | |||
public: | |||
typedef _Ty value_type; | |||
value_type _value; | |||
state_t<_Ty>* lock() | |||
{ | |||
++_count; | |||
return this; | |||
} | |||
void set_value(const value_type& t) | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
_value = t; | |||
state_base::set_value_none_lock(); | |||
} | |||
void set_value(value_type&& t) | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
_value = std::forward<value_type>(t); | |||
state_base::set_value_none_lock(); | |||
} | |||
_Ty & get_value() | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
if (!_ready) | |||
throw future_exception{ future_error::not_ready }; | |||
return _value; | |||
} | |||
void reset() | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
state_base::reset_none_lock(); | |||
_value = value_type{}; | |||
} | |||
}; | |||
template<> | |||
struct state_t<void> : public state_base | |||
{ | |||
state_t<void>* lock() | |||
{ | |||
++_count; | |||
return this; | |||
} | |||
void set_value() | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
set_value_none_lock(); | |||
} | |||
void get_value() | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
if (!_ready) | |||
throw future_exception{ future_error::not_ready }; | |||
} | |||
void reset() | |||
{ | |||
scoped_lock<std::mutex> __guard(_mtx); | |||
reset_none_lock(); | |||
} | |||
}; | |||
// counted_ptr is similar to shared_ptr but allows explicit control | |||
// | |||
template <typename T> | |||
struct counted_ptr | |||
{ | |||
counted_ptr() = default; | |||
counted_ptr(const counted_ptr& cp) : _p(cp._p) | |||
{ | |||
_lock(); | |||
} | |||
counted_ptr(T* p) : _p(p) | |||
{ | |||
_lock(); | |||
} | |||
counted_ptr(counted_ptr&& cp) | |||
{ | |||
std::swap(_p, cp._p); | |||
} | |||
counted_ptr& operator=(const counted_ptr& cp) | |||
{ | |||
if (&cp != this) | |||
{ | |||
_unlock(); | |||
_lock(cp._p); | |||
} | |||
return *this; | |||
} | |||
counted_ptr& operator=(counted_ptr&& cp) | |||
{ | |||
if (&cp != this) | |||
std::swap(_p, cp._p); | |||
return *this; | |||
} | |||
~counted_ptr() | |||
{ | |||
_unlock(); | |||
} | |||
T* operator->() const | |||
{ | |||
return _p; | |||
} | |||
T* get() const | |||
{ | |||
return _p; | |||
} | |||
void reset() | |||
{ | |||
_unlock(); | |||
} | |||
protected: | |||
void _unlock() | |||
{ | |||
if (_p != nullptr) | |||
{ | |||
auto t = _p; | |||
_p = nullptr; | |||
t->unlock(); | |||
} | |||
} | |||
void _lock(T* p) | |||
{ | |||
if (p != nullptr) | |||
p->lock(); | |||
_p = p; | |||
} | |||
void _lock() | |||
{ | |||
if (_p != nullptr) | |||
_p->lock(); | |||
} | |||
T* _p = nullptr; | |||
}; | |||
template <typename T> | |||
counted_ptr<T> make_counted() | |||
{ | |||
return new T{}; | |||
} | |||
} | |||
@@ -0,0 +1,106 @@ | |||
#include "timer.h" | |||
namespace resumef | |||
{ | |||
timer_manager::timer_manager() | |||
{ | |||
_added_timers.reserve(128); | |||
} | |||
timer_manager::~timer_manager() | |||
{ | |||
clear(); | |||
} | |||
void timer_manager::call_target_(const timer_target_ptr & sptr, bool canceld) | |||
{ | |||
auto cb = std::move(sptr->cb); | |||
sptr->st = timer_target::State::Invalid; | |||
#if _DEBUG | |||
sptr->_manager = nullptr; | |||
#endif | |||
if(cb) cb(canceld); | |||
} | |||
void timer_manager::clear() | |||
{ | |||
auto _atimer = std::move(_added_timers); | |||
for (auto & sptr : _atimer) | |||
call_target_(sptr, true); | |||
auto _rtimer = std::move(_runing_timers); | |||
for (auto & kv : _rtimer) | |||
call_target_(kv.second, true); | |||
} | |||
detail::timer_target_ptr timer_manager::add_(const timer_target_ptr & sptr) | |||
{ | |||
assert(sptr); | |||
assert(sptr->st == timer_target::State::Invalid); | |||
#if _DEBUG | |||
assert(sptr->_manager == nullptr); | |||
sptr->_manager = this; | |||
#endif | |||
sptr->st = timer_target::State::Added; | |||
_added_timers.push_back(sptr); | |||
return sptr; | |||
} | |||
bool timer_manager::stop(const timer_target_ptr & sptr) | |||
{ | |||
if (!sptr || sptr->st == timer_target::State::Invalid) | |||
return false; | |||
#if _DEBUG | |||
assert(sptr->_manager == this); | |||
#endif | |||
sptr->st = timer_target::State::Invalid; | |||
return true; | |||
} | |||
void timer_manager::update() | |||
{ | |||
if (_added_timers.size() > 0) | |||
{ | |||
for (auto & sptr : _added_timers) | |||
{ | |||
if (sptr->st == timer_target::State::Added) | |||
{ | |||
sptr->st = timer_target::State::Runing; | |||
_runing_timers.insert({ sptr->tp, sptr }); | |||
} | |||
else | |||
{ | |||
assert(sptr->st == timer_target::State::Invalid); | |||
call_target_(sptr, true); | |||
} | |||
} | |||
_added_timers.clear(); | |||
} | |||
if (_runing_timers.size() > 0) | |||
{ | |||
auto now_ = clock_type::now(); | |||
timer_map_type _timers = std::move(_runing_timers); | |||
auto iter = _timers.begin(); | |||
for (; iter != _timers.end(); ++iter) | |||
{ | |||
auto & kv = *iter; | |||
if (kv.first > now_) | |||
break; | |||
call_target_(kv.second, kv.second->st == timer_target::State::Invalid); | |||
} | |||
_timers.erase(_timers.begin(), iter); | |||
_runing_timers = std::move(_timers); | |||
} | |||
} | |||
} |
@@ -0,0 +1,186 @@ | |||
#pragma once | |||
#include "def.h" | |||
namespace resumef | |||
{ | |||
struct timer_manager; | |||
typedef std::shared_ptr<timer_manager> timer_mgr_ptr; | |||
typedef std::weak_ptr<timer_manager> timer_mgr_wptr; | |||
namespace detail | |||
{ | |||
typedef std::chrono::system_clock timer_clock_type; | |||
typedef std::function<void(bool)> timer_callback_type; | |||
struct timer_target : public std::enable_shared_from_this<timer_target> | |||
{ | |||
friend timer_manager; | |||
private: | |||
enum struct State : uint32_t | |||
{ | |||
Invalid, | |||
Added, | |||
Runing, | |||
}; | |||
timer_clock_type::time_point tp; | |||
timer_callback_type cb; | |||
State st = State::Invalid; | |||
#if _DEBUG | |||
private: | |||
timer_manager * _manager = nullptr; | |||
#endif | |||
public: | |||
timer_target(const timer_clock_type::time_point & tp_, const timer_callback_type & cb_) | |||
: tp(tp_) | |||
, cb(cb_) | |||
{ | |||
} | |||
timer_target(const timer_clock_type::time_point & tp_, timer_callback_type && cb_) | |||
: tp(tp_) | |||
, cb(std::forward<timer_callback_type>(cb_)) | |||
{ | |||
} | |||
private: | |||
timer_target() = delete; | |||
timer_target(const timer_target &) = delete; | |||
timer_target(timer_target && right_) = delete; | |||
timer_target & operator = (const timer_target &) = delete; | |||
timer_target & operator = (timer_target && right_) = delete; | |||
}; | |||
typedef std::shared_ptr<timer_target> timer_target_ptr; | |||
typedef std::weak_ptr<timer_target> timer_target_wptr; | |||
} | |||
struct timer_handler | |||
{ | |||
private: | |||
timer_mgr_wptr _manager; | |||
detail::timer_target_wptr _target; | |||
public: | |||
timer_handler() = default; | |||
timer_handler(const timer_handler &) = default; | |||
timer_handler(timer_handler && right_); | |||
timer_handler & operator = (const timer_handler &) = default; | |||
timer_handler & operator = (timer_handler && right_); | |||
timer_handler(timer_manager * manager_, const detail::timer_target_ptr & target_); | |||
void reset(); | |||
bool stop(); | |||
bool expired() const; | |||
}; | |||
struct timer_manager : public std::enable_shared_from_this<timer_manager> | |||
{ | |||
typedef detail::timer_target timer_target; | |||
typedef detail::timer_target_ptr timer_target_ptr; | |||
typedef detail::timer_clock_type clock_type; | |||
typedef clock_type::duration duration_type; | |||
typedef clock_type::time_point time_point_type; | |||
typedef std::vector<timer_target_ptr> timer_vector_type; | |||
typedef std::multimap<clock_type::time_point, timer_target_ptr> timer_map_type; | |||
protected: | |||
timer_vector_type _added_timers; | |||
public: | |||
timer_map_type _runing_timers; | |||
RF_API timer_manager(); | |||
RF_API ~timer_manager(); | |||
template<class _Rep, class _Period, class _Cb> | |||
timer_target_ptr add(const std::chrono::duration<_Rep, _Period> & dt_, _Cb && cb_) | |||
{ | |||
return add_(std::chrono::duration_cast<duration_type>(dt_), std::forward<_Cb>(cb_)); | |||
} | |||
template<class _Clock, class _Duration = typename _Clock::duration, class _Cb> | |||
timer_target_ptr add(const std::chrono::time_point<_Clock, _Duration> & tp_, _Cb && cb_) | |||
{ | |||
return add_(std::chrono::time_point_cast<duration_type>(tp_), std::forward<_Cb>(cb_)); | |||
} | |||
template<class _Rep, class _Period, class _Cb> | |||
timer_handler add_handler(const std::chrono::duration<_Rep, _Period> & dt_, _Cb && cb_) | |||
{ | |||
return{ this, add(dt_, std::forward<_Cb>(cb_)) }; | |||
} | |||
template<class _Clock, class _Duration = typename _Clock::duration, class _Cb> | |||
timer_handler add_handler(const std::chrono::time_point<_Clock, _Duration> & tp_, _Cb && cb_) | |||
{ | |||
return{ this, add(tp_, std::forward<_Cb>(cb_)) }; | |||
} | |||
RF_API bool stop(const timer_target_ptr & sptr); | |||
inline bool empty() const | |||
{ | |||
return _added_timers.empty() && _runing_timers.empty(); | |||
} | |||
RF_API void clear(); | |||
RF_API void update(); | |||
template<class _Cb> | |||
timer_target_ptr add_(const duration_type & dt_, _Cb && cb_) | |||
{ | |||
return add_(std::make_shared<timer_target>(clock_type::now() + dt_, std::forward<_Cb>(cb_))); | |||
} | |||
template<class _Cb> | |||
timer_target_ptr add_(const time_point_type & tp_, _Cb && cb_) | |||
{ | |||
return add_(std::make_shared<timer_target>(tp_, std::forward<_Cb>(cb_))); | |||
} | |||
private: | |||
RF_API timer_target_ptr add_(const timer_target_ptr & sptr); | |||
static void call_target_(const timer_target_ptr & sptr, bool canceld); | |||
}; | |||
inline timer_handler::timer_handler(timer_manager * manager_, const detail::timer_target_ptr & target_) | |||
: _manager(manager_->shared_from_this()) | |||
, _target(target_) | |||
{ | |||
} | |||
inline timer_handler::timer_handler(timer_handler && right_) | |||
: _manager(std::move(right_._manager)) | |||
, _target(std::move(right_._target)) | |||
{ | |||
} | |||
inline timer_handler & timer_handler::operator = (timer_handler && right_) | |||
{ | |||
if (this != &right_) | |||
{ | |||
_manager = std::move(right_._manager); | |||
_target = std::move(right_._target); | |||
} | |||
return *this; | |||
} | |||
inline void timer_handler::reset() | |||
{ | |||
_manager.reset(); | |||
_target.reset(); | |||
} | |||
inline bool timer_handler::stop() | |||
{ | |||
bool result = false; | |||
if (!_target.expired()) | |||
{ | |||
auto sptr = _manager.lock(); | |||
if (sptr) | |||
result = sptr->stop(_target.lock()); | |||
_target.reset(); | |||
} | |||
_manager.reset(); | |||
return result; | |||
} | |||
inline bool timer_handler::expired() const | |||
{ | |||
return _target.expired(); | |||
} | |||
} |
@@ -0,0 +1,91 @@ | |||
//#pragma once | |||
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(); | |||
} | |||
template <typename _PromiseT> | |||
bool operator!=(coroutine_handle<_PromiseT> const &_Left, | |||
coroutine_handle<_PromiseT> const &_Right) noexcept { | |||
return !(_Left == _Right); | |||
} | |||
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() {} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
#pragma once | |||
namespace std | |||
{ | |||
template<typename _Function> | |||
inline auto _IsCallable(const _Function & _Func, int) -> decltype(_Func(), true_type()) | |||
{ | |||
(_Func); | |||
return true_type(); | |||
} | |||
template<typename _Function> | |||
inline false_type _IsCallable(const _Function &, ...) | |||
{ | |||
return false_type(); | |||
} | |||
} | |||
@@ -0,0 +1,62 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
auto async_get_long(int64_t val) | |||
{ | |||
using namespace std::chrono; | |||
resumef::promise_t<int64_t> awaitable; | |||
std::thread([val, st = awaitable._state->lock()] | |||
{ | |||
std::this_thread::sleep_for(500ms); | |||
st->set_value(val * val); | |||
st->unlock(); | |||
}).detach(); | |||
return awaitable.get_future(); | |||
} | |||
resumef::future_vt resumable_get_long(int64_t val) | |||
{ | |||
std::cout << val << std::endl; | |||
val = co_await async_get_long(val); | |||
std::cout << val << std::endl; | |||
val = co_await async_get_long(val); | |||
std::cout << val << std::endl; | |||
val = co_await async_get_long(val); | |||
std::cout << val << std::endl; | |||
} | |||
resumef::future_t<int64_t> loop_get_long(int64_t val) | |||
{ | |||
std::cout << val << std::endl; | |||
for (int i = 0; i < 5; ++i) | |||
{ | |||
val = co_await async_get_long(val); | |||
std::cout << val << std::endl; | |||
} | |||
return val; | |||
} | |||
void resumable_main_cb() | |||
{ | |||
std::cout << std::this_thread::get_id() << std::endl; | |||
go []()->resumef::future_vt | |||
{ | |||
auto val = co_await loop_get_long(2); | |||
std::cout << val << std::endl; | |||
}; | |||
//resumef::g_scheduler.run_until_notask(); | |||
go resumable_get_long(3); | |||
resumef::g_scheduler.run_until_notask(); | |||
} |
@@ -0,0 +1,89 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include <deque> | |||
#include <mutex> | |||
#include "librf.h" | |||
using namespace resumef; | |||
const size_t MAX_CHANNEL_QUEUE = 0; //0, 1, 5, 10, -1 | |||
future_vt test_channel_read(const channel_t<size_t> & c) | |||
{ | |||
using namespace std::chrono; | |||
for (size_t i = 0; i < 10; ++i) | |||
{ | |||
try | |||
{ | |||
//auto val = co_await c.read(); | |||
auto val = co_await c; //第二种从channel读出数据的方法。利用重载operator co_await(),而不是c是一个awaitable_t。 | |||
std::cout << val << ":"; | |||
#if _DEBUG | |||
for (auto val : c.debug_queue()) | |||
std::cout << val << ","; | |||
#endif | |||
std::cout << std::endl; | |||
} | |||
catch (channel_exception e) | |||
{ | |||
//MAX_CHANNEL_QUEUE=0,并且先读后写,会触发read_before_write异常 | |||
std::cout << e.what() << std::endl; | |||
} | |||
co_await sleep_for(50ms); | |||
} | |||
} | |||
future_vt test_channel_write(const channel_t<size_t> & c) | |||
{ | |||
using namespace std::chrono; | |||
for (size_t i = 0; i < 10; ++i) | |||
{ | |||
//co_await c.write(i); | |||
co_await (c << i); //第二种写入数据到channel的方法。因为优先级关系,需要将'c << i'括起来 | |||
std::cout << "<" << i << ">:"; | |||
#if _DEBUG | |||
for (auto val : c.debug_queue()) | |||
std::cout << val << ","; | |||
#endif | |||
std::cout << std::endl; | |||
} | |||
} | |||
void test_channel_read_first() | |||
{ | |||
channel_t<size_t> c(MAX_CHANNEL_QUEUE); | |||
go test_channel_read(c); | |||
go test_channel_write(c); | |||
g_scheduler.run_until_notask(); | |||
} | |||
void test_channel_write_first() | |||
{ | |||
channel_t<size_t> c(MAX_CHANNEL_QUEUE); | |||
go test_channel_write(c); | |||
go test_channel_read(c); | |||
g_scheduler.run_until_notask(); | |||
} | |||
void resumable_main_channel() | |||
{ | |||
test_channel_read_first(); | |||
std::cout << std::endl; | |||
test_channel_write_first(); | |||
} |
@@ -0,0 +1,62 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include "librf.h" | |||
static const int M = 10; | |||
size_t dynamic_go_count = 0; | |||
std::array<std::array<std::array<int32_t, M>, M>, 3> dynamic_cells; | |||
void test_dynamic_go() | |||
{ | |||
auto co_star = [](int j) | |||
{ | |||
for (int i = 0; i < M; ++i) | |||
{ | |||
go[=]() | |||
{ | |||
for (int k = 0; k < M; ++k) | |||
{ | |||
++dynamic_cells[j][i][k]; | |||
++dynamic_go_count; | |||
std::cout << j << " " << i << " " << k << std::endl; | |||
co_yield k; | |||
} | |||
co_return M; | |||
}; | |||
co_yield i; | |||
} | |||
co_return M; | |||
}; | |||
go co_star(0); | |||
go co_star(1); | |||
go co_star(2); | |||
resumef::g_scheduler.run_until_notask(); | |||
std::cout << "dynamic_go_count = " << dynamic_go_count << std::endl; | |||
for (auto & j : dynamic_cells) | |||
{ | |||
for (auto & i : j) | |||
{ | |||
for (auto k : i) | |||
std::cout << k; | |||
std::cout << std::endl; | |||
} | |||
std::cout << std::endl; | |||
} | |||
} | |||
void resumable_main_dynamic_go() | |||
{ | |||
test_dynamic_go(); | |||
} |
@@ -0,0 +1,163 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
std::thread async_set_event(const event_t & e, std::chrono::milliseconds dt) | |||
{ | |||
return std::thread([=] | |||
{ | |||
std::this_thread::sleep_for(dt); | |||
e.signal(); | |||
}); | |||
} | |||
future_vt resumable_wait_event(const event_t & e) | |||
{ | |||
using namespace std::chrono; | |||
if (co_await e.wait() == false) | |||
std::cout << "time out!" << std::endl; | |||
else | |||
std::cout << "event signal!" << std::endl; | |||
} | |||
void test_wait_one() | |||
{ | |||
using namespace std::chrono; | |||
{ | |||
event_t evt; | |||
go resumable_wait_event(evt); | |||
auto tt = async_set_event(evt, 1000ms); | |||
g_scheduler.run_until_notask(); | |||
tt.join(); | |||
} | |||
{ | |||
event_t evt2(1); | |||
go[&]() -> future_vt | |||
{ | |||
(void)co_await evt2.wait(); | |||
std::cout << "event signal on 1!" << std::endl; | |||
}; | |||
go[&]() -> future_vt | |||
{ | |||
(void)co_await evt2.wait(); | |||
std::cout << "event signal on 2!" << std::endl; | |||
}; | |||
std::cout << std::this_thread::get_id() << std::endl; | |||
auto tt = async_set_event(evt2, 1000ms); | |||
g_scheduler.run_until_notask(); | |||
tt.join(); | |||
} | |||
} | |||
void test_wait_any() | |||
{ | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
go[&]() -> future_vt | |||
{ | |||
for (int i = 0; i < _countof(evts); ++i) | |||
{ | |||
intptr_t idx = co_await event_t::wait_any(evts); | |||
std::cout << "event " << idx << " signal!" << std::endl; | |||
} | |||
}; | |||
std::vector<std::thread> vtt; | |||
srand((int)time(nullptr)); | |||
for (auto & e : evts) | |||
{ | |||
vtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000))); | |||
} | |||
g_scheduler.run_until_notask(); | |||
for (auto & tt : vtt) | |||
tt.join(); | |||
} | |||
void test_wait_all() | |||
{ | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
go[&]() -> future_vt | |||
{ | |||
if (co_await event_t::wait_all(evts)) | |||
std::cout << "all event signal!" << std::endl; | |||
else | |||
std::cout << "time out!" << std::endl; | |||
}; | |||
std::vector<std::thread> vtt; | |||
srand((int)time(nullptr)); | |||
for (auto & e : evts) | |||
{ | |||
vtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000))); | |||
} | |||
g_scheduler.run_until_notask(); | |||
for (auto & tt : vtt) | |||
tt.join(); | |||
} | |||
void test_wait_all_timeout() | |||
{ | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
go[&]() -> future_vt | |||
{ | |||
if (co_await event_t::wait_all_for(1000ms, evts)) | |||
std::cout << "all event signal!" << std::endl; | |||
else | |||
std::cout << "time out!" << std::endl; | |||
}; | |||
std::vector<std::thread> vtt; | |||
srand((int)time(nullptr)); | |||
for (auto & e : evts) | |||
{ | |||
vtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000))); | |||
} | |||
g_scheduler.run_until_notask(); | |||
for (auto & tt : vtt) | |||
tt.join(); | |||
} | |||
void resumable_main_event() | |||
{ | |||
test_wait_one(); | |||
std::cout << std::endl; | |||
test_wait_any(); | |||
std::cout << std::endl; | |||
test_wait_all(); | |||
std::cout << std::endl; | |||
test_wait_all_timeout(); | |||
std::cout << std::endl; | |||
} |
@@ -0,0 +1,178 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
future_vt resumalbe_set_event(const event_t & e, std::chrono::milliseconds dt) | |||
{ | |||
co_await resumef::sleep_for(dt); | |||
e.signal(); | |||
std::cout << "+"; | |||
} | |||
void async_set_event(const event_t & e, std::chrono::milliseconds dt) | |||
{ | |||
std::thread([=] | |||
{ | |||
std::this_thread::sleep_for(dt); | |||
e.signal(); | |||
}).detach(); | |||
} | |||
void test_wait_timeout_one() | |||
{ | |||
std::cout << __FUNCTION__ << std::endl; | |||
using namespace std::chrono; | |||
event_t evt; | |||
go [&evt]() -> future_vt | |||
{ | |||
intptr_t counter = 0; | |||
for (;;) | |||
{ | |||
if (co_await evt.wait_for(500ms)) | |||
break; | |||
++counter; | |||
std::cout << "."; | |||
} | |||
std::cout << counter << std::endl; | |||
}; | |||
async_set_event(evt, 10s + 50ms); | |||
//go resumalbe_set_event(evt, 10s + 50ms); | |||
g_scheduler.run_until_notask(); | |||
} | |||
void test_wait_timeout_any_invalid() | |||
{ | |||
std::cout << __FUNCTION__ << std::endl; | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
//无效的等待 | |||
go[&]()-> future_vt | |||
{ | |||
intptr_t idx = co_await event_t::wait_any_for(500ms, std::begin(evts), std::end(evts)); | |||
assert(idx < 0); | |||
std::cout << "invalid wait!" << std::endl; | |||
}; | |||
g_scheduler.run_until_notask(); | |||
} | |||
void test_wait_timeout_any() | |||
{ | |||
std::cout << __FUNCTION__ << std::endl; | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
go[&]() -> future_vt | |||
{ | |||
intptr_t counter = 0; | |||
for (;;) | |||
{ | |||
intptr_t idx = co_await event_t::wait_any_for(500ms, evts); | |||
if (idx >= 0) | |||
{ | |||
std::cout << counter << std::endl; | |||
std::cout << "event " << idx << " signal!" << std::endl; | |||
break; | |||
} | |||
++counter; | |||
std::cout << "."; | |||
} | |||
//取消剩下的定时器,以便于协程调度器退出来 | |||
g_scheduler.timer()->clear(); | |||
}; | |||
srand((int)time(nullptr)); | |||
for (auto & e : evts) | |||
{ | |||
//go resumalbe_set_event(e, 1ms * (1000 + rand() % 5000)); | |||
async_set_event(e, 1ms * (1000 + rand() % 5000)); | |||
} | |||
g_scheduler.run_until_notask(); | |||
} | |||
void test_wait_timeout_all_invalid() | |||
{ | |||
std::cout << __FUNCTION__ << std::endl; | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
//无效的等待 | |||
go[&]()-> future_vt | |||
{ | |||
bool result = co_await event_t::wait_all_for(500ms, std::begin(evts), std::end(evts)); | |||
assert(!result); | |||
std::cout << "invalid wait!" << std::endl; | |||
}; | |||
g_scheduler.run_until_notask(); | |||
} | |||
void test_wait_timeout_all() | |||
{ | |||
std::cout << __FUNCTION__ << std::endl; | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
go[&]() -> future_vt | |||
{ | |||
intptr_t counter = 0; | |||
for (;;) | |||
{ | |||
if (co_await event_t::wait_all_until(system_clock::now() + 500ms, evts)) | |||
{ | |||
std::cout << counter << std::endl; | |||
std::cout << "all event signal!" << std::endl; | |||
break; | |||
} | |||
++counter; | |||
std::cout << "."; | |||
} | |||
}; | |||
srand((int)time(nullptr)); | |||
for (auto & e : evts) | |||
{ | |||
go resumalbe_set_event(e, 1ms * (1000 + rand() % 5000)); | |||
//async_set_event(e, 1ms * (1000 + rand() % 5000)); | |||
} | |||
g_scheduler.run_until_notask(); | |||
} | |||
void resumable_main_event_timeout() | |||
{ | |||
using namespace std::chrono; | |||
test_wait_timeout_one(); | |||
std::cout << std::endl; | |||
test_wait_timeout_any_invalid(); | |||
std::cout << std::endl << std::endl; | |||
test_wait_timeout_any(); | |||
std::cout << std::endl << std::endl; | |||
test_wait_timeout_all_invalid(); | |||
std::cout << std::endl; | |||
test_wait_timeout_all(); | |||
std::cout << std::endl; | |||
} |
@@ -0,0 +1,75 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
//请打开结构化异常(/EHa) | |||
auto async_signal_exception(const intptr_t dividend) | |||
{ | |||
using namespace std::chrono; | |||
resumef::awaitable_t<int64_t> awaitable; | |||
std::thread([dividend, st = awaitable._state] | |||
{ | |||
std::this_thread::sleep_for(50ms); | |||
try | |||
{ | |||
if (dividend == 0) | |||
throw std::logic_error("divided by zero"); | |||
st->set_value(10000 / dividend); | |||
} | |||
catch (...) | |||
{ | |||
st->set_exception(std::current_exception()); | |||
} | |||
}).detach(); | |||
return awaitable; | |||
} | |||
future_vt test_signal_exception() | |||
{ | |||
for (intptr_t i = 10; i >= 0; --i) | |||
{ | |||
try | |||
{ | |||
auto r = co_await async_signal_exception(i); | |||
std::cout << "result is " << r << std::endl; | |||
} | |||
catch (const std::exception& e) | |||
{ | |||
std::cout << "exception signal : " << e.what() << std::endl; | |||
} | |||
catch (...) | |||
{ | |||
std::cout << "exception signal : who knows?" << std::endl; | |||
} | |||
} | |||
} | |||
future_vt test_bomb_exception() | |||
{ | |||
for (intptr_t i = 10; i >= 0; --i) | |||
{ | |||
auto r = co_await async_signal_exception(i); | |||
std::cout << "result is " << r << std::endl; | |||
} | |||
} | |||
void resumable_main_exception() | |||
{ | |||
go test_signal_exception(); | |||
g_scheduler.run_until_notask(); | |||
std::cout << std::endl; | |||
go test_bomb_exception(); | |||
g_scheduler.run_until_notask(); | |||
} |
@@ -0,0 +1,55 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include <deque> | |||
#include <mutex> | |||
#include "librf.h" | |||
using namespace resumef; | |||
mutex_t g_lock; | |||
std::deque<size_t> g_queue; | |||
future_vt test_mutex_pop(size_t idx) | |||
{ | |||
using namespace std::chrono; | |||
for (size_t i = 0; i < 10; ++i) | |||
{ | |||
co_await resumf_guard_lock(g_lock); | |||
if (g_queue.size() > 0) | |||
{ | |||
size_t val = g_queue.front(); | |||
g_queue.pop_front(); | |||
std::cout << val << " on " << idx << std::endl; | |||
} | |||
co_await sleep_for(500ms); | |||
} | |||
} | |||
future_vt test_mutex_push() | |||
{ | |||
using namespace std::chrono; | |||
for (size_t i = 0; i < 10; ++i) | |||
{ | |||
co_await resumf_guard_lock(g_lock); | |||
g_queue.push_back(i); | |||
co_await sleep_for(500ms); | |||
} | |||
} | |||
void resumable_main_mutex() | |||
{ | |||
go test_mutex_push(); | |||
go test_mutex_pop(1); | |||
g_scheduler.run_until_notask(); | |||
} |
@@ -0,0 +1,52 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include "librf.h" | |||
//static const int N = 100000000; | |||
static const int N = 10; | |||
template <typename T> | |||
void dump(std::string name, int n, T start, T end) | |||
{ | |||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count(); | |||
std::cout << name << " " << n << " " << ns << " ns " << ns / n << " ns/op" << std::endl; | |||
} | |||
auto yield_switch(int coro) | |||
{ | |||
for (int i = 0; i < N / coro; ++i) | |||
co_yield i; | |||
return N / coro; | |||
} | |||
void resumable_switch(int coro) | |||
{ | |||
auto start = std::chrono::steady_clock::now(); | |||
for (int i = 0; i < coro; ++i) | |||
{ | |||
//go yield_switch(coro); | |||
go [=] | |||
{ | |||
for (int i = 0; i < N / coro; ++i) | |||
co_yield i; | |||
return N / coro; | |||
}; | |||
} | |||
resumef::g_scheduler.run_until_notask(); | |||
auto end = std::chrono::steady_clock::now(); | |||
dump("BenchmarkSwitch_" + std::to_string(coro), N, start, end); | |||
} | |||
void resumable_main_resumable() | |||
{ | |||
resumable_switch(1); | |||
resumable_switch(1000); | |||
resumable_switch(1000000); | |||
} |
@@ -0,0 +1,35 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
future_vt test_routine_use_timer() | |||
{ | |||
using namespace std::chrono; | |||
for (size_t i = 0; i < 10; ++i) | |||
{ | |||
co_await resumef::sleep_for(100ms); | |||
std::cout << "timer after 100ms." << std::endl; | |||
} | |||
} | |||
future_vt test_routine_use_timer_2() | |||
{ | |||
co_await test_routine_use_timer(); | |||
co_await test_routine_use_timer(); | |||
co_await test_routine_use_timer(); | |||
} | |||
void resumable_main_routine() | |||
{ | |||
go test_routine_use_timer_2(); | |||
//test_routine_use_timer(); | |||
g_scheduler.run_until_notask(); | |||
} |
@@ -0,0 +1,66 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
future_vt test_sleep_use_timer() | |||
{ | |||
using namespace std::chrono; | |||
co_await resumef::sleep_for(100ms); | |||
std::cout << "timer after 100ms." << std::endl; | |||
if (co_await resumef::sleep_until(system_clock::now() + 200ms)) | |||
std::cout << "timer canceled." << std::endl; | |||
else | |||
std::cout << "timer after 200ms." << std::endl; | |||
} | |||
void test_wait_all_events_with_signal_by_sleep() | |||
{ | |||
using namespace std::chrono; | |||
event_t evts[8]; | |||
go[&]() -> future_vt | |||
{ | |||
if (co_await event_t::wait_all(evts)) | |||
std::cout << "all event signal!" << std::endl; | |||
else | |||
std::cout << "time out!" << std::endl; | |||
}; | |||
srand((int)time(nullptr)); | |||
for(size_t i=0; i<_countof(evts); ++i) | |||
{ | |||
go[&, i]() -> future_vt | |||
{ | |||
co_await resumef::sleep_for(1ms * (500 + rand() % 1000)); | |||
evts[i].signal(); | |||
std::cout << "event[ " << i << " ] signal!" << std::endl; | |||
}; | |||
} | |||
while (!g_scheduler.empty()) | |||
{ | |||
g_scheduler.run_one_batch(); | |||
//std::cout << "press any key to continue." << std::endl; | |||
//_getch(); | |||
} | |||
} | |||
void resumable_main_sleep() | |||
{ | |||
go test_sleep_use_timer(); | |||
g_scheduler.run_until_notask(); | |||
std::cout << std::endl; | |||
test_wait_all_events_with_signal_by_sleep(); | |||
std::cout << std::endl; | |||
} |
@@ -0,0 +1,95 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
template<size_t _N> | |||
future_vt test_loop_sleep() | |||
{ | |||
using namespace std::chrono; | |||
for (size_t i = 0; i < _N; ++i) | |||
{ | |||
co_await resumef::sleep_for(100ms); | |||
std::cout << "."; | |||
} | |||
std::cout << std::endl; | |||
} | |||
future_vt test_recursive_await() | |||
{ | |||
std::cout << "---1" << std::endl; | |||
co_await test_loop_sleep<5>(); | |||
std::cout << "---2" << std::endl; | |||
co_await test_loop_sleep<6>(); | |||
std::cout << "---3" << std::endl; | |||
co_await test_loop_sleep<7>(); | |||
std::cout << "---4" << std::endl; | |||
} | |||
future_vt test_recursive_go() | |||
{ | |||
std::cout << "---1" << std::endl; | |||
co_await test_loop_sleep<3>(); | |||
std::cout << "---2" << std::endl; | |||
go test_loop_sleep<5>(); | |||
std::cout << "---3" << std::endl; | |||
co_await test_loop_sleep<4>(); | |||
std::cout << "---4" << std::endl; | |||
} | |||
void resumable_main_suspend_always() | |||
{ | |||
go test_recursive_await(); | |||
go test_recursive_go(); | |||
g_scheduler.run_until_notask(); | |||
} | |||
/* | |||
resume from 0000016B8477CE00 on thread 7752 | |||
resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
. | |||
resume from 0000016B8477CE00 on thread 7752 | |||
resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
. | |||
resume from 0000016B8477CE00 on thread 7752 | |||
resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
.resume from 0000016B847726C0 on thread 7752 | |||
. | |||
resume from 0000016B8477CE00 on thread 7752 | |||
说明有四个协程对象(其中三个对象的内存被复用,表现为地址一样) | |||
test_recursive_await -> 0000016B8477CE00 | |||
test_loop_sleep<5> -> 0000016B847726C0 | |||
test_loop_sleep<6> -> 0000016B847726C0 | |||
test_loop_sleep<7> -> 0000016B847726C0 | |||
*/ |
@@ -0,0 +1,36 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
void resumable_main_timer() | |||
{ | |||
using namespace std::chrono; | |||
auto th = g_scheduler.timer()->add_handler(system_clock::now() + 5s, | |||
[](bool bValue) | |||
{ | |||
if (bValue) | |||
std::cout << "timer canceled." << std::endl; | |||
else | |||
std::cout << "timer after 5s." << std::endl; | |||
}); | |||
auto th2 = g_scheduler.timer()->add_handler(1s, | |||
[&th](bool) | |||
{ | |||
std::cout << "timer after 1s." << std::endl; | |||
th.stop(); | |||
}); | |||
g_scheduler.run_until_notask(); | |||
th2.stop(); //but th2 is invalid | |||
} |
@@ -0,0 +1,71 @@ | |||
| |||
#include <chrono> | |||
#include <iostream> | |||
#include <string> | |||
#include <conio.h> | |||
#include <thread> | |||
#include "librf.h" | |||
using namespace resumef; | |||
//std::experimental::generator<int> | |||
auto test_yield_int() | |||
{ | |||
std::cout << "1 will yield return" << std::endl; | |||
co_yield 1; | |||
std::cout << "2 will yield return" << std::endl; | |||
co_yield 2; | |||
std::cout << "3 will yield return" << std::endl; | |||
co_yield 3; | |||
std::cout << "4 will return" << std::endl; | |||
return 4; | |||
std::cout << "5 will never yield return" << std::endl; | |||
co_yield 5; | |||
} | |||
/*不能编译*/ | |||
/* | |||
auto test_yield_void() | |||
{ | |||
std::cout << "1 will yield return" << std::endl; | |||
co_yield ; | |||
std::cout << "2 will yield return" << std::endl; | |||
co_yield ; | |||
std::cout << "3 will yield return" << std::endl; | |||
co_yield ; | |||
std::cout << "4 will return" << std::endl; | |||
co_return ; | |||
std::cout << "5 will never yield return" << std::endl; | |||
co_yield ; | |||
} | |||
*/ | |||
#define co_yield_void co_yield nullptr | |||
#define co_return_void co_return nullptr | |||
auto test_yield_void() | |||
{ | |||
std::cout << "block 1 will yield return" << std::endl; | |||
co_yield_void; | |||
std::cout << "block 2 will yield return" << std::endl; | |||
co_yield_void; | |||
std::cout << "block 3 will yield return" << std::endl; | |||
co_yield_void; | |||
std::cout << "block 4 will return" << std::endl; | |||
co_return_void; | |||
std::cout << "block 5 will never yield return" << std::endl; | |||
co_yield_void; | |||
} | |||
void resumable_main_yield_return() | |||
{ | |||
go test_yield_int(); | |||
g_scheduler.run_until_notask(); | |||
go test_yield_void(); | |||
g_scheduler.run_until_notask(); | |||
} |
@@ -0,0 +1,35 @@ | |||
#include "librf.h" | |||
extern void resumable_main_yield_return(); | |||
extern void resumable_main_timer(); | |||
extern void resumable_main_suspend_always(); | |||
extern void resumable_main_sleep(); | |||
extern void resumable_main_routine(); | |||
extern void resumable_main_resumable(); | |||
extern void resumable_main_mutex(); | |||
extern void resumable_main_exception(); | |||
extern void resumable_main_event(); | |||
extern void resumable_main_event_timeout(); | |||
extern void resumable_main_dynamic_go(); | |||
extern void resumable_main_channel(); | |||
extern void resumable_main_cb(); | |||
int main(int argc, const char * argv[]) | |||
{ | |||
resumable_main_yield_return(); | |||
resumable_main_timer(); | |||
resumable_main_suspend_always(); | |||
resumable_main_sleep(); | |||
resumable_main_routine(); | |||
resumable_main_resumable(); | |||
resumable_main_mutex(); | |||
resumable_main_exception(); | |||
resumable_main_event(); | |||
resumable_main_event_timeout(); | |||
resumable_main_dynamic_go(); | |||
resumable_main_channel(); | |||
resumable_main_cb(); | |||
return 0; | |||
} |
@@ -0,0 +1,31 @@ | |||
| |||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio 15 | |||
VisualStudioVersion = 15.0.26730.15 | |||
MinimumVisualStudioVersion = 10.0.40219.1 | |||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "librf", "librf.vcxproj", "{C1D4A6BD-592F-4E48-8178-7C87219BF80E}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|x64 = Debug|x64 | |||
Debug|x86 = Debug|x86 | |||
Release|x64 = Release|x64 | |||
Release|x86 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x64.ActiveCfg = Debug|x64 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x64.Build.0 = Debug|x64 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x86.ActiveCfg = Debug|Win32 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x86.Build.0 = Debug|Win32 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x64.ActiveCfg = Release|x64 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x64.Build.0 = Release|x64 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x86.ActiveCfg = Release|Win32 | |||
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x86.Build.0 = Release|Win32 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
GlobalSection(ExtensibilityGlobals) = postSolution | |||
SolutionGuid = {401D9E59-A4B3-4CA3-9696-B7D2D14D90FD} | |||
EndGlobalSection | |||
EndGlobal |
@@ -0,0 +1,193 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<ItemGroup Label="ProjectConfigurations"> | |||
<ProjectConfiguration Include="Debug|Win32"> | |||
<Configuration>Debug</Configuration> | |||
<Platform>Win32</Platform> | |||
</ProjectConfiguration> | |||
<ProjectConfiguration Include="Release|Win32"> | |||
<Configuration>Release</Configuration> | |||
<Platform>Win32</Platform> | |||
</ProjectConfiguration> | |||
<ProjectConfiguration Include="Debug|x64"> | |||
<Configuration>Debug</Configuration> | |||
<Platform>x64</Platform> | |||
</ProjectConfiguration> | |||
<ProjectConfiguration Include="Release|x64"> | |||
<Configuration>Release</Configuration> | |||
<Platform>x64</Platform> | |||
</ProjectConfiguration> | |||
</ItemGroup> | |||
<PropertyGroup Label="Globals"> | |||
<VCProjectVersion>15.0</VCProjectVersion> | |||
<ProjectGuid>{C1D4A6BD-592F-4E48-8178-7C87219BF80E}</ProjectGuid> | |||
<Keyword>Win32Proj</Keyword> | |||
<RootNamespace>librf</RootNamespace> | |||
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion> | |||
</PropertyGroup> | |||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | |||
<ConfigurationType>Application</ConfigurationType> | |||
<UseDebugLibraries>true</UseDebugLibraries> | |||
<PlatformToolset>v141</PlatformToolset> | |||
<CharacterSet>NotSet</CharacterSet> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> | |||
<ConfigurationType>Application</ConfigurationType> | |||
<UseDebugLibraries>false</UseDebugLibraries> | |||
<PlatformToolset>v141</PlatformToolset> | |||
<WholeProgramOptimization>true</WholeProgramOptimization> | |||
<CharacterSet>NotSet</CharacterSet> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> | |||
<ConfigurationType>Application</ConfigurationType> | |||
<UseDebugLibraries>true</UseDebugLibraries> | |||
<PlatformToolset>v141</PlatformToolset> | |||
<CharacterSet>NotSet</CharacterSet> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> | |||
<ConfigurationType>Application</ConfigurationType> | |||
<UseDebugLibraries>false</UseDebugLibraries> | |||
<PlatformToolset>v141</PlatformToolset> | |||
<WholeProgramOptimization>true</WholeProgramOptimization> | |||
<CharacterSet>NotSet</CharacterSet> | |||
</PropertyGroup> | |||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | |||
<ImportGroup Label="ExtensionSettings"> | |||
</ImportGroup> | |||
<ImportGroup Label="Shared"> | |||
</ImportGroup> | |||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<PropertyGroup Label="UserMacros" /> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | |||
<LinkIncremental>true</LinkIncremental> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<LinkIncremental>true</LinkIncremental> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
<LinkIncremental>false</LinkIncremental> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<LinkIncremental>false</LinkIncremental> | |||
</PropertyGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<Optimization>Disabled</Optimization> | |||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<SDLCheck>true</SDLCheck> | |||
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
<AdditionalOptions>/await /stdlast++ %(AdditionalOptions)</AdditionalOptions> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Console</SubSystem> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<Optimization>Disabled</Optimization> | |||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<SDLCheck>true</SDLCheck> | |||
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
<AdditionalOptions>/await /std:c++latest %(AdditionalOptions)</AdditionalOptions> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Console</SubSystem> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<Optimization>MaxSpeed</Optimization> | |||
<FunctionLevelLinking>true</FunctionLevelLinking> | |||
<IntrinsicFunctions>true</IntrinsicFunctions> | |||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<SDLCheck>true</SDLCheck> | |||
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
<AdditionalOptions>/await /stdlast++ %(AdditionalOptions)</AdditionalOptions> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Console</SubSystem> | |||
<EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
<OptimizeReferences>true</OptimizeReferences> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<Optimization>MaxSpeed</Optimization> | |||
<FunctionLevelLinking>true</FunctionLevelLinking> | |||
<IntrinsicFunctions>true</IntrinsicFunctions> | |||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<SDLCheck>true</SDLCheck> | |||
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
<AdditionalOptions>/await /stdlast++ %(AdditionalOptions)</AdditionalOptions> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Console</SubSystem> | |||
<EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
<OptimizeReferences>true</OptimizeReferences> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemGroup> | |||
<ClCompile Include="..\librf\src\event.cpp" /> | |||
<ClCompile Include="..\librf\src\mutex.cpp" /> | |||
<ClCompile Include="..\librf\src\rf_task.cpp" /> | |||
<ClCompile Include="..\librf\src\scheduler.cpp" /> | |||
<ClCompile Include="..\librf\src\sleep.cpp" /> | |||
<ClCompile Include="..\librf\src\timer.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_cb.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_channel.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_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_timer.cpp" /> | |||
<ClCompile Include="..\tutorial\test_async_yield_return.cpp" /> | |||
<ClCompile Include="librf.cpp" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClInclude Include="..\librf\librf.h" /> | |||
<ClInclude Include="..\librf\src\asio_task.h" /> | |||
<ClInclude Include="..\librf\src\channel.h" /> | |||
<ClInclude Include="..\librf\src\def.h" /> | |||
<ClInclude Include="..\librf\src\event.h" /> | |||
<ClInclude Include="..\librf\src\future.h" /> | |||
<ClInclude Include="..\librf\src\generator.h" /> | |||
<ClInclude Include="..\librf\src\mutex.h" /> | |||
<ClInclude Include="..\librf\src\rf_task.h" /> | |||
<ClInclude Include="..\librf\src\scheduler.h" /> | |||
<ClInclude Include="..\librf\src\sleep.h" /> | |||
<ClInclude Include="..\librf\src\spinlock.h" /> | |||
<ClInclude Include="..\librf\src\state.h" /> | |||
<ClInclude Include="..\librf\src\timer.h" /> | |||
<ClInclude Include="..\librf\src\unix\coroutine.h" /> | |||
<ClInclude Include="..\librf\src\utils.h" /> | |||
<ClInclude Include="..\librf\src\_awaker.h" /> | |||
</ItemGroup> | |||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |||
<ImportGroup Label="ExtensionTargets"> | |||
</ImportGroup> | |||
</Project> |
@@ -0,0 +1,136 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<ItemGroup> | |||
<Filter Include="Source Files"> | |||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> | |||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> | |||
</Filter> | |||
<Filter Include="librf"> | |||
<UniqueIdentifier>{27e93efb-a01d-48f5-b10b-155fc8d52101}</UniqueIdentifier> | |||
</Filter> | |||
<Filter Include="librf\src"> | |||
<UniqueIdentifier>{7cbfb0bd-8820-4639-9235-93ff984a9bb3}</UniqueIdentifier> | |||
</Filter> | |||
<Filter Include="librf\src\unix"> | |||
<UniqueIdentifier>{46825786-52e6-4092-9418-cad7045aa118}</UniqueIdentifier> | |||
</Filter> | |||
<Filter Include="tutorial"> | |||
<UniqueIdentifier>{d82e4930-c171-4b70-9ebb-cdec7218ff9c}</UniqueIdentifier> | |||
</Filter> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClCompile Include="librf.cpp"> | |||
<Filter>Source Files</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\librf\src\event.cpp"> | |||
<Filter>librf\src</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\librf\src\mutex.cpp"> | |||
<Filter>librf\src</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\librf\src\rf_task.cpp"> | |||
<Filter>librf\src</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\librf\src\scheduler.cpp"> | |||
<Filter>librf\src</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\librf\src\sleep.cpp"> | |||
<Filter>librf\src</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\librf\src\timer.cpp"> | |||
<Filter>librf\src</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_cb.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_channel.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_dynamic_go.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_event.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_event_timeout.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_exception.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_mutex.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_resumable.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_routine.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_sleep.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_suspend_always.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_timer.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
<ClCompile Include="..\tutorial\test_async_yield_return.cpp"> | |||
<Filter>tutorial</Filter> | |||
</ClCompile> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClInclude Include="..\librf\librf.h"> | |||
<Filter>librf</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\_awaker.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\asio_task.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\channel.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\def.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\event.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\future.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\generator.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\mutex.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\rf_task.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\scheduler.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\sleep.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\spinlock.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\state.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\timer.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\utils.h"> | |||
<Filter>librf\src</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\librf\src\unix\coroutine.h"> | |||
<Filter>librf\src\unix</Filter> | |||
</ClInclude> | |||
</ItemGroup> | |||
</Project> |