#include "src/channel.h" | #include "src/channel.h" | ||||
#include "src/scheduler.h" | #include "src/scheduler.h" | ||||
#include "src/promise.inl" | #include "src/promise.inl" | ||||
#include "src/state.inl" | |||||
#include "src/sleep.h" | #include "src/sleep.h" | ||||
#include "src/awaitable.h" | #include "src/awaitable.h" | ||||
namespace resumef | namespace resumef | ||||
{ | { | ||||
template<class _Ty> | template<class _Ty> | ||||
struct future_impl_t | |||||
struct future_t | |||||
{ | { | ||||
using value_type = _Ty; | using value_type = _Ty; | ||||
using state_type = state_t<value_type>; | using state_type = state_t<value_type>; | ||||
using promise_type = promise_t<value_type>; | using promise_type = promise_t<value_type>; | ||||
using future_type = future_t<value_type>; | using future_type = future_t<value_type>; | ||||
using lock_type = typename state_type::lock_type; | |||||
counted_ptr<state_type> _state; | counted_ptr<state_type> _state; | ||||
future_impl_t(counted_ptr<state_type> _st) | |||||
future_t(counted_ptr<state_type> _st) | |||||
:_state(std::move(_st)) {} | :_state(std::move(_st)) {} | ||||
future_impl_t(const future_impl_t&) = default; | |||||
future_impl_t(future_impl_t&&) = default; | |||||
future_t(const future_t&) = default; | |||||
future_t(future_t&&) = default; | |||||
future_impl_t& operator = (const future_impl_t&) = default; | |||||
future_impl_t& operator = (future_impl_t&&) = default; | |||||
future_t& operator = (const future_t&) = default; | |||||
future_t& operator = (future_t&&) = default; | |||||
bool await_ready() | bool await_ready() | ||||
{ | { | ||||
return _state->has_value(); | |||||
return _state->future_await_ready(); | |||||
} | } | ||||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | ||||
void await_suspend(coroutine_handle<_PromiseT> handler) | void await_suspend(coroutine_handle<_PromiseT> handler) | ||||
{ | { | ||||
_PromiseT& promise = handler.promise(); | |||||
scheduler_t* sch = promise._state->_scheduler; | |||||
sch->add_await(_state.get(), handler); | |||||
_state->future_await_suspend(handler); | |||||
} | } | ||||
}; | |||||
template<class _Ty> | |||||
struct future_t : public future_impl_t<_Ty> | |||||
{ | |||||
using future_impl_t::future_impl_t; | |||||
using future_impl_t::_state; | |||||
value_type await_resume() | value_type await_resume() | ||||
{ | { | ||||
if (_state->_exception) | |||||
std::rethrow_exception(std::move(_state->_exception)); | |||||
return std::move(_state->_value.value()); | |||||
} | |||||
}; | |||||
template<> | |||||
struct future_t<void> : public future_impl_t<void> | |||||
{ | |||||
using future_impl_t::future_impl_t; | |||||
using future_impl_t::_state; | |||||
void await_resume() | |||||
{ | |||||
if (_state->_exception) | |||||
std::rethrow_exception(std::move(_state->_exception)); | |||||
return _state->future_await_resume(); | |||||
} | } | ||||
}; | }; | ||||
} | } |
} | } | ||||
scheduler_t::scheduler_t() | scheduler_t::scheduler_t() | ||||
: _task() | |||||
, _ready_task() | |||||
, _timer(std::make_shared<timer_manager>()) | |||||
: _timer(std::make_shared<timer_manager>()) | |||||
{ | { | ||||
_runing_states.reserve(1024); | |||||
_cached_states.reserve(1024); | |||||
} | } | ||||
scheduler_t::~scheduler_t() | scheduler_t::~scheduler_t() | ||||
{ | { | ||||
cancel_all_task_(); | |||||
//cancel_all_task_(); | |||||
#if RESUMEF_ENABLE_MULT_SCHEDULER | #if RESUMEF_ENABLE_MULT_SCHEDULER | ||||
if (th_scheduler_ptr == this) | if (th_scheduler_ptr == this) | ||||
th_scheduler_ptr = nullptr; | th_scheduler_ptr = nullptr; | ||||
void scheduler_t::new_task(task_base_t * task) | void scheduler_t::new_task(task_base_t * task) | ||||
{ | { | ||||
if (task) | |||||
state_base_t* sptr = task->get_state(); | |||||
{ | { | ||||
scoped_lock<spinlock> __guard(_mtx_ready); | |||||
scoped_lock<spinlock> __guard(_lock_ready); | |||||
this->_ready_task.emplace(sptr, task); | |||||
} | |||||
//如果是单独的future,没有被co_await过,则handler是nullptr。 | |||||
if (sptr->get_handler() != nullptr) | |||||
this->add_initial(sptr); | |||||
else | |||||
sptr->set_scheduler(this); | |||||
} | |||||
void scheduler_t::add_initial(state_base_t* sptr) | |||||
{ | |||||
sptr->set_scheduler(this); | |||||
this->_ready_task.push_back(task); | |||||
this->add_initial(task->get_state()); | |||||
scoped_lock<lock_type> __guard(_lock_running); | |||||
_runing_states.emplace_back(sptr); | |||||
} | |||||
void scheduler_t::add_await(state_base_t* sptr, coroutine_handle<> handler) | |||||
{ | |||||
sptr->set_scheduler(this); | |||||
sptr->set_handler(handler); | |||||
if (sptr->is_ready()) | |||||
{ | |||||
scoped_lock<lock_type> __guard(_lock_running); | |||||
_runing_states.emplace_back(sptr); | |||||
} | } | ||||
} | } | ||||
void scheduler_t::cancel_all_task_() | |||||
void scheduler_t::add_ready(state_base_t* sptr) | |||||
{ | { | ||||
assert(sptr->get_scheduler() == this); | |||||
if (sptr->get_handler() != nullptr) | |||||
{ | { | ||||
scoped_lock<lock_type> __guard(_mtx_task); | |||||
this->_task.clear(true); | |||||
scoped_lock<lock_type> __guard(_lock_running); | |||||
_runing_states.emplace_back(sptr); | |||||
} | } | ||||
else | |||||
{ | { | ||||
scoped_lock<spinlock> __guard(_mtx_ready); | |||||
this->_ready_task.clear(true); | |||||
scoped_lock<spinlock> __guard(_lock_ready); | |||||
this->_ready_task.erase(sptr); | |||||
} | } | ||||
} | } | ||||
void scheduler_t::del_final(state_base_t* sptr) | |||||
{ | |||||
assert(sptr->get_scheduler() == this); | |||||
scoped_lock<spinlock> __guard(_lock_ready); | |||||
this->_ready_task.erase(sptr); | |||||
} | |||||
/* | |||||
void scheduler_t::cancel_all_task_() | |||||
{ | |||||
scoped_lock<spinlock, lock_type> __guard(_lock_ready, _lock_running); | |||||
this->_ready_task.clear(); | |||||
this->_runing_states.clear(); | |||||
} | |||||
void scheduler_t::break_all() | void scheduler_t::break_all() | ||||
{ | { | ||||
cancel_all_task_(); | cancel_all_task_(); | ||||
scoped_lock<lock_type> __guard(_mtx_task); | |||||
this->_timer->clear(); | this->_timer->clear(); | ||||
} | } | ||||
*/ | |||||
void scheduler_t::run_one_batch() | void scheduler_t::run_one_batch() | ||||
{ | { | ||||
if (th_scheduler_ptr == nullptr) | if (th_scheduler_ptr == nullptr) | ||||
th_scheduler_ptr = this; | th_scheduler_ptr = this; | ||||
#endif | #endif | ||||
this->_timer->update(); | |||||
{ | { | ||||
scoped_lock<lock_type> __guard(_mtx_task); | |||||
scoped_lock<lock_type> __guard(_lock_running); | |||||
std::swap(_cached_states, _runing_states); | |||||
} | |||||
this->_timer->update(); | |||||
for (state_sptr& sptr : _cached_states) | |||||
sptr->resume(); | |||||
state_vector states = std::move(_runing_states); | |||||
for (state_sptr& sptr : states) | |||||
sptr->resume(); | |||||
} | |||||
_cached_states.clear(); | |||||
} | } | ||||
void scheduler_t::run_until_notask() | void scheduler_t::run_until_notask() |
struct scheduler_t : public std::enable_shared_from_this<scheduler_t> | struct scheduler_t : public std::enable_shared_from_this<scheduler_t> | ||||
{ | { | ||||
using state_sptr = std::shared_ptr<state_base_t>; | |||||
using state_sptr = counted_ptr<state_base_t>; | |||||
using state_vector = std::vector<state_sptr>; | using state_vector = std::vector<state_sptr>; | ||||
private: | private: | ||||
state_vector _runing_states; | |||||
private: | |||||
//typedef spinlock lock_type; | |||||
typedef std::recursive_mutex lock_type; | |||||
using lock_type = std::recursive_mutex; | |||||
using task_dictionary_type = std::unordered_map<state_base_t*, task_base_t*>; | |||||
mutable lock_type _lock_running; | |||||
state_vector _runing_states; | |||||
state_vector _cached_states; | |||||
mutable spinlock _mtx_ready; | |||||
task_list _ready_task; | |||||
mutable spinlock _lock_ready; | |||||
task_dictionary_type _ready_task; | |||||
mutable lock_type _mtx_task; | |||||
task_list _task; | |||||
timer_mgr_ptr _timer; | |||||
timer_mgr_ptr _timer; | |||||
RF_API void new_task(task_base_t * task); | RF_API void new_task(task_base_t * task); | ||||
void cancel_all_task_(); | |||||
//void cancel_all_task_(); | |||||
public: | public: | ||||
RF_API void run_one_batch(); | RF_API void run_one_batch(); | ||||
RF_API void run_until_notask(); | RF_API void run_until_notask(); | ||||
RF_API void run(); | RF_API void run(); | ||||
//RF_API void break_all(); | |||||
template<class _Ty, typename = std::enable_if_t<std::is_callable_v<_Ty> || is_future_v<_Ty>>> | template<class _Ty, typename = std::enable_if_t<std::is_callable_v<_Ty> || is_future_v<_Ty>>> | ||||
inline void operator + (_Ty && t_) | inline void operator + (_Ty && t_) | ||||
inline bool empty() const | inline bool empty() const | ||||
{ | { | ||||
scoped_lock<spinlock, lock_type> __guard(_mtx_ready, _mtx_task); | |||||
return this->_task.empty() && this->_ready_task.empty() && this->_timer->empty(); | |||||
scoped_lock<spinlock> __guard(_lock_ready); | |||||
return this->_ready_task.empty() && this->_timer->empty(); | |||||
} | } | ||||
RF_API void break_all(); | |||||
inline timer_manager * timer() const | inline timer_manager * timer() const | ||||
{ | { | ||||
return _timer.get(); | return _timer.get(); | ||||
} | } | ||||
void add_initial(state_base_t* sptr); | |||||
void add_await(state_base_t* sptr, coroutine_handle<> handler); | |||||
void add_ready(state_base_t* sptr); | |||||
void del_final(state_base_t* sptr); | |||||
friend struct task_base; | friend struct task_base; | ||||
friend struct local_scheduler; | friend struct local_scheduler; | ||||
void add_initial(state_base_t* sptr) | |||||
{ | |||||
sptr->_scheduler = this; | |||||
assert(sptr->_coro != nullptr); | |||||
_runing_states.emplace_back(sptr); | |||||
} | |||||
void add_await(state_base_t* sptr, coroutine_handle<> handler) | |||||
{ | |||||
sptr->_scheduler = this; | |||||
sptr->_coro = handler; | |||||
if (sptr->has_value() || sptr->_exception != nullptr) | |||||
_runing_states.emplace_back(sptr); | |||||
} | |||||
void add_ready(state_base_t* sptr) | |||||
{ | |||||
assert(sptr->_scheduler == this); | |||||
if (sptr->_coro != nullptr) | |||||
_runing_states.emplace_back(sptr); | |||||
} | |||||
protected: | protected: | ||||
RF_API scheduler_t(); | RF_API scheduler_t(); | ||||
public: | public: |
namespace resumef | namespace resumef | ||||
{ | { | ||||
std::atomic<intptr_t> g_resumef_static_count = {0}; | |||||
state_base_t::~state_base_t() | state_base_t::~state_base_t() | ||||
{ | { | ||||
} | } | ||||
void state_base_t::promise_final_suspend() | |||||
{ | |||||
scoped_lock<lock_type> __guard(this->_mtx); | |||||
scheduler_t* _scheduler = this->_scheduler; | |||||
if (_scheduler != nullptr) | |||||
_scheduler->del_final(this); | |||||
} | |||||
void state_base_t::set_exception(std::exception_ptr e) | |||||
{ | |||||
scoped_lock<lock_type> __guard(this->_mtx); | |||||
this->_exception = std::move(e); | |||||
if (this->_scheduler != nullptr) | |||||
this->_scheduler->add_ready(this); | |||||
} | |||||
void state_t<void>::future_await_resume() | |||||
{ | |||||
scoped_lock<lock_type> __guard(this->_mtx); | |||||
if (this->_exception) | |||||
std::rethrow_exception(std::move(this->_exception)); | |||||
} | |||||
void state_t<void>::set_value() | |||||
{ | |||||
scoped_lock<lock_type> __guard(this->_mtx); | |||||
this->_has_value = true; | |||||
if (this->_scheduler != nullptr) | |||||
this->_scheduler->add_ready(this); | |||||
} | |||||
} | } |
namespace resumef | namespace resumef | ||||
{ | { | ||||
extern std::atomic<intptr_t> g_resumef_static_count; | |||||
struct state_base_t : public counted_t<state_base_t> | struct state_base_t : public counted_t<state_base_t> | ||||
{ | { | ||||
typedef std::recursive_mutex lock_type; | typedef std::recursive_mutex lock_type; | ||||
lock_type _mtx; | |||||
scheduler_t * _scheduler = nullptr; | |||||
protected: | |||||
mutable lock_type _mtx; | |||||
scheduler_t* _scheduler = nullptr; | |||||
//可能来自协程里的promise产生的,则经过co_await操作后,_coro在初始时不会为nullptr。 | |||||
//也可能来自awaitable_t,如果 | |||||
// 一、经过co_await操作后,_coro在初始时不会为nullptr。 | |||||
// 二、没有co_await操作,直接加入到了调度器里,则_coro在初始时为nullptr。调度器需要特殊处理此种情况。 | |||||
coroutine_handle<> _coro; | coroutine_handle<> _coro; | ||||
std::exception_ptr _exception; | std::exception_ptr _exception; | ||||
public: | |||||
intptr_t _id; | |||||
state_base_t* _parent = nullptr; | |||||
state_base_t() | |||||
{ | |||||
_id = ++g_resumef_static_count; | |||||
} | |||||
RF_API virtual ~state_base_t(); | RF_API virtual ~state_base_t(); | ||||
virtual bool has_value() const = 0; | virtual bool has_value() const = 0; | ||||
bool is_ready() const | |||||
{ | |||||
return has_value() && _exception != nullptr; | |||||
} | |||||
void resume() | void resume() | ||||
{ | { | ||||
scoped_lock<lock_type> __guard(_mtx); | scoped_lock<lock_type> __guard(_mtx); | ||||
_coro = nullptr; | _coro = nullptr; | ||||
handler(); | handler(); | ||||
} | } | ||||
void set_handler(coroutine_handle<> handler) | |||||
{ | |||||
scoped_lock<lock_type> __guard(_mtx); | |||||
assert(_coro == nullptr); | |||||
_coro = handler; | |||||
} | |||||
coroutine_handle<> get_handler() const | |||||
{ | |||||
return _coro; | |||||
} | |||||
void set_scheduler(scheduler_t* sch) | |||||
{ | |||||
scoped_lock<lock_type> __guard(_mtx); | |||||
_scheduler = sch; | |||||
} | |||||
scheduler_t* get_scheduler() const | |||||
{ | |||||
return _scheduler; | |||||
} | |||||
void set_exception(std::exception_ptr e); | |||||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | |||||
void future_await_suspend(coroutine_handle<_PromiseT> handler); | |||||
void promise_final_suspend(); | |||||
}; | }; | ||||
template <typename _Ty> | template <typename _Ty> | ||||
struct state_t : public state_base_t | struct state_t : public state_base_t | ||||
{ | { | ||||
using state_base_t::lock_type; | |||||
using value_type = _Ty; | using value_type = _Ty; | ||||
protected: | |||||
std::optional<value_type> _value; | std::optional<value_type> _value; | ||||
public: | |||||
virtual bool has_value() const override | virtual bool has_value() const override | ||||
{ | { | ||||
scoped_lock<lock_type> __guard(this->_mtx); | |||||
return _value.has_value(); | |||||
} | |||||
bool future_await_ready() | |||||
{ | |||||
scoped_lock<lock_type> __guard(this->_mtx); | |||||
return _value.has_value(); | return _value.has_value(); | ||||
} | } | ||||
auto future_await_resume() -> value_type; | |||||
void set_value(value_type val); | |||||
}; | }; | ||||
template<> | template<> | ||||
struct state_t<void> : public state_base_t | struct state_t<void> : public state_base_t | ||||
{ | { | ||||
bool _has_value = false; | |||||
using state_base_t::lock_type; | |||||
protected: | |||||
std::atomic<bool> _has_value{ false }; | |||||
public: | |||||
virtual bool has_value() const override | virtual bool has_value() const override | ||||
{ | { | ||||
return _has_value; | return _has_value; | ||||
} | } | ||||
bool future_await_ready() | |||||
{ | |||||
return _has_value; | |||||
} | |||||
void future_await_resume(); | |||||
void set_value(); | |||||
}; | }; | ||||
} | } | ||||
using namespace std::chrono; | using namespace std::chrono; | ||||
std::thread([val, cb = std::forward<_Ctype>(cb)] | std::thread([val, cb = std::forward<_Ctype>(cb)] | ||||
{ | { | ||||
std::this_thread::sleep_for(500ms); | |||||
//std::this_thread::sleep_for(500ms); | |||||
std::this_thread::sleep_for(10s); | |||||
cb(val * val); | cb(val * val); | ||||
}).detach(); | }).detach(); | ||||
} | } | ||||
return st.get_future(); | return st.get_future(); | ||||
} | } | ||||
resumef::future_t<> wait_get_long(int64_t val) | |||||
{ | |||||
co_await async_get_long(val); | |||||
} | |||||
//这种情况下,会生成对应的 frame-context,一个promise_type被内嵌在frame-context里 | //这种情况下,会生成对应的 frame-context,一个promise_type被内嵌在frame-context里 | ||||
resumef::future_t<> resumable_get_long(int64_t val) | resumef::future_t<> resumable_get_long(int64_t val) | ||||
{ | { | ||||
val = co_await async_get_long(val); | val = co_await async_get_long(val); | ||||
std::cout << val << std::endl; | std::cout << val << std::endl; | ||||
} | } | ||||
return val; | |||||
co_return val; | |||||
} | } | ||||
void resumable_main_cb() | void resumable_main_cb() | ||||
{ | { | ||||
std::cout << std::this_thread::get_id() << std::endl; | std::cout << std::this_thread::get_id() << std::endl; | ||||
go wait_get_long(3); | |||||
resumef::this_scheduler()->run_until_notask(); | |||||
go []()->resumef::future_t<> | |||||
GO | |||||
{ | { | ||||
auto val = co_await loop_get_long(2); | auto val = co_await loop_get_long(2); | ||||
std::cout << val << std::endl; | std::cout << val << std::endl; | ||||
}; | }; | ||||
//resumef::this_scheduler()->run_until_notask(); | |||||
resumef::this_scheduler()->run_until_notask(); | |||||
go resumable_get_long(3); | |||||
//go resumable_get_long(3); | |||||
resumef::this_scheduler()->run_until_notask(); | resumef::this_scheduler()->run_until_notask(); | ||||
} | } |
extern void resumable_main_benchmark_asio_server(); | extern void resumable_main_benchmark_asio_server(); | ||||
extern void resumable_main_benchmark_asio_client(intptr_t nNum); | extern void resumable_main_benchmark_asio_client(intptr_t nNum); | ||||
void async_get_long(int64_t val, std::function<void(int64_t)> cb) | |||||
{ | |||||
using namespace std::chrono; | |||||
std::thread([val, cb = std::move(cb)] | |||||
{ | |||||
std::this_thread::sleep_for(10s); | |||||
cb(val * val); | |||||
}).detach(); | |||||
} | |||||
//这种情况下,没有生成 frame-context,因此,并没有promise_type被内嵌在frame-context里 | |||||
resumef::future_t<int64_t> co_get_long(int64_t val) | |||||
{ | |||||
resumef::awaitable_t<int64_t > st; | |||||
std::cout << "co_get_long@1" << std::endl; | |||||
async_get_long(val, [st](int64_t value) | |||||
{ | |||||
std::cout << "co_get_long@2" << std::endl; | |||||
st.set_value(value); | |||||
}); | |||||
std::cout << "co_get_long@3" << std::endl; | |||||
return st.get_future(); | |||||
} | |||||
//这种情况下,会生成对应的 frame-context,一个promise_type被内嵌在frame-context里 | |||||
resumef::future_t<> test_librf2() | |||||
{ | |||||
auto f = co_await co_get_long(2); | |||||
std::cout << f << std::endl; | |||||
} | |||||
int main(int argc, const char* argv[]) | int main(int argc, const char* argv[]) | ||||
{ | { | ||||
resumable_main_cb(); | resumable_main_cb(); |
<None Include="..\librf\src\asio_task_1.10.0.inl" /> | <None Include="..\librf\src\asio_task_1.10.0.inl" /> | ||||
<None Include="..\librf\src\asio_task_1.12.0.inl" /> | <None Include="..\librf\src\asio_task_1.12.0.inl" /> | ||||
<None Include="..\librf\src\promise.inl" /> | <None Include="..\librf\src\promise.inl" /> | ||||
<None Include="..\librf\src\state.inl" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
<ImportGroup Label="ExtensionTargets"> | <ImportGroup Label="ExtensionTargets"> |
<None Include="..\librf\src\promise.inl"> | <None Include="..\librf\src\promise.inl"> | ||||
<Filter>librf\src</Filter> | <Filter>librf\src</Filter> | ||||
</None> | </None> | ||||
<None Include="..\librf\src\state.inl"> | |||||
<Filter>librf\src</Filter> | |||||
</None> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |