same "printed page" as the copyright notice for easier | same "printed page" as the copyright notice for easier | ||||
identification within third-party archives. | identification within third-party archives. | ||||
Copyright {yyyy} {name of copyright owner} | |||||
Copyright 2017 lanzhengpeng | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | Licensed under the Apache License, Version 2.0 (the "License"); | ||||
you may not use this file except in compliance with the License. | you may not use this file except in compliance with the License. |
# 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库的特性** |
/* | |||||
*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" |
#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; | |||||
}; | |||||
} | |||||
} |
| |||||
#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" |
#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; | |||||
} |
#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 |
#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; | |||||
} | |||||
} |
#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); | |||||
}; | |||||
} |
| |||||
#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>; | |||||
} | |||||
/*** | |||||
*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_ */ |
#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; | |||||
} | |||||
} |
#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) | |||||
} |
| |||||
#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)); | |||||
} | |||||
} |
#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()); | |||||
} | |||||
}; | |||||
} |
#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; | |||||
} |
| |||||
#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_); | |||||
} | |||||
} | |||||
#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; | |||||
} | |||||
} |
#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)); | |||||
} | |||||
} |
| |||||
//用于内部实现的循环锁 | |||||
#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); | |||||
} | |||||
}; | |||||
} |
| |||||
#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{}; | |||||
} | |||||
} | |||||
#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); | |||||
} | |||||
} | |||||
} |
#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(); | |||||
} | |||||
} |
//#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() {} | |||||
}; | |||||
} | |||||
} |
#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(); | |||||
} | |||||
} | |||||
| |||||
#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(); | |||||
} |
| |||||
#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(); | |||||
} |
| |||||
#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(); | |||||
} |
| |||||
#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; | |||||
} |
| |||||
#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; | |||||
} |
| |||||
#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(); | |||||
} |
| |||||
#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(); | |||||
} |
| |||||
#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); | |||||
} |
| |||||
#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(); | |||||
} |
| |||||
#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; | |||||
} |
| |||||
#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 | |||||
*/ |
| |||||
#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 | |||||
} |
| |||||
#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(); | |||||
} |
#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; | |||||
} |
| |||||
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 |
<?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> |
<?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> |