@@ -11,7 +11,8 @@ | |||
目前仅支持: | |||
Windows (使用VS2017/VS2019编译) | |||
Windows (使用VS2017/VS2019/clang编译) | |||
Android (使用NDK 20.1 自带的clang编译) | |||
librf有以下特点: |
@@ -12,7 +12,7 @@ const size_t LOOP_COUNT = 100; | |||
volatile size_t globalValue = 0; | |||
void resumable_main_benchmark_mem() | |||
void resumable_main_benchmark_mem(bool wait_key) | |||
{ | |||
using namespace std::chrono; | |||
@@ -30,8 +30,11 @@ void resumable_main_benchmark_mem() | |||
} | |||
resumef::this_scheduler()->run_until_notask(); | |||
std::cout << "press any key to continue." << std::endl; | |||
(void)_getch(); | |||
if (wait_key) | |||
{ | |||
std::cout << "press any key to continue." << std::endl; | |||
(void)_getch(); | |||
} | |||
} | |||
//clang : 平均 210字节 |
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#define LIB_RESUMEF_VERSION 20302 // 2.3.2 | |||
#define LIB_RESUMEF_VERSION 20303 // 2.3.3 | |||
#if defined(RESUMEF_MODULE_EXPORT) | |||
#define RESUMEF_NS export namespace resumef |
@@ -150,6 +150,7 @@ RESUMEF_NS | |||
void set_exception(std::exception_ptr e) | |||
{ | |||
(void)e; | |||
std::terminate(); | |||
} | |||
#ifdef __clang__ |
@@ -23,8 +23,11 @@ RESUMEF_NS | |||
promise_impl_t& operator = (const promise_impl_t&) = delete; | |||
auto get_state()->state_type*; | |||
suspend_on_initial initial_suspend() noexcept; | |||
suspend_on_final final_suspend() noexcept; | |||
template <typename _Uty> | |||
_Uty&& await_transform(_Uty&& _Whatever); | |||
void set_exception(std::exception_ptr e); | |||
#ifdef __clang__ | |||
void unhandled_exception(); //If the coroutine ends with an uncaught exception, it performs the following: |
@@ -55,6 +55,17 @@ RESUMEF_NS | |||
return {}; | |||
} | |||
template <typename _Ty> | |||
template <typename _Uty> | |||
_Uty&& promise_impl_t<_Ty>::await_transform(_Uty&& _Whatever) | |||
{ | |||
if constexpr (is_future_v<_Uty> || is_awaitable_v<_Uty>) | |||
{ | |||
_Whatever._state->set_scheduler(get_state()->get_scheduler()); | |||
} | |||
return std::forward<_Uty>(_Whatever); | |||
} | |||
template <typename _Ty> | |||
inline void promise_impl_t<_Ty>::set_exception(std::exception_ptr e) | |||
{ |
@@ -93,43 +93,17 @@ RESUMEF_NS | |||
void scheduler_t::new_task(task_base_t * task) | |||
{ | |||
state_base_t* sptr = task->get_state(); | |||
{ | |||
scoped_lock<spinlock> __guard(_lock_ready); | |||
this->_ready_task.emplace(sptr, task); | |||
} | |||
//如果是单独的future,没有被co_await过,则handler是nullptr。 | |||
sptr->set_scheduler(this); | |||
if (sptr->has_handler()) | |||
this->add_initial(sptr); | |||
} | |||
void scheduler_t::add_initial(state_base_t* sptr) | |||
{ | |||
scoped_lock<spinlock, spinlock> __guard(_lock_ready, _lock_running); | |||
_ready_task.try_emplace(sptr, nullptr); | |||
_runing_states.emplace_back(sptr); | |||
} | |||
void scheduler_t::add_await(state_base_t* sptr) | |||
{ | |||
if (sptr->is_ready()) | |||
{ | |||
scoped_lock<spinlock> __guard(_lock_running); | |||
_runing_states.emplace_back(sptr); | |||
scoped_lock<spinlock, spinlock> __guard(_lock_ready, _lock_running); | |||
_ready_task.emplace(sptr, task); | |||
} | |||
} | |||
void scheduler_t::add_ready(state_base_t* sptr) | |||
{ | |||
assert(sptr->is_ready()); | |||
//如果是单独的future,没有被co_await过,则handler是nullptr。 | |||
if (sptr->has_handler()) | |||
{ | |||
scoped_lock<spinlock> __guard(_lock_running); | |||
_runing_states.emplace_back(sptr); | |||
add_generator(sptr); | |||
} | |||
} | |||
@@ -141,15 +115,8 @@ RESUMEF_NS | |||
void scheduler_t::del_final(state_base_t* sptr) | |||
{ | |||
{ | |||
scoped_lock<spinlock> __guard(_lock_ready); | |||
this->_ready_task.erase(sptr); | |||
} | |||
if (sptr->has_handler()) | |||
{ | |||
scoped_lock<spinlock> __guard(_lock_running); | |||
_runing_states.emplace_back(sptr); | |||
} | |||
scoped_lock<spinlock> __guard(_lock_ready); | |||
this->_ready_task.erase(sptr); | |||
} | |||
std::unique_ptr<task_base_t> scheduler_t::del_switch(state_base_t* sptr) |
@@ -50,9 +50,6 @@ RESUMEF_NS | |||
return _timer.get(); | |||
} | |||
void add_initial(state_base_t* sptr); | |||
void add_await(state_base_t* sptr); | |||
void add_ready(state_base_t* sptr); | |||
void add_generator(state_base_t* sptr); | |||
void del_final(state_base_t* sptr); | |||
std::unique_ptr<task_base_t> del_switch(state_base_t* sptr); |
@@ -54,11 +54,6 @@ RESUMEF_NS | |||
} | |||
} | |||
bool state_generator_t::is_ready() const | |||
{ | |||
return (bool)_coro && !_coro.done(); | |||
} | |||
bool state_generator_t::has_handler() const | |||
{ | |||
return (bool)_coro; | |||
@@ -128,25 +123,22 @@ RESUMEF_NS | |||
bool state_future_t::has_handler() const | |||
{ | |||
scoped_lock<lock_type> __guard(_mtx); | |||
return (bool)_coro || _is_initor != initor_type::None; | |||
} | |||
bool state_future_t::is_ready() const | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
return _exception != nullptr || _has_value.load(std::memory_order_acquire) || !_is_awaitor; | |||
return has_handler_skip_lock(); | |||
} | |||
void state_future_t::set_exception(std::exception_ptr e) | |||
{ | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
this->_exception = std::move(e); | |||
} | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
this->_exception = std::move(e); | |||
scheduler_t* sch = this->get_scheduler(); | |||
if (sch != nullptr) | |||
sch->add_ready(this); | |||
{ | |||
if (this->has_handler_skip_lock()) | |||
sch->add_generator(this); | |||
else | |||
sch->del_final(this); | |||
} | |||
} | |||
bool state_future_t::switch_scheduler_await_suspend(scheduler_t* sch, coroutine_handle<> handler) | |||
@@ -191,13 +183,16 @@ RESUMEF_NS | |||
void state_t<void>::set_value() | |||
{ | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
this->_has_value.store(true, std::memory_order_release); | |||
} | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
this->_has_value.store(true, std::memory_order_release); | |||
scheduler_t* sch = this->get_scheduler(); | |||
if (sch != nullptr) | |||
sch->add_ready(this); | |||
{ | |||
if (this->has_handler_skip_lock()) | |||
sch->add_generator(this); | |||
else | |||
sch->del_final(this); | |||
} | |||
} | |||
} |
@@ -33,7 +33,6 @@ RESUMEF_NS | |||
public: | |||
virtual void resume() = 0; | |||
virtual bool has_handler() const = 0; | |||
virtual bool is_ready() const = 0; | |||
virtual bool switch_scheduler_await_suspend(scheduler_t* sch, coroutine_handle<> handler) = 0; | |||
void set_scheduler(scheduler_t* sch) | |||
@@ -53,7 +52,6 @@ RESUMEF_NS | |||
public: | |||
virtual void resume() override; | |||
virtual bool has_handler() const override; | |||
virtual bool is_ready() const override; | |||
virtual bool switch_scheduler_await_suspend(scheduler_t* sch, coroutine_handle<> handler) override; | |||
void set_initial_suspend(coroutine_handle<> handler) | |||
@@ -114,19 +112,27 @@ RESUMEF_NS | |||
virtual void destroy_deallocate() override; | |||
virtual void resume() override; | |||
virtual bool has_handler() const override; | |||
virtual bool is_ready() const override; | |||
virtual bool switch_scheduler_await_suspend(scheduler_t* sch, coroutine_handle<> handler) override; | |||
inline bool is_ready() const | |||
{ | |||
return _exception != nullptr || _has_value.load(std::memory_order_acquire) || !_is_awaitor; | |||
} | |||
inline bool has_handler_skip_lock() const | |||
{ | |||
return (bool)_coro || _is_initor != initor_type::None; | |||
} | |||
scheduler_t* get_scheduler() const | |||
inline scheduler_t* get_scheduler() const | |||
{ | |||
return _parent ? _parent->get_scheduler() : _scheduler; | |||
} | |||
state_base_t * get_parent() const | |||
inline state_base_t * get_parent() const | |||
{ | |||
return _parent; | |||
} | |||
uint32_t get_alloc_size() const | |||
inline uint32_t get_alloc_size() const | |||
{ | |||
return _alloc_size; | |||
} | |||
@@ -134,14 +140,14 @@ RESUMEF_NS | |||
void set_exception(std::exception_ptr e); | |||
template<class _Exp> | |||
void throw_exception(_Exp e) | |||
inline void throw_exception(_Exp e) | |||
{ | |||
set_exception(std::make_exception_ptr(std::move(e))); | |||
} | |||
template<class _PromiseT, typename = std::enable_if_t<is_promise_v<_PromiseT>>> | |||
void future_await_suspend(coroutine_handle<_PromiseT> handler); | |||
bool future_await_ready() | |||
inline bool future_await_ready() | |||
{ | |||
//scoped_lock<lock_type> __guard(this->_mtx); | |||
return _has_value.load(std::memory_order_acquire); | |||
@@ -153,7 +159,7 @@ RESUMEF_NS | |||
void promise_final_suspend(coroutine_handle<_PromiseT> handler); | |||
template<class _Sty> | |||
static _Sty* _Alloc_state(bool awaitor) | |||
static inline _Sty* _Alloc_state(bool awaitor) | |||
{ | |||
_Alloc_char _Al; | |||
size_t _Size = sizeof(_Sty); |
@@ -14,15 +14,16 @@ RESUMEF_NS | |||
template<class _PromiseT, typename _Enable> | |||
void state_future_t::promise_final_suspend(coroutine_handle<_PromiseT> handler) | |||
{ | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
this->_initor = handler; | |||
this->_is_initor = initor_type::Final; | |||
} | |||
this->_initor = handler; | |||
this->_is_initor = initor_type::Final; | |||
scheduler_t* sch = this->get_scheduler(); | |||
assert(sch != nullptr); | |||
if (this->has_handler_skip_lock()) | |||
sch->add_generator(this); | |||
sch->del_final(this); | |||
} | |||
@@ -33,21 +34,19 @@ RESUMEF_NS | |||
auto* parent_state = promise.get_state(); | |||
scheduler_t* sch = parent_state->get_scheduler(); | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
if (this != parent_state) | |||
{ | |||
this->_parent = parent_state; | |||
this->_scheduler = sch; | |||
} | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
if (!this->_coro) | |||
this->_coro = handler; | |||
if (this != parent_state) | |||
{ | |||
this->_parent = parent_state; | |||
this->_scheduler = sch; | |||
} | |||
if (sch != nullptr) | |||
sch->add_await(this); | |||
if (!this->_coro) | |||
this->_coro = handler; | |||
if (sch != nullptr && this->is_ready()) | |||
sch->add_generator(this); | |||
} | |||
template<class _PromiseT, typename _Enable > | |||
@@ -55,18 +54,16 @@ RESUMEF_NS | |||
{ | |||
coroutine_handle<_PromiseT> handler = coroutine_handle<_PromiseT>::from_promise(*promise); | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
if (!handler.done()) | |||
{ | |||
if (!this->_coro) | |||
this->_coro = handler; | |||
} | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
this->_has_value.store(true, std::memory_order_release); | |||
if (!handler.done()) | |||
{ | |||
if (!this->_coro) | |||
this->_coro = handler; | |||
} | |||
this->_has_value.store(true, std::memory_order_release); | |||
if (!handler.done()) | |||
{ | |||
scheduler_t* sch = this->get_scheduler(); | |||
@@ -81,24 +78,22 @@ RESUMEF_NS | |||
{ | |||
coroutine_handle<_PromiseT> handler = coroutine_handle<_PromiseT>::from_promise(*promise); | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
if (!handler.done()) | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
if (!handler.done()) | |||
{ | |||
if (this->_coro == nullptr) | |||
this->_coro = handler; | |||
} | |||
if (this->_has_value.load(std::memory_order_acquire)) | |||
{ | |||
*this->cast_value_ptr() = std::forward<U>(val); | |||
} | |||
else | |||
{ | |||
new (this->cast_value_ptr()) value_type(std::forward<U>(val)); | |||
this->_has_value.store(true, std::memory_order_release); | |||
} | |||
if (this->_coro == nullptr) | |||
this->_coro = handler; | |||
} | |||
if (this->_has_value.load(std::memory_order_acquire)) | |||
{ | |||
*this->cast_value_ptr() = std::forward<U>(val); | |||
} | |||
else | |||
{ | |||
new (this->cast_value_ptr()) value_type(std::forward<U>(val)); | |||
this->_has_value.store(true, std::memory_order_release); | |||
} | |||
if (!handler.done()) | |||
@@ -125,23 +120,26 @@ RESUMEF_NS | |||
template<typename U> | |||
void state_t<_Ty>::set_value(U&& val) | |||
{ | |||
{ | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
scoped_lock<lock_type> __guard(this->_mtx); | |||
if (this->_has_value.load(std::memory_order_acquire)) | |||
{ | |||
*this->cast_value_ptr() = std::forward<U>(val); | |||
} | |||
else | |||
{ | |||
new (this->cast_value_ptr()) value_type(std::forward<U>(val)); | |||
this->_has_value.store(true, std::memory_order_release); | |||
} | |||
if (this->_has_value.load(std::memory_order_acquire)) | |||
{ | |||
*this->cast_value_ptr() = std::forward<U>(val); | |||
} | |||
else | |||
{ | |||
new (this->cast_value_ptr()) value_type(std::forward<U>(val)); | |||
this->_has_value.store(true, std::memory_order_release); | |||
} | |||
scheduler_t* sch = this->get_scheduler(); | |||
if (sch != nullptr) | |||
sch->add_ready(this); | |||
{ | |||
if (this->has_handler_skip_lock()) | |||
sch->add_generator(this); | |||
else | |||
sch->del_final(this); | |||
} | |||
} | |||
} | |||
@@ -32,7 +32,6 @@ RESUMEF_NS | |||
template<class _Ty> | |||
constexpr bool is_await_suspend_v = is_future_v<_Ty> | |||
|| is_generator_v<_Ty> | |||
|| is_awaitable_v<_Ty> | |||
|| std::is_same_v<remove_cvref_t<_Ty>, switch_scheduler_t> | |||
; |
@@ -62,7 +62,11 @@ static future_t<int64_t> loop_get_long(int64_t val) | |||
void resumable_main_cb() | |||
{ | |||
std::cout << std::this_thread::get_id() << std::endl; | |||
//由于使用者可能不能明确的区分是resume function返回的awaitor还是awaitable function返回的awaitor | |||
//导致均有可能加入到协程里去调度。 | |||
//所以,协程调度器应该需要能处理这种情况。 | |||
go async_get_long(3); | |||
resumef::this_scheduler()->run_until_notask(); | |||
GO | |||
{ |
@@ -10,6 +10,7 @@ | |||
#include "librf.h" | |||
using namespace resumef; | |||
using namespace std::chrono; | |||
const size_t MAX_CHANNEL_QUEUE = 5; //0, 1, 5, 10, -1 | |||
@@ -84,10 +85,43 @@ void test_channel_write_first() | |||
this_scheduler()->run_until_notask(); | |||
} | |||
static const int N = 1000000; | |||
void test_channel_performance() | |||
{ | |||
channel_t<int> c{1}; | |||
go[&]() -> future_t<> | |||
{ | |||
for (int i = N - 1; i >= 0; --i) | |||
{ | |||
co_await(c << i); | |||
} | |||
}; | |||
go[&]() -> future_t<> | |||
{ | |||
auto tstart = high_resolution_clock::now(); | |||
int i; | |||
do | |||
{ | |||
i = co_await c; | |||
} while (i > 0); | |||
auto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count(); | |||
std::cout << "channel w/r " << N << " times, cost time " << dt << "s" << std::endl; | |||
}; | |||
this_scheduler()->run_until_notask(); | |||
} | |||
void resumable_main_channel() | |||
{ | |||
test_channel_read_first(); | |||
std::cout << std::endl; | |||
test_channel_write_first(); | |||
std::cout << std::endl; | |||
test_channel_performance(); | |||
} |
@@ -57,6 +57,21 @@ auto test_yield_void() -> generator_t<> | |||
co_yield_void; | |||
} | |||
auto test_yield_future() -> future_t<int64_t> | |||
{ | |||
std::cout << "future 1 will yield return" << std::endl; | |||
co_yield 1; | |||
std::cout << "future 2 will yield return" << std::endl; | |||
co_yield 2; | |||
std::cout << "future 3 will yield return" << std::endl; | |||
co_yield 3; | |||
std::cout << "future 4 will return" << std::endl; | |||
co_return 4; | |||
std::cout << "future 5 will never yield return" << std::endl; | |||
co_yield 5; | |||
} | |||
void resumable_main_yield_return() | |||
{ | |||
for (int i : test_yield_int()) | |||
@@ -69,4 +84,7 @@ void resumable_main_yield_return() | |||
go test_yield_void(); | |||
this_scheduler()->run_until_notask(); | |||
go test_yield_future(); | |||
this_scheduler()->run_until_notask(); | |||
} |
@@ -22,7 +22,7 @@ extern void resumable_main_when_all(); | |||
extern void resumable_main_layout(); | |||
extern void resumable_main_switch_scheduler(); | |||
extern void resumable_main_benchmark_mem(); | |||
extern void resumable_main_benchmark_mem(bool wait_key); | |||
extern void benchmark_main_channel_passing_next(); | |||
extern void resumable_main_benchmark_asio_server(); | |||
extern void resumable_main_benchmark_asio_client(intptr_t nNum); | |||
@@ -31,8 +31,8 @@ int main(int argc, const char* argv[]) | |||
{ | |||
(void)argc; | |||
(void)argv; | |||
resumable_main_layout(); | |||
return 0; | |||
//resumable_main_resumable(); | |||
//return 0; | |||
//if (argc > 1) | |||
// resumable_main_benchmark_asio_client(atoi(argv[1])); | |||
@@ -50,7 +50,7 @@ int main(int argc, const char* argv[]) | |||
resumable_main_dynamic_go(); | |||
resumable_main_multi_thread(); | |||
resumable_main_timer(); | |||
resumable_main_benchmark_mem(); | |||
resumable_main_benchmark_mem(false); | |||
resumable_main_mutex(); | |||
resumable_main_event(); | |||
resumable_main_event_timeout(); |