Browse Source

create repository

tags/v2.9.7
tearshark 6 years ago
parent
commit
ba792374ab
51 changed files with 4699 additions and 2 deletions
  1. 1
    1
      LICENSE
  2. 24
    1
      README.md
  3. BIN
      doc/C++ Coroutines - Gor Nishanov - CppCon 2015 (1.pdf
  4. BIN
      doc/C++ Coroutines - Under The Covers - Gor Nishan.pdf
  5. BIN
      doc/CppNow2015_Coroutines_in_C++17.pdf
  6. BIN
      doc/await 2.0 - Stackless Resumable Functions - Gor Nishanov - CppCon 2014.pdf
  7. BIN
      doc/n3964-Asynchronous Operations.pdf
  8. BIN
      doc/n4134-Resumable Functions v2 .pdf
  9. BIN
      doc/n4649-Working Draft Technical Specification for C++ Extensions for Coroutines.pdf
  10. BIN
      doc/p0057r0-Wording for Coroutines.pdf
  11. BIN
      doc/p0057r3-Wording for Coroutines(Revision 3).pdf
  12. 23
    0
      librf/librf.h
  13. 59
    0
      librf/src/_awaker.h
  14. 150
    0
      librf/src/asio_task.h
  15. 260
    0
      librf/src/channel.h
  16. 107
    0
      librf/src/def.h
  17. 356
    0
      librf/src/event.cpp
  18. 207
    0
      librf/src/event.h
  19. 345
    0
      librf/src/future.h
  20. 239
    0
      librf/src/generator.h
  21. 126
    0
      librf/src/mutex.cpp
  22. 92
    0
      librf/src/mutex.h
  23. 78
    0
      librf/src/rf_task.cpp
  24. 146
    0
      librf/src/rf_task.h
  25. 175
    0
      librf/src/scheduler.cpp
  26. 94
    0
      librf/src/scheduler.h
  27. 20
    0
      librf/src/sleep.cpp
  28. 35
    0
      librf/src/sleep.h
  29. 44
    0
      librf/src/spinlock.h
  30. 284
    0
      librf/src/state.h
  31. 106
    0
      librf/src/timer.cpp
  32. 186
    0
      librf/src/timer.h
  33. 91
    0
      librf/src/unix/coroutine.h
  34. 17
    0
      librf/src/utils.h
  35. 62
    0
      tutorial/test_async_cb.cpp
  36. 89
    0
      tutorial/test_async_channel.cpp
  37. 62
    0
      tutorial/test_async_dynamic_go.cpp
  38. 163
    0
      tutorial/test_async_event.cpp
  39. 178
    0
      tutorial/test_async_event_timeout.cpp
  40. 75
    0
      tutorial/test_async_exception.cpp
  41. 55
    0
      tutorial/test_async_mutex.cpp
  42. 52
    0
      tutorial/test_async_resumable.cpp
  43. 35
    0
      tutorial/test_async_routine.cpp
  44. 66
    0
      tutorial/test_async_sleep.cpp
  45. 95
    0
      tutorial/test_async_suspend_always.cpp
  46. 36
    0
      tutorial/test_async_timer.cpp
  47. 71
    0
      tutorial/test_async_yield_return.cpp
  48. 35
    0
      vs_proj/librf.cpp
  49. 31
    0
      vs_proj/librf.sln
  50. 193
    0
      vs_proj/librf.vcxproj
  51. 136
    0
      vs_proj/librf.vcxproj.filters

+ 1
- 1
LICENSE View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright {yyyy} {name of copyright owner}
Copyright 2017 lanzhengpeng

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

+ 24
- 1
README.md View File

@@ -1 +1,24 @@
# librf
# librf

### librf - 协程库

librf是一个基于C++ Coroutines提案 ‘Stackless Resumable Functions’编写的非对称stackless协程库。

目前仅支持:

Windows (使用VS2015/2016编译)


librf有以下特点:

* 1.基于C++17提案'Stackless Resumable Functions'编写的非对称stackless协程库,可以以同步的方式编写简单的代码,同时获得异步的性能
* 2.理论上支持海量协程, 创建100万个协程只需使用<待测试>物理内存
* 3.提供协程锁(mutex), 定时器, channel等特性, 帮助用户更加容易地编写程序
* 4.可以很好的跟asio,libuv等库结合,能更现有的callback范式的异步/延迟代码结合
* 5.目前还处于实验状态,不对今后正式的C++ Coroutines支持有任何正式的承诺
* 如果你发现了任何bug、有好的建议、或使用上有不明之处,可以提交到issue,也可以直接联系作者:
email: tearshark@163.net QQ交流群: 296561497

* **doc目录下有作者搜集的一些关于C++协程的资料**
* **tutorial目录下有针对每个特性的范例代码,让用户可以循序渐进的了解librf库的特性**

BIN
doc/C++ Coroutines - Gor Nishanov - CppCon 2015 (1.pdf View File


BIN
doc/C++ Coroutines - Under The Covers - Gor Nishan.pdf View File


BIN
doc/CppNow2015_Coroutines_in_C++17.pdf View File


BIN
doc/await 2.0 - Stackless Resumable Functions - Gor Nishanov - CppCon 2014.pdf View File


BIN
doc/n3964-Asynchronous Operations.pdf View File


BIN
doc/n4134-Resumable Functions v2 .pdf View File


BIN
doc/n4649-Working Draft Technical Specification for C++ Extensions for Coroutines.pdf View File


BIN
doc/p0057r0-Wording for Coroutines.pdf View File


BIN
doc/p0057r3-Wording for Coroutines(Revision 3).pdf View File


+ 23
- 0
librf/librf.h View File

@@ -0,0 +1,23 @@
/*
*Copyright 2017 lanzhengpeng
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/
#pragma once
#include "src/event.h"
#include "src/mutex.h"
#include "src/channel.h"
#include "src/scheduler.h"
#include "src/sleep.h"

+ 59
- 0
librf/src/_awaker.h View File

@@ -0,0 +1,59 @@
#pragma once
#include "spinlock.h"
#include "future.h"
namespace resumef
{
namespace detail
{
template<class _Ety, class... _Types>
struct _awaker
{
//如果超时
// e 始终为nullptr
// 不关心返回值
//如果不是超时,
// e 指向当前触发的事件,用于实现wait_any
// 返回true表示成功触发了事件,event内部减小一次事件计数,并删除此awaker
// 返回false表示此事件已经无效,event内部只删除此awaker
typedef std::function<bool(_Ety * e, _Types...)> callee_type;
private:
spinlock _lock;
callee_type _callee;
std::atomic<intptr_t> _counter;
public:
_awaker(callee_type && callee_, intptr_t init_count_ = 0)
: _callee(std::forward<callee_type>(callee_))
, _counter(init_count_)
{
}
//调用一次后,_callee就被置nullptr,下次再调用,必然返回false
//第一次调用,返回调用_callee的返回值
//超时通过传入nullptr来调用
bool awake(_Ety * e, intptr_t count_, const _Types&... args)
{
assert(count_ > 0);
scoped_lock<spinlock> lock_(this->_lock);
if ((this->_counter.fetch_sub(count_) - count_) <= 0)
{
if (this->_callee)
{
callee_type callee_ = std::move(this->_callee);
return callee_(e, args...);
}
return false;
}
return true;
}
private:
_awaker(const _awaker &) = delete;
_awaker(_awaker &&) = delete;
_awaker & operator = (const _awaker &) = delete;
_awaker & operator = (_awaker &&) = delete;
};
}
}

+ 150
- 0
librf/src/asio_task.h View File

@@ -0,0 +1,150 @@

#pragma once

#include "future.h"
#include "asio/detail/config.hpp"
#include <memory>

#include "asio/detail/push_options.hpp"
#include "asio/async_result.hpp"
#include "asio/error_code.hpp"
#include "asio/handler_type.hpp"
#include "asio/system_error.hpp"

namespace asio {

template<typename Allocator = std::allocator<void> >
class rf_task_t
{
public:
typedef Allocator allocator_type;
constexpr rf_task_t() {}
explicit rf_task_t(const Allocator& allocator) : allocator_(allocator) {}

template<typename OtherAllocator>
rf_task_t<OtherAllocator> operator[](const OtherAllocator& allocator) const {
return rf_task_t<OtherAllocator>(allocator);
}

allocator_type get_allocator() const { return allocator_; }
private:
Allocator allocator_;
};

//constexpr rf_task_t<> rf_task;
#pragma warning(push)
#pragma warning(disable : 4592)
__declspec(selectany) rf_task_t<> rf_task;
#pragma warning(pop)

namespace detail {

template<typename T>
class promise_handler
{
public:
using result_type_t = T;
using state_type = resumef::state_t<result_type_t>;

template<typename Allocator>
promise_handler(const rf_task_t<Allocator> &)
: state_(resumef::make_counted<state_type>())
{
}

void operator()(T t) const
{
state_->set_value(std::move(t));
}

void operator()(const asio::error_code& ec, T t) const
{
if (!ec)
{
state_->set_value(std::move(t));
}
else
{
state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));
}
}

resumef::counted_ptr<state_type> state_;
};

template<>
class promise_handler<void>
{
public:
using result_type_t = void;
using state_type = resumef::state_t<result_type_t>;

template<typename Allocator>
promise_handler(const rf_task_t<Allocator> &)
: state_(resumef::make_counted<state_type>())
{
}

void operator()() const
{
state_->set_value();
}

void operator()(const asio::error_code& ec) const
{
if (!ec)
{
state_->set_value();
}
else
{
state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));
}
}

resumef::counted_ptr<state_type> state_;
};

} // namespace detail

template<typename T>
class async_result<detail::promise_handler<T> >
{
public:
typedef resumef::future_t<T> type;

explicit async_result(detail::promise_handler<T> & h)
: task_(std::move(h.state_))
{ }

resumef::future_t<T> get() { return std::move(task_); }
private:
resumef::future_t<T> task_;
};

// Handler type specialisation for zero arg.
template<typename Allocator, typename ReturnType>
struct handler_type<rf_task_t<Allocator>, ReturnType()> {
typedef detail::promise_handler<void> type;
};

// Handler type specialisation for one arg.
template<typename Allocator, typename ReturnType, typename Arg1>
struct handler_type<rf_task_t<Allocator>, ReturnType(Arg1)> {
typedef detail::promise_handler<Arg1> type;
};

// Handler type specialisation for two arg.
template<typename Allocator, typename ReturnType, typename Arg2>
struct handler_type<rf_task_t<Allocator>, ReturnType(asio::error_code, Arg2)> {
typedef detail::promise_handler<Arg2> type;
};

template<typename Allocator, typename ReturnType>
struct handler_type<rf_task_t<Allocator>, ReturnType(asio::error_code)> {
typedef detail::promise_handler<void> type;
};

} // namespace asio

#include "asio/detail/pop_options.hpp"

+ 260
- 0
librf/src/channel.h View File

@@ -0,0 +1,260 @@
#pragma once
#include "_awaker.h"
namespace resumef
{
namespace detail
{
template<class _Ty>
struct channel_impl : public std::enable_shared_from_this<channel_impl<_Ty>>
{
typedef _awaker<channel_impl<_Ty>, _Ty *, future_error> channel_read_awaker;
typedef std::shared_ptr<channel_read_awaker> channel_read_awaker_ptr;
typedef _awaker<channel_impl<_Ty>> channel_write_awaker;
typedef std::shared_ptr<channel_write_awaker> channel_write_awaker_ptr;
typedef std::pair<channel_write_awaker_ptr, _Ty> write_tuple_type;
private:
spinlock _lock; //保证访问本对象是线程安全的
const size_t _max_counter; //数据队列的容量上限
std::deque<_Ty> _values; //数据队列
std::list<channel_read_awaker_ptr> _read_awakes; //读队列
std::list<write_tuple_type> _write_awakes; //写队列
public:
channel_impl(size_t max_counter_)
:_max_counter(max_counter_)
{
}
#if _DEBUG
const std::deque<_Ty> & debug_queue() const
{
return _values;
}
#endif
template<class callee_t, class = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, channel_read_awaker_ptr>::value>>
auto read(callee_t && awaker)
{
return read_(std::make_shared<channel_read_awaker>(std::forward<callee_t>(awaker)));
}
template<class callee_t, class = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, channel_write_awaker_ptr>::value>>
auto write(callee_t && awaker, const _Ty& val)
{
return write_(std::make_shared<channel_write_awaker>(std::forward<callee_t>(awaker)), val);
}
template<class callee_t, class = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, channel_write_awaker_ptr>::value>>
auto write(callee_t && awaker, _Ty&& val)
{
return write_(std::make_shared<channel_write_awaker>(std::forward<callee_t>(awaker)), std::forward<_Ty>(val));
}
//如果已经触发了awaker,则返回true
bool read_(channel_read_awaker_ptr && r_awaker)
{
assert(r_awaker);
scoped_lock<spinlock> lock_(this->_lock);
bool ret_value;
if (_values.size() > 0)
{
//如果数据队列有数据,则可以直接读数据
auto val = std::move(_values.front());
_values.pop_front();
r_awaker->awake(this, 1, &val, future_error::none);
ret_value = true;
}
else
{
//否则,将“读等待”放入“读队列”
_read_awakes.push_back(r_awaker);
ret_value = false;
}
//如果已有写队列,则唤醒一个“写等待”
awake_one_writer_();
return ret_value;
}
void write_(channel_write_awaker_ptr && w_awaker, const _Ty& val)
{
assert(w_awaker);
scoped_lock<spinlock> lock_(this->_lock);
//如果满了,则不添加到数据队列,而是将“写等待”和值,放入“写队列”
bool is_full = _values.size() >= _max_counter;
if (is_full)
_write_awakes.push_back(std::make_pair(std::forward<channel_write_awaker_ptr>(w_awaker), val));
else
_values.push_back(val);
//如果已有读队列,则唤醒一个“读等待”
awake_one_reader_();
//触发 没有放入“写队列”的“写等待”
if (!is_full) w_awaker->awake(this, 1);
}
void write_(channel_write_awaker_ptr && w_awaker, _Ty&& val)
{
assert(w_awaker);
scoped_lock<spinlock> lock_(this->_lock);
//如果满了,则不添加到数据队列,而是将“写等待”和值,放入“写队列”
bool is_full = _values.size() >= _max_counter;
if (is_full)
_write_awakes.push_back(std::make_pair(std::forward<channel_write_awaker_ptr>(w_awaker), std::forward<_Ty>(val)));
else
_values.push_back(std::forward<_Ty>(val));
//如果已有读队列,则唤醒一个“读等待”
awake_one_reader_();
//触发 没有放入“写队列”的“写等待”
if(!is_full) w_awaker->awake(this, 1);
}
private:
void awake_one_reader_()
{
//assert(!(_read_awakes.size() >= 0 && _values.size() == 0));
for (auto iter = _read_awakes.begin(); iter != _read_awakes.end(); )
{
auto r_awaker = *iter;
iter = _read_awakes.erase(iter);
if (r_awaker->awake(this, 1, _values.size() ? &_values.front() : nullptr, future_error::read_before_write))
{
if(_values.size()) _values.pop_front();
//唤醒一个“读等待”后,尝试唤醒一个“写等待”,以处理“数据队列”满后的“写等待”
awake_one_writer_();
break;
}
}
}
void awake_one_writer_()
{
for (auto iter = _write_awakes.begin(); iter != _write_awakes.end(); )
{
auto w_awaker = std::move(*iter);
iter = _write_awakes.erase(iter);
if (w_awaker.first->awake(this, 1))
{
//一个“写等待”唤醒后,将“写等待”绑定的值,放入“数据队列”
_values.push_back(std::move(w_awaker.second));
break;
}
}
}
channel_impl(const channel_impl &) = delete;
channel_impl(channel_impl &&) = delete;
channel_impl & operator = (const channel_impl &) = delete;
channel_impl & operator = (channel_impl &&) = delete;
};
}
template<class _Ty>
struct channel_t
{
typedef detail::channel_impl<_Ty> channel_impl_type;
typedef typename channel_impl_type::channel_read_awaker channel_read_awaker;
typedef typename channel_impl_type::channel_write_awaker channel_write_awaker;
typedef std::shared_ptr<channel_impl_type> channel_impl_ptr;
typedef std::weak_ptr<channel_impl_type> channel_impl_wptr;
typedef std::chrono::system_clock clock_type;
private:
channel_impl_ptr _chan;
public:
channel_t(size_t max_counter = 0)
:_chan(std::make_shared<channel_impl_type>(max_counter))
{
}
awaitable_t<bool> write(_Ty&& val) const
{
awaitable_t<bool> awaitable;
auto awaker = std::make_shared<channel_write_awaker>(
[st = awaitable._state](channel_impl_type * chan) -> bool
{
st->set_value(chan ? true : false);
return true;
});
_chan->write_(std::move(awaker), std::forward<_Ty>(val));
return awaitable;
}
awaitable_t<bool> write(const _Ty& val) const
{
awaitable_t<bool> awaitable;
auto awaker = std::make_shared<channel_write_awaker>(
[st = awaitable._state](channel_impl_type * chan) -> bool
{
st->set_value(chan ? true : false);
return true;
});
_chan->write_(std::move(awaker), val);
return awaitable;
}
awaitable_t<_Ty> read() const
{
awaitable_t<_Ty> awaitable;
auto awaker = std::make_shared<channel_read_awaker>(
[st = awaitable._state](channel_impl_type *, _Ty * val, future_error fe) -> bool
{
if(val)
st->set_value(std::move(*val));
else
st->throw_exception(channel_exception{ fe });
return true;
});
_chan->read_(std::move(awaker));
return awaitable;
}
awaitable_t<bool> operator << (_Ty&& val) const
{
return std::move(write(std::forward<_Ty>(val)));
}
awaitable_t<bool> operator << (const _Ty& val) const
{
return std::move(write(val));
}
awaitable_t<_Ty> operator co_await () const
{
return read();
}
#if _DEBUG
const auto & debug_queue() const
{
return _chan->debug_queue();
}
#endif
channel_t(const channel_t &) = default;
channel_t(channel_t &&) = default;
channel_t & operator = (const channel_t &) = default;
channel_t & operator = (channel_t &&) = default;
};
typedef channel_t<bool> semaphore_t;
}

+ 107
- 0
librf/src/def.h View File

@@ -0,0 +1,107 @@
#pragma once
#include <yvals.h>
#include <atomic>
#include <memory>
#include <chrono>
#include <thread>
#include <list>
#include <vector>
#include <deque>
#include <mutex>
#include <functional>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <assert.h>
#include <experimental/coroutine>
//#include <experimental/generator>
//替代<experimental/generator>,因为VS2015/VS2017的generator<>未实现return_value,导致yield后不能return
#include "generator.h"
#if defined(RESUMEF_DLL_EXPORT)
# define RF_API __declspec(dllexport)
#elif defined(RESUMEF_DLL_IMPORT)
# define RF_API __declspec(dllimport)
#else
# define RF_API
#endif
#if RESUMEF_DEBUG_COUNTER
extern std::atomic<intptr_t> g_resumef_state_count;
extern std::atomic<intptr_t> g_resumef_task_count;
extern std::atomic<intptr_t> g_resumef_evtctx_count;
#endif
namespace resumef
{
#if _HAS_CXX17
template<class... _Mutexes>
using scoped_lock = std::scoped_lock<_Mutexes...>;
#else
template<class... _Mutexes>
using scoped_lock = std::lock_guard<_Mutexes...>;
#endif
enum struct future_error
{
none,
not_ready, // get_value called when value not available
already_acquired, // attempt to get another future
unlock_more, // unlock 次数多余lock次数
read_before_write, // 0容量的channel,先读后写
max__
};
const char * get_error_string(future_error fe, const char * classname);
//const char * future_error_string[size_t(future_error::max__)];
struct future_exception : std::exception
{
future_error _error;
future_exception(future_error fe)
: exception(get_error_string(fe, "future_exception"))
, _error(fe)
{
}
};
struct lock_exception : std::exception
{
future_error _error;
lock_exception(future_error fe)
: exception(get_error_string(fe, "lock_exception"))
, _error(fe)
{
}
};
struct channel_exception : std::exception
{
future_error _error;
channel_exception(future_error fe)
: exception(get_error_string(fe, "channel_exception"))
, _error(fe)
{
}
};
struct scheduler;
struct state_base;
//获得当前线程下的调度器
extern scheduler * this_scheduler();
//获得当前线程下,正在由调度器调度的协程
//extern state_base * this_coroutine();
//namespace detail
//{
// extern state_base * current_coroutine();
//}
}
#define co_yield_void co_yield nullptr
#define co_return_void co_return nullptr

+ 356
- 0
librf/src/event.cpp View File

@@ -0,0 +1,356 @@
#include "event.h"
#include <assert.h>
#include <mutex>
#include "scheduler.h"
namespace resumef
{
namespace detail
{
event_impl::event_impl(intptr_t initial_counter_)
: _counter(initial_counter_)
{
}
void event_impl::signal()
{
scoped_lock<spinlock> lock_(this->_lock);
++this->_counter;
for (auto iter = this->_awakes.begin(); iter != this->_awakes.end(); )
{
auto awaker = *iter;
iter = this->_awakes.erase(iter);
if (awaker->awake(this, 1))
{
if (--this->_counter == 0)
break;
}
}
}
void event_impl::reset()
{
scoped_lock<spinlock> lock_(this->_lock);
this->_awakes.clear();
this->_counter = 0;
}
bool event_impl::wait_(const event_awaker_ptr & awaker)
{
assert(awaker);
scoped_lock<spinlock> lock_(this->_lock);
if (this->_counter > 0)
{
if(awaker->awake(this, 1))
{
--this->_counter;
return true;
}
}
else
{
this->_awakes.push_back(awaker);
}
return false;
}
}
event_t::event_t(intptr_t initial_counter_)
: _event(std::make_shared<detail::event_impl>(initial_counter_))
{
}
awaitable_t<bool> event_t::wait() const
{
awaitable_t<bool> awaitable;
auto awaker = std::make_shared<detail::event_awaker>(
[st = awaitable._state](detail::event_impl * e) -> bool
{
st->set_value(e ? true : false);
return true;
});
_event->wait_(awaker);
return awaitable;
}
awaitable_t<bool> event_t::wait_until_(const clock_type::time_point & tp) const
{
awaitable_t<bool> awaitable;
auto awaker = std::make_shared<detail::event_awaker>(
[st = awaitable._state](detail::event_impl * e) -> bool
{
st->set_value(e ? true : false);
return true;
});
_event->wait_(awaker);
g_scheduler.timer()->add(tp,
[awaker](bool bValue)
{
awaker->awake(nullptr, 1);
});
return awaitable;
}
struct wait_any_awaker
{
typedef state_t<intptr_t> state_type;
counted_ptr<state_type> st;
std::vector<detail::event_impl *> evts;
wait_any_awaker(const counted_ptr<state_type> & st_, std::vector<detail::event_impl *> && evts_)
: st(st_)
, evts(std::forward<std::vector<detail::event_impl *>>(evts_))
{}
wait_any_awaker(const wait_any_awaker &) = delete;
wait_any_awaker(wait_any_awaker &&) = default;
bool operator()(detail::event_impl * e) const
{
if (e)
{
for (auto i = evts.begin(); i != evts.end(); ++i)
{
if (e == (*i))
{
st->set_value((intptr_t)(i - evts.begin()));
return true;
}
}
}
else
{
st->set_value(-1);
}
return false;
}
};
awaitable_t<intptr_t> event_t::wait_any_(std::vector<event_impl_ptr> && evts)
{
awaitable_t<intptr_t> awaitable;
if (evts.size() <= 0)
{
awaitable._state->set_value(-1);
return awaitable;
}
auto awaker = std::make_shared<detail::event_awaker>(
[st = awaitable._state, evts](detail::event_impl * e) -> bool
{
if (e)
{
for (auto i = evts.begin(); i != evts.end(); ++i)
{
if (e == (*i).get())
{
st->set_value((intptr_t)(i - evts.begin()));
return true;
}
}
}
else
{
st->set_value(-1);
}
return false;
});
for (auto e : evts)
{
e->wait_(awaker);
}
return awaitable;
}
awaitable_t<intptr_t> event_t::wait_any_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts)
{
awaitable_t<intptr_t> awaitable;
auto awaker = std::make_shared<detail::event_awaker>(
[st = awaitable._state, evts](detail::event_impl * e) -> bool
{
if (e)
{
for (auto i = evts.begin(); i != evts.end(); ++i)
{
if (e == (*i).get())
{
st->set_value((intptr_t)(i - evts.begin()));
return true;
}
}
}
else
{
st->set_value(-1);
}
return false;
});
for (auto e : evts)
{
e->wait_(awaker);
}
g_scheduler.timer()->add(tp,
[awaker](bool bValue)
{
awaker->awake(nullptr, 1);
});
return awaitable;
}
awaitable_t<bool> event_t::wait_all_(std::vector<event_impl_ptr> && evts)
{
awaitable_t<bool> awaitable;
if (evts.size() <= 0)
{
awaitable._state->set_value(false);
return awaitable;
}
auto awaker = std::make_shared<detail::event_awaker>(
[st = awaitable._state](detail::event_impl * e) -> bool
{
st->set_value(e ? true : false);
return true;
},
evts.size());
for (auto e : evts)
{
e->wait_(awaker);
}
return awaitable;
}
struct event_t::wait_all_ctx
{
counted_ptr<state_t<bool>> st;
std::vector<event_impl_ptr> evts;
std::vector<event_impl_ptr> evts_waited;
timer_handler th;
spinlock _lock;
wait_all_ctx()
{
#if RESUMEF_DEBUG_COUNTER
++g_resumef_evtctx_count;
#endif
}
~wait_all_ctx()
{
th.stop();
#if RESUMEF_DEBUG_COUNTER
--g_resumef_evtctx_count;
#endif
}
bool awake(detail::event_impl * eptr)
{
scoped_lock<spinlock> lock_(this->_lock);
//如果st为nullptr,则说明之前已经返回过值了。本环境无效了。
if (!st.get())
return false;
if (eptr)
{
//记录已经等到的事件
evts_waited.emplace_back(eptr->shared_from_this());
//已经等到的事件达到预期
if (evts_waited.size() == evts.size())
{
evts_waited.clear();
//返回true表示等待成功
st->set_value(true);
//丢弃st,以便于还有其它持有的ctx返回false
st.reset();
//取消定时器
th.stop();
}
}
else
{
//超时后,恢复已经等待的事件计数
for (auto sptr : evts_waited)
{
sptr->signal();
}
evts_waited.clear();
//返回true表示等待失败
st->set_value(false);
//丢弃st,以便于还有其它持有的ctx返回false
st.reset();
//定时器句柄已经无意义了
th.reset();
}
return true;
}
};
//等待所有的事件
//超时后的行为应该表现为:
//要么所有的事件计数减一,要么所有事件计数不动
//则需要超时后,恢复已经等待的事件计数
awaitable_t<bool> event_t::wait_all_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts)
{
awaitable_t<bool> awaitable;
if (evts.size() <= 0)
{
g_scheduler.timer()->add_handler(tp,
[st = awaitable._state](bool bValue)
{
st->set_value(false);
});
return awaitable;
}
auto ctx = std::make_shared<wait_all_ctx>();
ctx->st = awaitable._state;
ctx->evts_waited.reserve(evts.size());
ctx->evts = std::move(evts);
ctx->th = std::move(g_scheduler.timer()->add_handler(tp,
[ctx](bool bValue)
{
ctx->awake(nullptr);
}));
for (auto e : ctx->evts)
{
auto awaker = std::make_shared<detail::event_awaker>(
[ctx](detail::event_impl * eptr) -> bool
{
return ctx->awake(eptr);
});
e->wait_(awaker);
}
return awaitable;
}
}

+ 207
- 0
librf/src/event.h View File

@@ -0,0 +1,207 @@
#pragma once
#include "_awaker.h"
namespace resumef
{
namespace detail
{
struct event_impl;
typedef _awaker<event_impl> event_awaker;
typedef std::shared_ptr<event_awaker> event_awaker_ptr;
struct event_impl : public std::enable_shared_from_this<event_impl>
{
private:
std::list<event_awaker_ptr> _awakes;
intptr_t _counter;
spinlock _lock;
public:
RF_API event_impl(intptr_t initial_counter_);
RF_API void signal();
RF_API void reset();
//如果已经触发了awaker,则返回true
RF_API bool wait_(const event_awaker_ptr & awaker);
template<class callee_t, class dummy_t = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, event_awaker_ptr>::value>>
auto wait(callee_t && awaker, dummy_t * dummy_ = nullptr)
{
return wait_(std::make_shared<event_awaker>(std::forward<callee_t>(awaker)));
}
event_impl(const event_impl &) = delete;
event_impl(event_impl &&) = delete;
event_impl & operator = (const event_impl &) = delete;
event_impl & operator = (event_impl &&) = delete;
};
}
struct event_t
{
typedef std::shared_ptr<detail::event_impl> event_impl_ptr;
typedef std::weak_ptr<detail::event_impl> event_impl_wptr;
typedef std::chrono::system_clock clock_type;
private:
event_impl_ptr _event;
struct wait_all_ctx;
public:
RF_API event_t(intptr_t initial_counter_ = 0);
void signal() const
{
_event->signal();
}
void reset() const
{
_event->reset();
}
RF_API awaitable_t<bool>
wait() const;
template<class _Rep, class _Period>
awaitable_t<bool>
wait_for(const std::chrono::duration<_Rep, _Period> & dt) const
{
return wait_for_(std::chrono::duration_cast<clock_type::duration>(dt));
}
template<class _Clock, class _Duration>
awaitable_t<bool>
wait_until(const std::chrono::time_point<_Clock, _Duration> & tp) const
{
return wait_until_(std::chrono::time_point_cast<clock_type::duration>(tp));
}
template<class _Iter>
static awaitable_t<intptr_t>
wait_any(_Iter begin_, _Iter end_)
{
return wait_any_(make_event_vector(begin_, end_));
}
template<class _Cont>
static awaitable_t<intptr_t>
wait_any(const _Cont & cnt_)
{
return wait_any_(make_event_vector(std::begin(cnt_), std::end(cnt_)));
}
template<class _Rep, class _Period, class _Iter>
static awaitable_t<intptr_t>
wait_any_for(const std::chrono::duration<_Rep, _Period> & dt, _Iter begin_, _Iter end_)
{
return wait_any_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(begin_, end_));
}
template<class _Rep, class _Period, class _Cont>
static awaitable_t<intptr_t>
wait_any_for(const std::chrono::duration<_Rep, _Period> & dt, const _Cont & cnt_)
{
return wait_any_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(std::begin(cnt_), std::end(cnt_)));
}
template<class _Clock, class _Duration, class _Iter>
static awaitable_t<intptr_t>
wait_any_until(const std::chrono::time_point<_Clock, _Duration> & tp, _Iter begin_, _Iter end_)
{
return wait_any_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(begin_, end_));
}
template<class _Clock, class _Duration, class _Cont>
static awaitable_t<intptr_t>
wait_any_until(const std::chrono::time_point<_Clock, _Duration> & tp, const _Cont & cnt_)
{
return wait_any_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(std::begin(cnt_), std::end(cnt_)));
}
template<class _Iter>
static awaitable_t<bool>
wait_all(_Iter begin_, _Iter end_)
{
return wait_all_(make_event_vector(begin_, end_));
}
template<class _Cont>
static awaitable_t<bool>
wait_all(const _Cont & cnt_)
{
return wait_all(std::begin(cnt_), std::end(cnt_));
}
template<class _Rep, class _Period, class _Iter>
static awaitable_t<bool>
wait_all_for(const std::chrono::duration<_Rep, _Period> & dt, _Iter begin_, _Iter end_)
{
return wait_all_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(begin_, end_));
}
template<class _Rep, class _Period, class _Cont>
static awaitable_t<bool>
wait_all_for(const std::chrono::duration<_Rep, _Period> & dt, const _Cont & cnt_)
{
return wait_all_for_(std::chrono::duration_cast<clock_type::duration>(dt), make_event_vector(std::begin(cnt_), std::end(cnt_)));
}
template<class _Clock, class _Duration, class _Iter>
static awaitable_t<bool>
wait_all_until(const std::chrono::time_point<_Clock, _Duration> & tp, _Iter begin_, _Iter end_)
{
return wait_all_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(begin_, end_));
}
template<class _Clock, class _Duration, class _Cont>
static awaitable_t<bool>
wait_all_until(const std::chrono::time_point<_Clock, _Duration> & tp, const _Cont & cnt_)
{
return wait_all_until_(std::chrono::time_point_cast<clock_type::duration>(tp), make_event_vector(std::begin(cnt_), std::end(cnt_)));
}
RF_API event_t(const event_t &) = default;
RF_API event_t(event_t &&) = default;
RF_API event_t & operator = (const event_t &) = default;
RF_API event_t & operator = (event_t &&) = default;
private:
template<class _Iter>
static std::vector<event_impl_ptr> make_event_vector(_Iter begin_, _Iter end_)
{
std::vector<event_impl_ptr> evts;
evts.reserve(std::distance(begin_, end_));
for (auto i = begin_; i != end_; ++i)
evts.push_back((*i)._event);
return std::move(evts);
}
inline awaitable_t<bool> wait_for_(const clock_type::duration & dt) const
{
return wait_until_(clock_type::now() + dt);
}
RF_API awaitable_t<bool> wait_until_(const clock_type::time_point & tp) const;
RF_API static awaitable_t<intptr_t> wait_any_(std::vector<event_impl_ptr> && evts);
inline static awaitable_t<intptr_t> wait_any_for_(const clock_type::duration & dt, std::vector<event_impl_ptr> && evts)
{
return wait_any_until_(clock_type::now() + dt, std::forward<std::vector<event_impl_ptr>>(evts));
}
RF_API static awaitable_t<intptr_t> wait_any_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts);
RF_API static awaitable_t<bool> wait_all_(std::vector<event_impl_ptr> && evts);
inline static awaitable_t<bool> wait_all_for_(const clock_type::duration & dt, std::vector<event_impl_ptr> && evts)
{
return wait_all_until_(clock_type::now() + dt, std::forward<std::vector<event_impl_ptr>>(evts));
}
RF_API static awaitable_t<bool> wait_all_until_(const clock_type::time_point & tp, std::vector<event_impl_ptr> && evts);
};
}

+ 345
- 0
librf/src/future.h View File

@@ -0,0 +1,345 @@

#pragma once
#include "state.h"
namespace resumef
{
template <typename T = void>
struct promise_t;
template <typename T>
struct future_impl_t
{
typedef promise_t<T> promise_type;
typedef state_t<T> state_type;
counted_ptr<state_type> _state;
future_impl_t(const counted_ptr<state_type>& state) : _state(state)
{
_state->_future_acquired = true;
}
// movable, but not copyable
future_impl_t() = default;
future_impl_t(future_impl_t&& f) = default;
future_impl_t & operator = (future_impl_t&& f) = default;
future_impl_t(const future_impl_t&) = delete;
future_impl_t & operator = (const future_impl_t&) = delete;
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
bool await_ready()
{
return _state->_ready;
}
void await_suspend(std::experimental::coroutine_handle<> resume_cb)
{
_state->await_suspend(resume_cb);
}
//return_value, return_void只能由派生类去实现
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
//if ready, can get value
bool ready()
{
return _state->_ready;
}
auto & get_value()
{
return _state->get_value();
}
bool is_suspend() const
{
return _state->is_suspend();
}
bool cancellation_requested() const
{
return _state->cancellation_requested();
}
void set_exception(std::exception_ptr e_)
{
_state->set_exception(std::move(e_));
}
void cancel()
{
_state->cancel();
}
};
template <typename T = void>
struct future_t : public future_impl_t<T>
{
future_t(const counted_ptr<state_type>& state)
: future_impl_t<T>(state)
{
}
// movable, but not copyable
future_t(const future_t&) = delete;
future_t(future_t&& f) = default;
future_t() = default;
future_t & operator = (const future_t&) = delete;
future_t & operator = (future_t&& f) = default;
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
T await_resume()
{
_state->rethrow_if_exception();
return std::move(_state->get_value());
}
void return_value(const T& val)
{
_state->set_value(val);
}
void return_value(T&& val)
{
_state->set_value(std::forward<T>(val));
}
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
template <>
struct future_t<void> : public future_impl_t<void>
{
future_t(const counted_ptr<state_type>& state)
: future_impl_t<void>(state)
{
}
// movable, but not copyable
future_t(const future_t&) = delete;
future_t(future_t&& f) = default;
future_t() = default;
future_t & operator = (const future_t&) = delete;
future_t & operator = (future_t&& f) = default;
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
void await_resume()
{
_state->rethrow_if_exception();
return _state->get_value();
}
void return_void()
{
_state->set_value();
}
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
using future_vt = future_t<void>;
template <typename T>
struct promise_impl_t
{
typedef future_t<T> future_type;
typedef state_t<T> state_type;
counted_ptr<state_type> _state;
// movable not copyable
promise_impl_t()
: _state(make_counted<state_type>())
{
}
promise_impl_t(promise_impl_t&&) = default;
promise_impl_t & operator = (promise_impl_t&&) = default;
promise_impl_t(const promise_impl_t&) = delete;
promise_impl_t & operator = (const promise_impl_t&) = delete;
//这个函数,用于callback里,返回关联的future对象
//callback里,不应该调用 get_return_object()
future_type get_future()
{
if (_state->_future_acquired)
throw future_exception{ future_error::already_acquired };
return future_type(_state);
}
// Most functions don't need this but timers and reads from streams
// cause multiple callbacks.
future_type next_future()
{
// reset and return another future
if (_state->_future_acquired)
_state->reset();
return future_type(_state);
}
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
//如果在协程外启动一个resumable function,则:
// 1、即将交给调度器调度
// 2、手工获取结果(将来支持)
// 无论哪种情况,都返回未准备好。则编译器调用await_suspend()获得继续运行的resume_cb
//如果在协程内启动一个resumable function,则:
// 1、即将交给调度器调度
// 2、通过await启动另外一个子函数
// (1)情况下,无法区分是否已经拥有的resume_cb,可以特殊处理
// (2)情况下,返回准备好了,让编译器继续运行
std::experimental::suspend_never initial_suspend() noexcept
{
return {};
/*
struct AWaitor
{
counted_ptr<state_tt> _state;
bool await_ready() _NOEXCEPT
{
return false;
}
void await_suspend(std::experimental::coroutine_handle<> resume_cb) _NOEXCEPT
{
_state->await_suspend(resume_cb);
_state->run_in_coroutine(this_coroutine());
}
void await_resume() _NOEXCEPT
{
}
};
return AWaitor{ _state };
*/
}
//这在一个协程被销毁之时调用。
//我们选择不挂起协程,只是通知state的对象,本协程已经准备好了删除了
std::experimental::suspend_never final_suspend() noexcept
{
_state->final_suspend();
return{};
}
//返回与之关联的future对象
future_type get_return_object()
{
return future_type(_state);
}
void set_exception(std::exception_ptr e_)
{
_state->set_exception(std::move(e_));
}
bool cancellation_requested()
{
return _state->cancellation_requested();
}
//return_value/return_void 只能由派生类去实现
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
template <typename T>
struct promise_t : public promise_impl_t<T>
{
typedef promise_t<T> promise_type;
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
void return_value(const T& val)
{
_state->set_value(val);
}
void return_value(T&& val)
{
_state->set_value(std::forward<T>(val));
}
void yield_value(const T& val)
{
_state->set_value(val);
}
void yield_value(T&& val)
{
_state->set_value(std::forward<T>(val));
}
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
template <>
struct promise_t<void> : public promise_impl_t<void>
{
typedef promise_t<void> promise_type;
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
void return_void()
{
_state->set_value();
}
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
using promise_vt = promise_t<void>;
template <typename T = void>
struct awaitable_t
{
typedef state_t<T> state_type;
counted_ptr<state_type> _state;
// movable not copyable
awaitable_t()
: _state(make_counted<state_type>())
{
}
// movable, but not copyable
awaitable_t(const awaitable_t&) = delete;
awaitable_t(awaitable_t&& f) = default;
awaitable_t & operator = (const awaitable_t&) = delete;
awaitable_t & operator = (awaitable_t&& f) = default;
//------------------------------------------------------------------------------------------
//以下是与编译器生成的resumable function交互的接口
bool await_ready()
{
return _state->_ready;
}
void await_suspend(std::experimental::coroutine_handle<> resume_cb)
{
_state->await_suspend(resume_cb);
}
T await_resume()
{
_state->rethrow_if_exception();
return _state->get_value();
}
//以上是与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
using awaitable_vt = awaitable_t<void>;
}

+ 239
- 0
librf/src/generator.h View File

@@ -0,0 +1,239 @@
/***
*generator
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* Purpose: Library support of coroutines. generator class
* http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0057r0.pdf
*
* [Public]
*
****/
#pragma once
#ifndef _EXPERIMENTAL_GENERATOR_
#define _EXPERIMENTAL_GENERATOR_
#ifndef RC_INVOKED
#ifndef _RESUMABLE_FUNCTIONS_SUPPORTED
#error <experimental/generator> requires /await compiler option
#endif /* _RESUMABLE_FUNCTIONS_SUPPORTED */
#include <experimental/resumable>
#pragma pack(push, _CRT_PACKING)
#pragma push_macro("new")
#undef new
_STD_BEGIN
namespace experimental {
template <typename _Ty, typename promise_type>
struct generator_iterator;
template<typename promise_type>
struct generator_iterator<void, promise_type>
{
typedef _STD input_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
coroutine_handle<promise_type> _Coro;
generator_iterator(nullptr_t) : _Coro(nullptr)
{
}
generator_iterator(coroutine_handle<promise_type> _CoroArg) : _Coro(_CoroArg)
{
}
generator_iterator &operator++()
{
if (_Coro.done())
_Coro = nullptr;
else
_Coro.resume();
return *this;
}
generator_iterator operator++(int) = delete;
// generator iterator current_value
// is a reference to a temporary on the coroutine frame
// implementing postincrement will require storing a copy
// of the value in the iterator.
//{
// auto _Result = *this;
// ++(*this);
// return _Result;
//}
bool operator==(generator_iterator const &right_) const
{
return _Coro == right_._Coro;
}
bool operator!=(generator_iterator const &right_) const
{
return !(*this == right_);
}
};
template <typename promise_type>
struct generator_iterator<nullptr_t, promise_type> : public generator_iterator<void, promise_type>
{
generator_iterator(nullptr_t) : generator_iterator<void, promise_type>(nullptr)
{
}
generator_iterator(coroutine_handle<promise_type> _CoroArg) : generator_iterator<void, promise_type>(_CoroArg)
{
}
};
template <typename _Ty, typename promise_type>
struct generator_iterator : public generator_iterator<void, promise_type>
{
generator_iterator(nullptr_t) : generator_iterator<void, promise_type>(nullptr)
{
}
generator_iterator(coroutine_handle<promise_type> _CoroArg) : generator_iterator<void, promise_type>(_CoroArg)
{
}
_Ty const &operator*() const
{
return *_Coro.promise()._CurrentValue;
}
_Ty const *operator->() const
{
return _STD addressof(operator*());
}
};
template <typename _Ty, typename _Alloc = allocator<char>>
struct generator
{
struct promise_type
{
_Ty const *_CurrentValue;
promise_type &get_return_object()
{
return *this;
}
bool initial_suspend()
{
return (true);
}
bool final_suspend()
{
return (true);
}
void yield_value(_Ty const &_Value)
{
_CurrentValue = _STD addressof(_Value);
}
template<class = _STD enable_if_t<!is_same_v<_Ty, void>, _Ty>>
void return_value(_Ty const &_Value)
{
_CurrentValue = _STD addressof(_Value);
}
template<class = _STD enable_if_t<is_same_v<_Ty, void>, _Ty>>
void return_value()
{
_CurrentValue = nullptr;
}
template <typename _Uty>
_Uty && await_transform(_Uty &&_Whatever)
{
static_assert(_Always_false<_Uty>::value,
"co_await is not supported in coroutines of type std::experiemental::generator");
return _STD forward<_Uty>(_Whatever);
}
using _Alloc_traits = allocator_traits<_Alloc>;
using _Alloc_of_char_type =
typename _Alloc_traits::template rebind_alloc<char>;
void *operator new(size_t _Size)
{
_Alloc_of_char_type _Al;
return _Al.allocate(_Size);
}
void operator delete(void *_Ptr, size_t _Size)_NOEXCEPT
{
_Alloc_of_char_type _Al;
return _Al.deallocate(static_cast<char *>(_Ptr), _Size);
}
};
typedef generator_iterator<_Ty, promise_type> iterator;
iterator begin()
{
if (_Coro)
{
if (_Coro.done())
return{ nullptr };
_Coro.resume();
}
return { _Coro };
}
iterator end()
{
return{ nullptr };
}
explicit generator(promise_type &_Prom)
: _Coro(coroutine_handle<promise_type>::from_promise(
_Prom))
{
}
generator() = default;
generator(generator const &) = delete;
generator &operator=(generator const &) = delete;
generator(generator &&right_) : _Coro(right_._Coro)
{
right_._Coro = nullptr;
}
generator &operator=(generator &&right_)
{
if (&right_ != this) {
_Coro = right_._Coro;
right_._Coro = nullptr;
}
return *this;
}
~generator()
{
if (_Coro) {
_Coro.destroy();
}
}
private:
coroutine_handle<promise_type> _Coro = nullptr;
};
} // namespace experimental
_STD_END
#pragma pop_macro("new")
#pragma pack(pop)
#endif /* RC_INVOKED */
#endif /* _EXPERIMENTAL_GENERATOR_ */

+ 126
- 0
librf/src/mutex.cpp View File

@@ -0,0 +1,126 @@
#include "mutex.h"
#include <assert.h>
#include "scheduler.h"
namespace resumef
{
namespace detail
{
mutex_impl::mutex_impl()
{
}
void mutex_impl::unlock()
{
scoped_lock<spinlock> lock_(this->_lock);
if (_owner != nullptr)
{
for (auto iter = _awakes.begin(); iter != _awakes.end(); )
{
auto awaker = *iter;
iter = _awakes.erase(iter);
if (awaker->awake(this, 1))
{
_owner = awaker;
break;
}
}
if (_awakes.size() == 0)
{
_owner = nullptr;
}
}
}
bool mutex_impl::lock_(const mutex_awaker_ptr & awaker)
{
assert(awaker);
scoped_lock<spinlock> lock_(this->_lock);
if (_owner == nullptr)
{
_owner = awaker;
awaker->awake(this, 1);
return true;
}
else
{
_awakes.push_back(awaker);
return false;
}
}
bool mutex_impl::try_lock_(const mutex_awaker_ptr & awaker)
{
assert(awaker);
scoped_lock<spinlock> lock_(this->_lock);
if (_owner == nullptr)
{
_owner = awaker;
return true;
}
else
{
return false;
}
}
}
mutex_t::mutex_t()
: _locker(std::make_shared<detail::mutex_impl>())
{
}
awaitable_t<bool> mutex_t::lock() const
{
awaitable_t<bool> awaitable;
auto awaker = std::make_shared<detail::mutex_awaker>(
[st = awaitable._state](detail::mutex_impl * e) -> bool
{
st->set_value(e ? true : false);
return true;
});
_locker->lock_(awaker);
return awaitable;
}
bool mutex_t::try_lock() const
{
auto dummy_awaker = std::make_shared<detail::mutex_awaker>(
[](detail::mutex_impl * ) -> bool
{
return true;
});
return _locker->try_lock_(dummy_awaker);
}
awaitable_t<bool> mutex_t::try_lock_until_(const clock_type::time_point & tp) const
{
awaitable_t<bool> awaitable;
auto awaker = std::make_shared<detail::mutex_awaker>(
[st = awaitable._state](detail::mutex_impl * e) -> bool
{
st->set_value(e ? true : false);
return true;
});
_locker->lock_(awaker);
g_scheduler.timer()->add(tp,
[awaker](bool bValue)
{
awaker->awake(nullptr, 1);
});
return awaitable;
}
}

+ 92
- 0
librf/src/mutex.h View File

@@ -0,0 +1,92 @@
#pragma once
#include "_awaker.h"
namespace resumef
{
namespace detail
{
struct mutex_impl;
typedef _awaker<mutex_impl> mutex_awaker;
typedef std::shared_ptr<mutex_awaker> mutex_awaker_ptr;
struct mutex_impl : public std::enable_shared_from_this<mutex_impl>
{
private:
std::list<mutex_awaker_ptr> _awakes;
mutex_awaker_ptr _owner;
spinlock _lock;
public:
RF_API mutex_impl();
//如果已经触发了awaker,则返回true
RF_API bool lock_(const mutex_awaker_ptr & awaker);
RF_API bool try_lock_(const mutex_awaker_ptr & awaker);
RF_API void unlock();
template<class callee_t, class dummy_t = std::enable_if<!std::is_same<std::remove_cv_t<callee_t>, mutex_awaker_ptr>::value>>
auto lock(callee_t && awaker, dummy_t * dummy_ = nullptr)
{
return lock_(std::make_shared<mutex_awaker>(std::forward<callee_t>(awaker)));
}
private:
mutex_impl(const mutex_impl &) = delete;
mutex_impl(mutex_impl &&) = delete;
mutex_impl & operator = (const mutex_impl &) = delete;
mutex_impl & operator = (mutex_impl &&) = delete;
};
}
struct mutex_t
{
typedef std::shared_ptr<detail::mutex_impl> lock_impl_ptr;
typedef std::weak_ptr<detail::mutex_impl> lock_impl_wptr;
typedef std::chrono::system_clock clock_type;
private:
lock_impl_ptr _locker;
public:
RF_API mutex_t();
void unlock() const
{
_locker->unlock();
}
RF_API awaitable_t<bool>
lock() const;
RF_API bool
try_lock() const;
/*
template<class _Rep, class _Period>
awaitable_t<bool>
try_lock_for(const std::chrono::duration<_Rep, _Period> & dt) const
{
return try_lock_for_(std::chrono::duration_cast<clock_type::duration>(dt));
}
template<class _Clock, class _Duration>
awaitable_t<bool>
try_lock_until(const std::chrono::time_point<_Clock, _Duration> & tp) const
{
return try_lock_until_(std::chrono::time_point_cast<clock_type::duration>(tp));
}
*/
RF_API mutex_t(const mutex_t &) = default;
RF_API mutex_t(mutex_t &&) = default;
RF_API mutex_t & operator = (const mutex_t &) = default;
RF_API mutex_t & operator = (mutex_t &&) = default;
private:
inline awaitable_t<bool> try_lock_for_(const clock_type::duration & dt) const
{
return try_lock_until_(clock_type::now() + dt);
}
RF_API awaitable_t<bool> try_lock_until_(const clock_type::time_point & tp) const;
};
#define resumf_guard_lock(lker) (lker).lock(); resumef::scoped_lock<resumef::mutex_t> __resumf_guard##lker##__((lker), std::adopt_lock)
}

+ 78
- 0
librf/src/rf_task.cpp View File

@@ -0,0 +1,78 @@

#include "rf_task.h"
#include "scheduler.h"
#include <assert.h>
namespace resumef
{
task_base::task_base()
{
#if RESUMEF_DEBUG_COUNTER
++g_resumef_task_count;
#endif
}
task_base::~task_base()
{
#if RESUMEF_DEBUG_COUNTER
--g_resumef_task_count;
#endif
}
template<class state_tt>
struct awaitable_task_t : public task_base
{
counted_ptr<state_tt> _state;
awaitable_task_t() {}
awaitable_task_t(state_tt * state)
: _state(state)
{
}
virtual bool is_suspend() override
{
return !_state->ready();
}
virtual bool go_next(scheduler * schdler) override
{
_state->resume();
return false;
}
virtual void cancel() override
{
_state->cancel();
}
virtual void * get_id() override
{
return _state.get();
}
};
state_base::~state_base()
{
#if RESUMEF_DEBUG_COUNTER
--g_resumef_state_count;
#endif
}
void state_base::set_value_none_lock()
{
// Set all members first as calling coroutine may reset stuff here.
_ready = true;

this_scheduler()->push_task_internal(new awaitable_task_t<state_base>(this));
}
void state_base::set_exception(std::exception_ptr && e_)
{
scoped_lock<std::mutex> __guard(_mtx);
_exception = std::move(e_);
// Set all members first as calling coroutine may reset stuff here.
_ready = true;

this_scheduler()->push_task_internal(new awaitable_task_t<state_base>(this));
}
}

+ 146
- 0
librf/src/rf_task.h View File

@@ -0,0 +1,146 @@
#pragma once
#include "def.h"
#include "future.h"
namespace resumef
{
struct task_base;
struct task_base
{
RF_API task_base();
RF_API virtual ~task_base();
//如果返回true,则不会调用go_next()
virtual bool is_suspend() = 0;
//返回true,表示任务还未完成,后续还需要继续执行
//否则,任务从调度器里删除
virtual bool go_next(scheduler *) = 0;
virtual void cancel() = 0;
virtual void * get_id() = 0;
};
//----------------------------------------------------------------------------------------------
template<class _Ty>
struct task_t;
//co_task接受的是一个experimental::generator<_Ty>类型,是调用一个支持异步的函数后返回的结果
template<class _Ty>
struct task_t<std::experimental::generator<_Ty> > : public task_base
{
typedef std::experimental::generator<_Ty> future_type;
typedef typename future_type::iterator iterator_type;
future_type _future;
iterator_type _iterator;
bool _ready;
task_t()
: _iterator(nullptr)
, _ready(false)
{
}
task_t(future_type && f)
: _future(std::forward<future_type>(f))
, _iterator(nullptr)
, _ready(false)
{
}
virtual bool is_suspend() override
{
return false;
}
virtual bool go_next(scheduler *) override
{
if (!this->_ready)
{
this->_iterator = this->_future.begin();
this->_ready = true;
}
if (this->_iterator != this->_future.end())
{
return (++this->_iterator) != this->_future.end();
}
return false;
}
virtual void cancel() override
{
}
virtual void * get_id() override
{
return nullptr;
}
};
template<class _Ty>
struct task_t<future_t<_Ty> > : public task_base
{
typedef future_t<_Ty> future_type;
future_type _future;
task_t() = default;
task_t(future_type && f)
: _future(std::forward<future_type>(f))
{
}
//如果返回true,则不会调用go_next()
//
//如果第一次运行,则state应该有:
// _coro != nullptr
// _ready == false
//运行一次后,则state应该是:
// _coro == nullptr
// _ready == false
//最后一次运行,则state应该是:
// _coro == nullptr
// _ready == true
virtual bool is_suspend() override
{
auto * _state = _future._state.get();
return _state->is_suspend();
}
//返回true,表示任务还未完成,后续还需要继续执行
//否则,任务从调度器里删除
virtual bool go_next(scheduler * schdler) override
{
auto * _state = _future._state.get();
_state->resume();
return !_state->ready() && !_state->_done;
}
virtual void cancel() override
{
_future.cancel();
}
virtual void * get_id() override
{
return _future._state.get();
}
};
//----------------------------------------------------------------------------------------------
//co_task_with_ctx接受的是一个'函数对象'
//这个'函数对象'被调用后,返回generator<_Ty>/future_t<_Ty>类型
//然后'函数对象'作为异步执行的上下文状态保存起来
template<class _Ctx>
struct ctx_task_t : public task_t<typename std::result_of<_Ctx()>::type>
{
typedef _Ctx context_type;
context_type _context;
ctx_task_t(context_type && ctx)
: _context(std::forward<context_type>(ctx))
{
_future = std::move(_context());
}
};
}

+ 175
- 0
librf/src/scheduler.cpp View File

@@ -0,0 +1,175 @@
#include "scheduler.h"
#include <assert.h>
#if RESUMEF_DEBUG_COUNTER
std::atomic<intptr_t> g_resumef_state_count = 0;
std::atomic<intptr_t> g_resumef_task_count = 0;
std::atomic<intptr_t> g_resumef_evtctx_count = 0;
#endif
namespace resumef
{
static const char * future_error_string[(size_t)future_error::max__]
{
"none",
"not_ready",
"already_acquired",
"unlock_more",
"read_before_write",
};
static char sz_future_error_buffer[256];
const char * get_error_string(future_error fe, const char * classname)
{
if (classname)
{
sprintf_s(sz_future_error_buffer, "%s, code=%s", classname, future_error_string[(size_t)(fe)]);
return sz_future_error_buffer;
}
return future_error_string[(size_t)(fe)];
}
//获得当前线程下的调度器
scheduler * this_scheduler()
{
return &g_scheduler;
}
//获得当前线程下,正在由调度器调度的协程
/*
namespace detail
{
state_base * current_coroutine()
{
scheduler * schdler = this_scheduler();
if (schdler->current_state)
return schdler->current_state;
return schdler->top_state();
}
}
*/
scheduler::scheduler()
: _task()
, _ready_task()
, _timer(std::make_shared<timer_manager>())
{
}
scheduler::~scheduler()
{
cancel_all_task_();
}
scheduler::scheduler(scheduler && right_)
{
this->swap(right_);
}
scheduler & scheduler::operator = (scheduler && right_)
{
this->swap(right_);
return *this;
}
void scheduler::new_task(task_base * task)
{
if (task)
{
scoped_lock<std::recursive_mutex> __guard(_mtx_ready);
this->_ready_task.push_back(task);
}
}
void scheduler::cancel_all_task_()
{
{
scoped_lock<std::recursive_mutex> __guard(_mtx_ready);
for (auto task : this->_ready_task)
{
task->cancel();
delete task;
}
this->_ready_task.clear();
}
{
scoped_lock<std::recursive_mutex> __guard(_mtx_task);
for (auto task : this->_task)
{
task->cancel();
delete task;
}
this->_task.clear();
}
}
void scheduler::break_all()
{
cancel_all_task_();
scoped_lock<std::recursive_mutex> __guard(_mtx_task);
this->_timer->clear();
}
void scheduler::run_one_batch()
{
{
scoped_lock<std::recursive_mutex> __guard(_mtx_task);
this->_timer->update();
using namespace std::chrono;
for (auto iter = this->_task.begin(); iter != this->_task.end(); )
{
#if _DEBUG
#define MAX_TIME_COST 10000us
#else
#define MAX_TIME_COST 1000us
#endif
// time_cost_evaluation<microseconds> eva(MAX_TIME_COST);
auto task = *iter;
if (task->is_suspend() || task->go_next(this))
{
// eva.add("coscheduler");
++iter;
continue;
}
iter = this->_task.erase(iter);
delete task;
}
}
{
scoped_lock<std::recursive_mutex> __guard(_mtx_ready);
if (this->_ready_task.size() > 0)
{
this->_task.insert(this->_task.end(), this->_ready_task.begin(), this->_ready_task.end());
this->_ready_task.clear();
}
}
}
void scheduler::run_until_notask()
{
while (!this->empty())
this->run_one_batch();
}
void scheduler::swap(scheduler & right_)
{
if (this != &right_)
{
scoped_lock<std::recursive_mutex, std::recursive_mutex, std::recursive_mutex, std::recursive_mutex>
__guard(this->_mtx_ready, this->_mtx_task, right_._mtx_ready, right_._mtx_task);
std::swap(this->_ready_task, right_._ready_task);
std::swap(this->_task, right_._task);
std::swap(this->_timer, right_._timer);
}
}
scheduler g_scheduler;
}

+ 94
- 0
librf/src/scheduler.h View File

@@ -0,0 +1,94 @@

#pragma once
#include <array>
#include <vector>
#include <yvals.h>
#include "rf_task.h"
#include "utils.h"
#include "timer.h"
namespace resumef
{
struct scheduler : public std::enable_shared_from_this<scheduler>
{
private:
mutable std::recursive_mutex _mtx_ready;
std::deque<task_base *> _ready_task;
mutable std::recursive_mutex _mtx_task;
std::list<task_base *> _task;
timer_mgr_ptr _timer;
RF_API void new_task(task_base * task);
void cancel_all_task_();
public:
RF_API void run_one_batch();
RF_API void run_until_notask();
template<class _Ty>
inline void operator + (_Ty && t_)
{
typedef typename std::conditional<
decltype(std::_IsCallable(t_, 0))::value,
ctx_task_t<_Ty>,
task_t<_Ty> >::type task_type;
return new_task(new task_type(std::forward<_Ty>(t_)));
}
inline void push_task_internal(task_base * t_)
{
return new_task(t_);
}
inline bool empty() const
{
scoped_lock<std::recursive_mutex, std::recursive_mutex> __guard(_mtx_ready, _mtx_task);
return
(this->_task.size() + this->_ready_task.size()) == 0 &&
this->_timer->empty();
}
RF_API void break_all();
RF_API void swap(scheduler & right_);
inline timer_manager * timer() const
{
return _timer.get();
}
friend struct task_base;
RF_API scheduler();
RF_API ~scheduler();
RF_API scheduler(scheduler && right_);
RF_API scheduler & operator = (scheduler && right_);
scheduler(const scheduler &) = delete;
scheduler & operator = (const scheduler &) = delete;
};
//--------------------------------------------------------------------------------------------------
extern scheduler g_scheduler;
#if !defined(_DISABLE_RESUMEF_GO_MACRO)
#define go (*::resumef::this_scheduler()) +
#define GO (*::resumef::this_scheduler()) + [=]()->resumef::future_vt
#endif
//--------------------------------------------------------------------------------------------------
}
namespace std
{
inline void swap(resumef::scheduler & _Left, resumef::scheduler & right_)
{
_Left.swap(right_);
}
}

+ 20
- 0
librf/src/sleep.cpp View File

@@ -0,0 +1,20 @@
#include "scheduler.h"
#include "sleep.h"
namespace resumef
{
awaitable_t<bool> sleep_until_(const std::chrono::system_clock::time_point& tp_, scheduler & scheduler_)
{
resumef::awaitable_t<bool> awaitable;
scheduler_.timer()->add(tp_,
[st = awaitable._state](bool cancellation_requested)
{
st->set_value(cancellation_requested);
});
return awaitable;
}
}

+ 35
- 0
librf/src/sleep.h View File

@@ -0,0 +1,35 @@
#pragma once
namespace resumef
{
struct scheduler;
RF_API awaitable_t<bool> sleep_until_(const std::chrono::system_clock::time_point& tp_, scheduler & scheduler_);
inline awaitable_t<bool> sleep_for_(const std::chrono::system_clock::duration& dt_, scheduler & scheduler_)
{
return std::move(sleep_until_(std::chrono::system_clock::now() + dt_, scheduler_));
}
template<class _Rep, class _Period>
awaitable_t<bool> sleep_for(const std::chrono::duration<_Rep, _Period>& dt_, scheduler & scheduler_)
{
return std::move(sleep_for_(std::chrono::duration_cast<std::chrono::system_clock::duration>(dt_), scheduler_));
}
template<class _Rep, class _Period>
awaitable_t<bool> sleep_for(const std::chrono::duration<_Rep, _Period>& dt_)
{
return std::move(sleep_for_(std::chrono::duration_cast<std::chrono::system_clock::duration>(dt_), g_scheduler));
}
template<class _Clock, class _Duration = typename _Clock::duration>
awaitable_t<bool> sleep_until(const std::chrono::time_point<_Clock, _Duration>& tp_, scheduler & scheduler_)
{
return std::move(sleep_until_(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp_), scheduler_));
}
template<class _Clock, class _Duration>
awaitable_t<bool> sleep_until(const std::chrono::time_point<_Clock, _Duration>& tp_)
{
return std::move(sleep_until_(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp_), g_scheduler));
}
}

+ 44
- 0
librf/src/spinlock.h View File

@@ -0,0 +1,44 @@

//用于内部实现的循环锁
#pragma once
#include "def.h"
namespace resumef
{
struct spinlock
{
volatile std::atomic_flag lck;
spinlock()
{
lck.clear();
}
void lock()
{
if (std::atomic_flag_test_and_set_explicit(&lck, std::memory_order_acquire))
{
using namespace std::chrono;
auto dt = 1ms;
while (std::atomic_flag_test_and_set_explicit(&lck, std::memory_order_acquire))
{
std::this_thread::sleep_for(dt);
dt *= 2;
}
}
}
bool try_lock()
{
bool ret = !std::atomic_flag_test_and_set_explicit(&lck, std::memory_order_acquire);
return ret;
}
void unlock()
{
std::atomic_flag_clear_explicit(&lck, std::memory_order_release);
}
};
}

+ 284
- 0
librf/src/state.h View File

@@ -0,0 +1,284 @@

#pragma once
#include "def.h"
#include <iostream>
namespace resumef
{
struct state_base
{
protected:
std::mutex _mtx; //for value, _exception
RF_API void set_value_none_lock();
public:
std::experimental::coroutine_handle<> _coro;
std::atomic<intptr_t> _count = 0; // tracks reference count of state object
std::exception_ptr _exception;
bool _ready = false;
bool _future_acquired = false;
bool _cancellation = false;
bool _done = false;
state_base()
{
#if RESUMEF_DEBUG_COUNTER
++g_resumef_state_count;
#endif
}
//某个地方的代码,发生过截断,导致了内存泄漏。还是加上虚析构函数吧。
virtual ~state_base();
state_base(state_base&&) = delete;
state_base(const state_base&) = delete;
state_base & operator = (state_base&&) = delete;
state_base & operator = (const state_base&) = delete;
RF_API void set_exception(std::exception_ptr && e_);
template<class _Exp>
void throw_exception(const _Exp & e_)
{
set_exception(std::make_exception_ptr(e_));
}
void rethrow_if_exception()
{
if (_exception)
std::rethrow_exception(_exception);
}
bool ready() const
{
return _ready;
}
void reset_none_lock()
{
_coro = nullptr;
_ready = false;
_future_acquired = false;
}
void cancel()
{
scoped_lock<std::mutex> __guard(_mtx);
_cancellation = true;
_coro = nullptr;
}
bool is_suspend() const
{
return !_coro && !(_done || _ready || _cancellation);
}
void resume()
{
if (_coro)
{
auto coro = _coro;
_coro = nullptr;
//std::cout << "resume from " << coro.address() << " on thread " << std::this_thread::get_id() << std::endl;
coro();
}
}
state_base* lock()
{
++_count;
return this;
}
void unlock()
{
if (--_count == 0)
delete this;
}
//------------------------------------------------------------------------------------------
//以下是通过future_t/promise_t, 与编译器生成的resumable function交互的接口
bool await_ready()
{
return _ready;
}
void await_suspend(std::experimental::coroutine_handle<> resume_cb)
{
_coro = resume_cb;
}
void await_resume()
{
}
void final_suspend()
{
_done = true;
}
bool cancellation_requested() const
{
return _cancellation;
}
//以上是通过future_t/promise_t, 与编译器生成的resumable function交互的接口
//------------------------------------------------------------------------------------------
};
template <typename _Ty>
struct state_t : public state_base
{
public:
typedef _Ty value_type;
value_type _value;
state_t<_Ty>* lock()
{
++_count;
return this;
}
void set_value(const value_type& t)
{
scoped_lock<std::mutex> __guard(_mtx);
_value = t;
state_base::set_value_none_lock();
}
void set_value(value_type&& t)
{
scoped_lock<std::mutex> __guard(_mtx);
_value = std::forward<value_type>(t);
state_base::set_value_none_lock();
}
_Ty & get_value()
{
scoped_lock<std::mutex> __guard(_mtx);
if (!_ready)
throw future_exception{ future_error::not_ready };
return _value;
}
void reset()
{
scoped_lock<std::mutex> __guard(_mtx);
state_base::reset_none_lock();
_value = value_type{};
}
};
template<>
struct state_t<void> : public state_base
{
state_t<void>* lock()
{
++_count;
return this;
}
void set_value()
{
scoped_lock<std::mutex> __guard(_mtx);
set_value_none_lock();
}
void get_value()
{
scoped_lock<std::mutex> __guard(_mtx);
if (!_ready)
throw future_exception{ future_error::not_ready };
}
void reset()
{
scoped_lock<std::mutex> __guard(_mtx);
reset_none_lock();
}
};
// counted_ptr is similar to shared_ptr but allows explicit control
//
template <typename T>
struct counted_ptr
{
counted_ptr() = default;
counted_ptr(const counted_ptr& cp) : _p(cp._p)
{
_lock();
}
counted_ptr(T* p) : _p(p)
{
_lock();
}
counted_ptr(counted_ptr&& cp)
{
std::swap(_p, cp._p);
}
counted_ptr& operator=(const counted_ptr& cp)
{
if (&cp != this)
{
_unlock();
_lock(cp._p);
}
return *this;
}
counted_ptr& operator=(counted_ptr&& cp)
{
if (&cp != this)
std::swap(_p, cp._p);
return *this;
}
~counted_ptr()
{
_unlock();
}
T* operator->() const
{
return _p;
}
T* get() const
{
return _p;
}
void reset()
{
_unlock();
}
protected:
void _unlock()
{
if (_p != nullptr)
{
auto t = _p;
_p = nullptr;
t->unlock();
}
}
void _lock(T* p)
{
if (p != nullptr)
p->lock();
_p = p;
}
void _lock()
{
if (_p != nullptr)
_p->lock();
}
T* _p = nullptr;
};
template <typename T>
counted_ptr<T> make_counted()
{
return new T{};
}
}

+ 106
- 0
librf/src/timer.cpp View File

@@ -0,0 +1,106 @@
#include "timer.h"
namespace resumef
{
timer_manager::timer_manager()
{
_added_timers.reserve(128);
}
timer_manager::~timer_manager()
{
clear();
}
void timer_manager::call_target_(const timer_target_ptr & sptr, bool canceld)
{
auto cb = std::move(sptr->cb);
sptr->st = timer_target::State::Invalid;
#if _DEBUG
sptr->_manager = nullptr;
#endif
if(cb) cb(canceld);
}
void timer_manager::clear()
{
auto _atimer = std::move(_added_timers);
for (auto & sptr : _atimer)
call_target_(sptr, true);
auto _rtimer = std::move(_runing_timers);
for (auto & kv : _rtimer)
call_target_(kv.second, true);
}
detail::timer_target_ptr timer_manager::add_(const timer_target_ptr & sptr)
{
assert(sptr);
assert(sptr->st == timer_target::State::Invalid);
#if _DEBUG
assert(sptr->_manager == nullptr);
sptr->_manager = this;
#endif
sptr->st = timer_target::State::Added;
_added_timers.push_back(sptr);
return sptr;
}
bool timer_manager::stop(const timer_target_ptr & sptr)
{
if (!sptr || sptr->st == timer_target::State::Invalid)
return false;
#if _DEBUG
assert(sptr->_manager == this);
#endif
sptr->st = timer_target::State::Invalid;
return true;
}
void timer_manager::update()
{
if (_added_timers.size() > 0)
{
for (auto & sptr : _added_timers)
{
if (sptr->st == timer_target::State::Added)
{
sptr->st = timer_target::State::Runing;
_runing_timers.insert({ sptr->tp, sptr });
}
else
{
assert(sptr->st == timer_target::State::Invalid);
call_target_(sptr, true);
}
}
_added_timers.clear();
}
if (_runing_timers.size() > 0)
{
auto now_ = clock_type::now();
timer_map_type _timers = std::move(_runing_timers);
auto iter = _timers.begin();
for (; iter != _timers.end(); ++iter)
{
auto & kv = *iter;
if (kv.first > now_)
break;
call_target_(kv.second, kv.second->st == timer_target::State::Invalid);
}
_timers.erase(_timers.begin(), iter);
_runing_timers = std::move(_timers);
}
}
}

+ 186
- 0
librf/src/timer.h View File

@@ -0,0 +1,186 @@
#pragma once
#include "def.h"
namespace resumef
{
struct timer_manager;
typedef std::shared_ptr<timer_manager> timer_mgr_ptr;
typedef std::weak_ptr<timer_manager> timer_mgr_wptr;
namespace detail
{
typedef std::chrono::system_clock timer_clock_type;
typedef std::function<void(bool)> timer_callback_type;
struct timer_target : public std::enable_shared_from_this<timer_target>
{
friend timer_manager;
private:
enum struct State : uint32_t
{
Invalid,
Added,
Runing,
};
timer_clock_type::time_point tp;
timer_callback_type cb;
State st = State::Invalid;
#if _DEBUG
private:
timer_manager * _manager = nullptr;
#endif
public:
timer_target(const timer_clock_type::time_point & tp_, const timer_callback_type & cb_)
: tp(tp_)
, cb(cb_)
{
}
timer_target(const timer_clock_type::time_point & tp_, timer_callback_type && cb_)
: tp(tp_)
, cb(std::forward<timer_callback_type>(cb_))
{
}
private:
timer_target() = delete;
timer_target(const timer_target &) = delete;
timer_target(timer_target && right_) = delete;
timer_target & operator = (const timer_target &) = delete;
timer_target & operator = (timer_target && right_) = delete;
};
typedef std::shared_ptr<timer_target> timer_target_ptr;
typedef std::weak_ptr<timer_target> timer_target_wptr;
}
struct timer_handler
{
private:
timer_mgr_wptr _manager;
detail::timer_target_wptr _target;
public:
timer_handler() = default;
timer_handler(const timer_handler &) = default;
timer_handler(timer_handler && right_);
timer_handler & operator = (const timer_handler &) = default;
timer_handler & operator = (timer_handler && right_);
timer_handler(timer_manager * manager_, const detail::timer_target_ptr & target_);
void reset();
bool stop();
bool expired() const;
};
struct timer_manager : public std::enable_shared_from_this<timer_manager>
{
typedef detail::timer_target timer_target;
typedef detail::timer_target_ptr timer_target_ptr;
typedef detail::timer_clock_type clock_type;
typedef clock_type::duration duration_type;
typedef clock_type::time_point time_point_type;
typedef std::vector<timer_target_ptr> timer_vector_type;
typedef std::multimap<clock_type::time_point, timer_target_ptr> timer_map_type;
protected:
timer_vector_type _added_timers;
public:
timer_map_type _runing_timers;
RF_API timer_manager();
RF_API ~timer_manager();
template<class _Rep, class _Period, class _Cb>
timer_target_ptr add(const std::chrono::duration<_Rep, _Period> & dt_, _Cb && cb_)
{
return add_(std::chrono::duration_cast<duration_type>(dt_), std::forward<_Cb>(cb_));
}
template<class _Clock, class _Duration = typename _Clock::duration, class _Cb>
timer_target_ptr add(const std::chrono::time_point<_Clock, _Duration> & tp_, _Cb && cb_)
{
return add_(std::chrono::time_point_cast<duration_type>(tp_), std::forward<_Cb>(cb_));
}
template<class _Rep, class _Period, class _Cb>
timer_handler add_handler(const std::chrono::duration<_Rep, _Period> & dt_, _Cb && cb_)
{
return{ this, add(dt_, std::forward<_Cb>(cb_)) };
}
template<class _Clock, class _Duration = typename _Clock::duration, class _Cb>
timer_handler add_handler(const std::chrono::time_point<_Clock, _Duration> & tp_, _Cb && cb_)
{
return{ this, add(tp_, std::forward<_Cb>(cb_)) };
}
RF_API bool stop(const timer_target_ptr & sptr);
inline bool empty() const
{
return _added_timers.empty() && _runing_timers.empty();
}
RF_API void clear();
RF_API void update();
template<class _Cb>
timer_target_ptr add_(const duration_type & dt_, _Cb && cb_)
{
return add_(std::make_shared<timer_target>(clock_type::now() + dt_, std::forward<_Cb>(cb_)));
}
template<class _Cb>
timer_target_ptr add_(const time_point_type & tp_, _Cb && cb_)
{
return add_(std::make_shared<timer_target>(tp_, std::forward<_Cb>(cb_)));
}
private:
RF_API timer_target_ptr add_(const timer_target_ptr & sptr);
static void call_target_(const timer_target_ptr & sptr, bool canceld);
};
inline timer_handler::timer_handler(timer_manager * manager_, const detail::timer_target_ptr & target_)
: _manager(manager_->shared_from_this())
, _target(target_)
{
}
inline timer_handler::timer_handler(timer_handler && right_)
: _manager(std::move(right_._manager))
, _target(std::move(right_._target))
{
}
inline timer_handler & timer_handler::operator = (timer_handler && right_)
{
if (this != &right_)
{
_manager = std::move(right_._manager);
_target = std::move(right_._target);
}
return *this;
}
inline void timer_handler::reset()
{
_manager.reset();
_target.reset();
}
inline bool timer_handler::stop()
{
bool result = false;
if (!_target.expired())
{
auto sptr = _manager.lock();
if (sptr)
result = sptr->stop(_target.lock());
_target.reset();
}
_manager.reset();
return result;
}
inline bool timer_handler::expired() const
{
return _target.expired();
}
}

+ 91
- 0
librf/src/unix/coroutine.h View File

@@ -0,0 +1,91 @@
//#pragma once

namespace std {
namespace experimental {
template <typename R, typename...> struct coroutine_traits {
using promise_type = typename R::promise_type;
};

template <typename Promise = void> struct coroutine_handle;

template <> struct coroutine_handle<void> {
static coroutine_handle from_address(void *addr) noexcept {
coroutine_handle me;
me.ptr = addr;
return me;
}
void operator()() { resume(); }
void *address() const { return ptr; }
void resume() const { __builtin_coro_resume(ptr); }
void destroy() const { __builtin_coro_destroy(ptr); }
bool done() const { return __builtin_coro_done(ptr); }
coroutine_handle &operator=(decltype(nullptr)) {
ptr = nullptr;
return *this;
}
coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
coroutine_handle() : ptr(nullptr) {}
// void reset() { ptr = nullptr; } // add to P0057?
explicit operator bool() const { return ptr; }

protected:
void *ptr;
};

template <typename Promise> struct coroutine_handle : coroutine_handle<> {

static coroutine_handle from_address(void *addr) noexcept {
coroutine_handle me;
me.ptr = addr;
return me;
}
coroutine_handle() {}
coroutine_handle(decltype(nullptr)) {}
coroutine_handle &operator=(decltype(nullptr)) {
ptr = nullptr;
return *this;
}

Promise &promise() const {
return *reinterpret_cast<Promise *>(
__builtin_coro_promise(ptr, alignof(Promise), false));
}
static coroutine_handle from_promise(Promise &promise) {
coroutine_handle p;
p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
return p;
}
};

template <typename _PromiseT>
bool operator==(coroutine_handle<_PromiseT> const &_Left,
coroutine_handle<_PromiseT> const &_Right) noexcept {
return _Left.address() == _Right.address();
}

template <typename _PromiseT>
bool operator!=(coroutine_handle<_PromiseT> const &_Left,
coroutine_handle<_PromiseT> const &_Right) noexcept {
return !(_Left == _Right);
}

struct suspend_always {
bool await_ready() { return false; }
void await_suspend(coroutine_handle<>) {}
void await_resume() {}
};
struct suspend_never {
bool await_ready() { return true; }
void await_suspend(coroutine_handle<>) {}
void await_resume() {}
};
struct suspend_if {
bool _Ready;

explicit suspend_if(bool _Condition) : _Ready(!_Condition) {}
bool await_ready() { return _Ready; }
void await_suspend(coroutine_handle<>) {}
void await_resume() {}
};
}
}

+ 17
- 0
librf/src/utils.h View File

@@ -0,0 +1,17 @@
#pragma once
namespace std
{
template<typename _Function>
inline auto _IsCallable(const _Function & _Func, int) -> decltype(_Func(), true_type())
{
(_Func);
return true_type();
}
template<typename _Function>
inline false_type _IsCallable(const _Function &, ...)
{
return false_type();
}
}

+ 62
- 0
tutorial/test_async_cb.cpp View File

@@ -0,0 +1,62 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
auto async_get_long(int64_t val)
{
using namespace std::chrono;
resumef::promise_t<int64_t> awaitable;
std::thread([val, st = awaitable._state->lock()]
{
std::this_thread::sleep_for(500ms);
st->set_value(val * val);
st->unlock();
}).detach();
return awaitable.get_future();
}
resumef::future_vt resumable_get_long(int64_t val)
{
std::cout << val << std::endl;
val = co_await async_get_long(val);
std::cout << val << std::endl;
val = co_await async_get_long(val);
std::cout << val << std::endl;
val = co_await async_get_long(val);
std::cout << val << std::endl;
}
resumef::future_t<int64_t> loop_get_long(int64_t val)
{
std::cout << val << std::endl;
for (int i = 0; i < 5; ++i)
{
val = co_await async_get_long(val);
std::cout << val << std::endl;
}
return val;
}
void resumable_main_cb()
{
std::cout << std::this_thread::get_id() << std::endl;
go []()->resumef::future_vt
{
auto val = co_await loop_get_long(2);
std::cout << val << std::endl;
};
//resumef::g_scheduler.run_until_notask();
go resumable_get_long(3);
resumef::g_scheduler.run_until_notask();
}

+ 89
- 0
tutorial/test_async_channel.cpp View File

@@ -0,0 +1,89 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include <deque>
#include <mutex>
#include "librf.h"
using namespace resumef;
const size_t MAX_CHANNEL_QUEUE = 0; //0, 1, 5, 10, -1
future_vt test_channel_read(const channel_t<size_t> & c)
{
using namespace std::chrono;
for (size_t i = 0; i < 10; ++i)
{
try
{
//auto val = co_await c.read();
auto val = co_await c; //第二种从channel读出数据的方法。利用重载operator co_await(),而不是c是一个awaitable_t。
std::cout << val << ":";
#if _DEBUG
for (auto val : c.debug_queue())
std::cout << val << ",";
#endif
std::cout << std::endl;
}
catch (channel_exception e)
{
//MAX_CHANNEL_QUEUE=0,并且先读后写,会触发read_before_write异常
std::cout << e.what() << std::endl;
}
co_await sleep_for(50ms);
}
}
future_vt test_channel_write(const channel_t<size_t> & c)
{
using namespace std::chrono;
for (size_t i = 0; i < 10; ++i)
{
//co_await c.write(i);
co_await (c << i); //第二种写入数据到channel的方法。因为优先级关系,需要将'c << i'括起来
std::cout << "<" << i << ">:";
#if _DEBUG
for (auto val : c.debug_queue())
std::cout << val << ",";
#endif
std::cout << std::endl;
}
}
void test_channel_read_first()
{
channel_t<size_t> c(MAX_CHANNEL_QUEUE);
go test_channel_read(c);
go test_channel_write(c);
g_scheduler.run_until_notask();
}
void test_channel_write_first()
{
channel_t<size_t> c(MAX_CHANNEL_QUEUE);
go test_channel_write(c);
go test_channel_read(c);
g_scheduler.run_until_notask();
}
void resumable_main_channel()
{
test_channel_read_first();
std::cout << std::endl;
test_channel_write_first();
}

+ 62
- 0
tutorial/test_async_dynamic_go.cpp View File

@@ -0,0 +1,62 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include "librf.h"
static const int M = 10;
size_t dynamic_go_count = 0;
std::array<std::array<std::array<int32_t, M>, M>, 3> dynamic_cells;
void test_dynamic_go()
{
auto co_star = [](int j)
{
for (int i = 0; i < M; ++i)
{
go[=]()
{
for (int k = 0; k < M; ++k)
{
++dynamic_cells[j][i][k];
++dynamic_go_count;
std::cout << j << " " << i << " " << k << std::endl;
co_yield k;
}
co_return M;
};
co_yield i;
}
co_return M;
};
go co_star(0);
go co_star(1);
go co_star(2);
resumef::g_scheduler.run_until_notask();
std::cout << "dynamic_go_count = " << dynamic_go_count << std::endl;
for (auto & j : dynamic_cells)
{
for (auto & i : j)
{
for (auto k : i)
std::cout << k;
std::cout << std::endl;
}
std::cout << std::endl;
}
}
void resumable_main_dynamic_go()
{
test_dynamic_go();
}

+ 163
- 0
tutorial/test_async_event.cpp View File

@@ -0,0 +1,163 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
std::thread async_set_event(const event_t & e, std::chrono::milliseconds dt)
{
return std::thread([=]
{
std::this_thread::sleep_for(dt);
e.signal();
});
}
future_vt resumable_wait_event(const event_t & e)
{
using namespace std::chrono;
if (co_await e.wait() == false)
std::cout << "time out!" << std::endl;
else
std::cout << "event signal!" << std::endl;
}
void test_wait_one()
{
using namespace std::chrono;
{
event_t evt;
go resumable_wait_event(evt);
auto tt = async_set_event(evt, 1000ms);
g_scheduler.run_until_notask();
tt.join();
}
{
event_t evt2(1);
go[&]() -> future_vt
{
(void)co_await evt2.wait();
std::cout << "event signal on 1!" << std::endl;
};
go[&]() -> future_vt
{
(void)co_await evt2.wait();
std::cout << "event signal on 2!" << std::endl;
};
std::cout << std::this_thread::get_id() << std::endl;
auto tt = async_set_event(evt2, 1000ms);
g_scheduler.run_until_notask();
tt.join();
}
}
void test_wait_any()
{
using namespace std::chrono;
event_t evts[8];
go[&]() -> future_vt
{
for (int i = 0; i < _countof(evts); ++i)
{
intptr_t idx = co_await event_t::wait_any(evts);
std::cout << "event " << idx << " signal!" << std::endl;
}
};
std::vector<std::thread> vtt;
srand((int)time(nullptr));
for (auto & e : evts)
{
vtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000)));
}
g_scheduler.run_until_notask();
for (auto & tt : vtt)
tt.join();
}
void test_wait_all()
{
using namespace std::chrono;
event_t evts[8];
go[&]() -> future_vt
{
if (co_await event_t::wait_all(evts))
std::cout << "all event signal!" << std::endl;
else
std::cout << "time out!" << std::endl;
};
std::vector<std::thread> vtt;
srand((int)time(nullptr));
for (auto & e : evts)
{
vtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000)));
}
g_scheduler.run_until_notask();
for (auto & tt : vtt)
tt.join();
}
void test_wait_all_timeout()
{
using namespace std::chrono;
event_t evts[8];
go[&]() -> future_vt
{
if (co_await event_t::wait_all_for(1000ms, evts))
std::cout << "all event signal!" << std::endl;
else
std::cout << "time out!" << std::endl;
};
std::vector<std::thread> vtt;
srand((int)time(nullptr));
for (auto & e : evts)
{
vtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000)));
}
g_scheduler.run_until_notask();
for (auto & tt : vtt)
tt.join();
}
void resumable_main_event()
{
test_wait_one();
std::cout << std::endl;
test_wait_any();
std::cout << std::endl;
test_wait_all();
std::cout << std::endl;
test_wait_all_timeout();
std::cout << std::endl;
}

+ 178
- 0
tutorial/test_async_event_timeout.cpp View File

@@ -0,0 +1,178 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
future_vt resumalbe_set_event(const event_t & e, std::chrono::milliseconds dt)
{
co_await resumef::sleep_for(dt);
e.signal();
std::cout << "+";
}
void async_set_event(const event_t & e, std::chrono::milliseconds dt)
{
std::thread([=]
{
std::this_thread::sleep_for(dt);
e.signal();
}).detach();
}
void test_wait_timeout_one()
{
std::cout << __FUNCTION__ << std::endl;
using namespace std::chrono;
event_t evt;
go [&evt]() -> future_vt
{
intptr_t counter = 0;
for (;;)
{
if (co_await evt.wait_for(500ms))
break;
++counter;
std::cout << ".";
}
std::cout << counter << std::endl;
};
async_set_event(evt, 10s + 50ms);
//go resumalbe_set_event(evt, 10s + 50ms);
g_scheduler.run_until_notask();
}
void test_wait_timeout_any_invalid()
{
std::cout << __FUNCTION__ << std::endl;
using namespace std::chrono;
event_t evts[8];
//无效的等待
go[&]()-> future_vt
{
intptr_t idx = co_await event_t::wait_any_for(500ms, std::begin(evts), std::end(evts));
assert(idx < 0);
std::cout << "invalid wait!" << std::endl;
};
g_scheduler.run_until_notask();
}
void test_wait_timeout_any()
{
std::cout << __FUNCTION__ << std::endl;
using namespace std::chrono;
event_t evts[8];
go[&]() -> future_vt
{
intptr_t counter = 0;
for (;;)
{
intptr_t idx = co_await event_t::wait_any_for(500ms, evts);
if (idx >= 0)
{
std::cout << counter << std::endl;
std::cout << "event " << idx << " signal!" << std::endl;
break;
}
++counter;
std::cout << ".";
}
//取消剩下的定时器,以便于协程调度器退出来
g_scheduler.timer()->clear();
};
srand((int)time(nullptr));
for (auto & e : evts)
{
//go resumalbe_set_event(e, 1ms * (1000 + rand() % 5000));
async_set_event(e, 1ms * (1000 + rand() % 5000));
}
g_scheduler.run_until_notask();
}
void test_wait_timeout_all_invalid()
{
std::cout << __FUNCTION__ << std::endl;
using namespace std::chrono;
event_t evts[8];
//无效的等待
go[&]()-> future_vt
{
bool result = co_await event_t::wait_all_for(500ms, std::begin(evts), std::end(evts));
assert(!result);
std::cout << "invalid wait!" << std::endl;
};
g_scheduler.run_until_notask();
}
void test_wait_timeout_all()
{
std::cout << __FUNCTION__ << std::endl;
using namespace std::chrono;
event_t evts[8];
go[&]() -> future_vt
{
intptr_t counter = 0;
for (;;)
{
if (co_await event_t::wait_all_until(system_clock::now() + 500ms, evts))
{
std::cout << counter << std::endl;
std::cout << "all event signal!" << std::endl;
break;
}
++counter;
std::cout << ".";
}
};
srand((int)time(nullptr));
for (auto & e : evts)
{
go resumalbe_set_event(e, 1ms * (1000 + rand() % 5000));
//async_set_event(e, 1ms * (1000 + rand() % 5000));
}
g_scheduler.run_until_notask();
}
void resumable_main_event_timeout()
{
using namespace std::chrono;
test_wait_timeout_one();
std::cout << std::endl;
test_wait_timeout_any_invalid();
std::cout << std::endl << std::endl;
test_wait_timeout_any();
std::cout << std::endl << std::endl;
test_wait_timeout_all_invalid();
std::cout << std::endl;
test_wait_timeout_all();
std::cout << std::endl;
}

+ 75
- 0
tutorial/test_async_exception.cpp View File

@@ -0,0 +1,75 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
//请打开结构化异常(/EHa)
auto async_signal_exception(const intptr_t dividend)
{
using namespace std::chrono;
resumef::awaitable_t<int64_t> awaitable;
std::thread([dividend, st = awaitable._state]
{
std::this_thread::sleep_for(50ms);
try
{
if (dividend == 0)
throw std::logic_error("divided by zero");
st->set_value(10000 / dividend);
}
catch (...)
{
st->set_exception(std::current_exception());
}
}).detach();
return awaitable;
}
future_vt test_signal_exception()
{
for (intptr_t i = 10; i >= 0; --i)
{
try
{
auto r = co_await async_signal_exception(i);
std::cout << "result is " << r << std::endl;
}
catch (const std::exception& e)
{
std::cout << "exception signal : " << e.what() << std::endl;
}
catch (...)
{
std::cout << "exception signal : who knows?" << std::endl;
}
}
}
future_vt test_bomb_exception()
{
for (intptr_t i = 10; i >= 0; --i)
{
auto r = co_await async_signal_exception(i);
std::cout << "result is " << r << std::endl;
}
}
void resumable_main_exception()
{
go test_signal_exception();
g_scheduler.run_until_notask();
std::cout << std::endl;
go test_bomb_exception();
g_scheduler.run_until_notask();
}

+ 55
- 0
tutorial/test_async_mutex.cpp View File

@@ -0,0 +1,55 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include <deque>
#include <mutex>
#include "librf.h"
using namespace resumef;
mutex_t g_lock;
std::deque<size_t> g_queue;
future_vt test_mutex_pop(size_t idx)
{
using namespace std::chrono;
for (size_t i = 0; i < 10; ++i)
{
co_await resumf_guard_lock(g_lock);
if (g_queue.size() > 0)
{
size_t val = g_queue.front();
g_queue.pop_front();
std::cout << val << " on " << idx << std::endl;
}
co_await sleep_for(500ms);
}
}
future_vt test_mutex_push()
{
using namespace std::chrono;
for (size_t i = 0; i < 10; ++i)
{
co_await resumf_guard_lock(g_lock);
g_queue.push_back(i);
co_await sleep_for(500ms);
}
}
void resumable_main_mutex()
{
go test_mutex_push();
go test_mutex_pop(1);
g_scheduler.run_until_notask();
}

+ 52
- 0
tutorial/test_async_resumable.cpp View File

@@ -0,0 +1,52 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include "librf.h"
//static const int N = 100000000;
static const int N = 10;
template <typename T>
void dump(std::string name, int n, T start, T end)
{
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
std::cout << name << " " << n << " " << ns << " ns " << ns / n << " ns/op" << std::endl;
}
auto yield_switch(int coro)
{
for (int i = 0; i < N / coro; ++i)
co_yield i;
return N / coro;
}
void resumable_switch(int coro)
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < coro; ++i)
{
//go yield_switch(coro);
go [=]
{
for (int i = 0; i < N / coro; ++i)
co_yield i;
return N / coro;
};
}
resumef::g_scheduler.run_until_notask();
auto end = std::chrono::steady_clock::now();
dump("BenchmarkSwitch_" + std::to_string(coro), N, start, end);
}
void resumable_main_resumable()
{
resumable_switch(1);
resumable_switch(1000);
resumable_switch(1000000);
}

+ 35
- 0
tutorial/test_async_routine.cpp View File

@@ -0,0 +1,35 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
future_vt test_routine_use_timer()
{
using namespace std::chrono;
for (size_t i = 0; i < 10; ++i)
{
co_await resumef::sleep_for(100ms);
std::cout << "timer after 100ms." << std::endl;
}
}
future_vt test_routine_use_timer_2()
{
co_await test_routine_use_timer();
co_await test_routine_use_timer();
co_await test_routine_use_timer();
}
void resumable_main_routine()
{
go test_routine_use_timer_2();
//test_routine_use_timer();
g_scheduler.run_until_notask();
}

+ 66
- 0
tutorial/test_async_sleep.cpp View File

@@ -0,0 +1,66 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
future_vt test_sleep_use_timer()
{
using namespace std::chrono;
co_await resumef::sleep_for(100ms);
std::cout << "timer after 100ms." << std::endl;
if (co_await resumef::sleep_until(system_clock::now() + 200ms))
std::cout << "timer canceled." << std::endl;
else
std::cout << "timer after 200ms." << std::endl;
}
void test_wait_all_events_with_signal_by_sleep()
{
using namespace std::chrono;
event_t evts[8];
go[&]() -> future_vt
{
if (co_await event_t::wait_all(evts))
std::cout << "all event signal!" << std::endl;
else
std::cout << "time out!" << std::endl;
};
srand((int)time(nullptr));
for(size_t i=0; i<_countof(evts); ++i)
{
go[&, i]() -> future_vt
{
co_await resumef::sleep_for(1ms * (500 + rand() % 1000));
evts[i].signal();
std::cout << "event[ " << i << " ] signal!" << std::endl;
};
}
while (!g_scheduler.empty())
{
g_scheduler.run_one_batch();
//std::cout << "press any key to continue." << std::endl;
//_getch();
}
}
void resumable_main_sleep()
{
go test_sleep_use_timer();
g_scheduler.run_until_notask();
std::cout << std::endl;
test_wait_all_events_with_signal_by_sleep();
std::cout << std::endl;
}

+ 95
- 0
tutorial/test_async_suspend_always.cpp View File

@@ -0,0 +1,95 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
template<size_t _N>
future_vt test_loop_sleep()
{
using namespace std::chrono;
for (size_t i = 0; i < _N; ++i)
{
co_await resumef::sleep_for(100ms);
std::cout << ".";
}
std::cout << std::endl;
}
future_vt test_recursive_await()
{
std::cout << "---1" << std::endl;
co_await test_loop_sleep<5>();
std::cout << "---2" << std::endl;
co_await test_loop_sleep<6>();
std::cout << "---3" << std::endl;
co_await test_loop_sleep<7>();
std::cout << "---4" << std::endl;
}
future_vt test_recursive_go()
{
std::cout << "---1" << std::endl;
co_await test_loop_sleep<3>();
std::cout << "---2" << std::endl;
go test_loop_sleep<5>();
std::cout << "---3" << std::endl;
co_await test_loop_sleep<4>();
std::cout << "---4" << std::endl;
}
void resumable_main_suspend_always()
{
go test_recursive_await();
go test_recursive_go();
g_scheduler.run_until_notask();
}
/*
resume from 0000016B8477CE00 on thread 7752
resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.
resume from 0000016B8477CE00 on thread 7752
resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.
resume from 0000016B8477CE00 on thread 7752
resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.resume from 0000016B847726C0 on thread 7752
.
resume from 0000016B8477CE00 on thread 7752
说明有四个协程对象(其中三个对象的内存被复用,表现为地址一样)
test_recursive_await -> 0000016B8477CE00
test_loop_sleep<5> -> 0000016B847726C0
test_loop_sleep<6> -> 0000016B847726C0
test_loop_sleep<7> -> 0000016B847726C0
*/

+ 36
- 0
tutorial/test_async_timer.cpp View File

@@ -0,0 +1,36 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
void resumable_main_timer()
{
using namespace std::chrono;
auto th = g_scheduler.timer()->add_handler(system_clock::now() + 5s,
[](bool bValue)
{
if (bValue)
std::cout << "timer canceled." << std::endl;
else
std::cout << "timer after 5s." << std::endl;
});
auto th2 = g_scheduler.timer()->add_handler(1s,
[&th](bool)
{
std::cout << "timer after 1s." << std::endl;
th.stop();
});
g_scheduler.run_until_notask();
th2.stop(); //but th2 is invalid
}

+ 71
- 0
tutorial/test_async_yield_return.cpp View File

@@ -0,0 +1,71 @@

#include <chrono>
#include <iostream>
#include <string>
#include <conio.h>
#include <thread>
#include "librf.h"
using namespace resumef;
//std::experimental::generator<int>
auto test_yield_int()
{
std::cout << "1 will yield return" << std::endl;
co_yield 1;
std::cout << "2 will yield return" << std::endl;
co_yield 2;
std::cout << "3 will yield return" << std::endl;
co_yield 3;
std::cout << "4 will return" << std::endl;
return 4;
std::cout << "5 will never yield return" << std::endl;
co_yield 5;
}
/*不能编译*/
/*
auto test_yield_void()
{
std::cout << "1 will yield return" << std::endl;
co_yield ;
std::cout << "2 will yield return" << std::endl;
co_yield ;
std::cout << "3 will yield return" << std::endl;
co_yield ;
std::cout << "4 will return" << std::endl;
co_return ;
std::cout << "5 will never yield return" << std::endl;
co_yield ;
}
*/
#define co_yield_void co_yield nullptr
#define co_return_void co_return nullptr
auto test_yield_void()
{
std::cout << "block 1 will yield return" << std::endl;
co_yield_void;
std::cout << "block 2 will yield return" << std::endl;
co_yield_void;
std::cout << "block 3 will yield return" << std::endl;
co_yield_void;
std::cout << "block 4 will return" << std::endl;
co_return_void;
std::cout << "block 5 will never yield return" << std::endl;
co_yield_void;
}
void resumable_main_yield_return()
{
go test_yield_int();
g_scheduler.run_until_notask();
go test_yield_void();
g_scheduler.run_until_notask();
}

+ 35
- 0
vs_proj/librf.cpp View File

@@ -0,0 +1,35 @@
#include "librf.h"
extern void resumable_main_yield_return();
extern void resumable_main_timer();
extern void resumable_main_suspend_always();
extern void resumable_main_sleep();
extern void resumable_main_routine();
extern void resumable_main_resumable();
extern void resumable_main_mutex();
extern void resumable_main_exception();
extern void resumable_main_event();
extern void resumable_main_event_timeout();
extern void resumable_main_dynamic_go();
extern void resumable_main_channel();
extern void resumable_main_cb();
int main(int argc, const char * argv[])
{
resumable_main_yield_return();
resumable_main_timer();
resumable_main_suspend_always();
resumable_main_sleep();
resumable_main_routine();
resumable_main_resumable();
resumable_main_mutex();
resumable_main_exception();
resumable_main_event();
resumable_main_event_timeout();
resumable_main_dynamic_go();
resumable_main_channel();
resumable_main_cb();
return 0;
}

+ 31
- 0
vs_proj/librf.sln View File

@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.15
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "librf", "librf.vcxproj", "{C1D4A6BD-592F-4E48-8178-7C87219BF80E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x64.ActiveCfg = Debug|x64
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x64.Build.0 = Debug|x64
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x86.ActiveCfg = Debug|Win32
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Debug|x86.Build.0 = Debug|Win32
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x64.ActiveCfg = Release|x64
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x64.Build.0 = Release|x64
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x86.ActiveCfg = Release|Win32
{C1D4A6BD-592F-4E48-8178-7C87219BF80E}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {401D9E59-A4B3-4CA3-9696-B7D2D14D90FD}
EndGlobalSection
EndGlobal

+ 193
- 0
vs_proj/librf.vcxproj View File

@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{C1D4A6BD-592F-4E48-8178-7C87219BF80E}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>librf</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/await /stdlast++ %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/await /std:c++latest %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/await /stdlast++ %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\librf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/await /stdlast++ %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\librf\src\event.cpp" />
<ClCompile Include="..\librf\src\mutex.cpp" />
<ClCompile Include="..\librf\src\rf_task.cpp" />
<ClCompile Include="..\librf\src\scheduler.cpp" />
<ClCompile Include="..\librf\src\sleep.cpp" />
<ClCompile Include="..\librf\src\timer.cpp" />
<ClCompile Include="..\tutorial\test_async_cb.cpp" />
<ClCompile Include="..\tutorial\test_async_channel.cpp" />
<ClCompile Include="..\tutorial\test_async_dynamic_go.cpp" />
<ClCompile Include="..\tutorial\test_async_event.cpp" />
<ClCompile Include="..\tutorial\test_async_event_timeout.cpp" />
<ClCompile Include="..\tutorial\test_async_exception.cpp" />
<ClCompile Include="..\tutorial\test_async_mutex.cpp" />
<ClCompile Include="..\tutorial\test_async_resumable.cpp" />
<ClCompile Include="..\tutorial\test_async_routine.cpp" />
<ClCompile Include="..\tutorial\test_async_sleep.cpp" />
<ClCompile Include="..\tutorial\test_async_suspend_always.cpp" />
<ClCompile Include="..\tutorial\test_async_timer.cpp" />
<ClCompile Include="..\tutorial\test_async_yield_return.cpp" />
<ClCompile Include="librf.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\librf\librf.h" />
<ClInclude Include="..\librf\src\asio_task.h" />
<ClInclude Include="..\librf\src\channel.h" />
<ClInclude Include="..\librf\src\def.h" />
<ClInclude Include="..\librf\src\event.h" />
<ClInclude Include="..\librf\src\future.h" />
<ClInclude Include="..\librf\src\generator.h" />
<ClInclude Include="..\librf\src\mutex.h" />
<ClInclude Include="..\librf\src\rf_task.h" />
<ClInclude Include="..\librf\src\scheduler.h" />
<ClInclude Include="..\librf\src\sleep.h" />
<ClInclude Include="..\librf\src\spinlock.h" />
<ClInclude Include="..\librf\src\state.h" />
<ClInclude Include="..\librf\src\timer.h" />
<ClInclude Include="..\librf\src\unix\coroutine.h" />
<ClInclude Include="..\librf\src\utils.h" />
<ClInclude Include="..\librf\src\_awaker.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

+ 136
- 0
vs_proj/librf.vcxproj.filters View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="librf">
<UniqueIdentifier>{27e93efb-a01d-48f5-b10b-155fc8d52101}</UniqueIdentifier>
</Filter>
<Filter Include="librf\src">
<UniqueIdentifier>{7cbfb0bd-8820-4639-9235-93ff984a9bb3}</UniqueIdentifier>
</Filter>
<Filter Include="librf\src\unix">
<UniqueIdentifier>{46825786-52e6-4092-9418-cad7045aa118}</UniqueIdentifier>
</Filter>
<Filter Include="tutorial">
<UniqueIdentifier>{d82e4930-c171-4b70-9ebb-cdec7218ff9c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="librf.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\librf\src\event.cpp">
<Filter>librf\src</Filter>
</ClCompile>
<ClCompile Include="..\librf\src\mutex.cpp">
<Filter>librf\src</Filter>
</ClCompile>
<ClCompile Include="..\librf\src\rf_task.cpp">
<Filter>librf\src</Filter>
</ClCompile>
<ClCompile Include="..\librf\src\scheduler.cpp">
<Filter>librf\src</Filter>
</ClCompile>
<ClCompile Include="..\librf\src\sleep.cpp">
<Filter>librf\src</Filter>
</ClCompile>
<ClCompile Include="..\librf\src\timer.cpp">
<Filter>librf\src</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_cb.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_channel.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_dynamic_go.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_event.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_event_timeout.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_exception.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_mutex.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_resumable.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_routine.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_sleep.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_suspend_always.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_timer.cpp">
<Filter>tutorial</Filter>
</ClCompile>
<ClCompile Include="..\tutorial\test_async_yield_return.cpp">
<Filter>tutorial</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\librf\librf.h">
<Filter>librf</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\_awaker.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\asio_task.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\channel.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\def.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\event.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\future.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\generator.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\mutex.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\rf_task.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\scheduler.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\sleep.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\spinlock.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\state.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\timer.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\utils.h">
<Filter>librf\src</Filter>
</ClInclude>
<ClInclude Include="..\librf\src\unix\coroutine.h">
<Filter>librf\src\unix</Filter>
</ClInclude>
</ItemGroup>
</Project>

Loading…
Cancel
Save