feat add rxcpp
This commit is contained in:
272
3party/rxcpp/schedulers/rx-currentthread.hpp
Normal file
272
3party/rxcpp/schedulers/rx-currentthread.hpp
Normal file
@@ -0,0 +1,272 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_CURRENT_THREAD_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_CURRENT_THREAD_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct action_queue
|
||||
{
|
||||
typedef action_queue this_type;
|
||||
|
||||
typedef scheduler_base::clock_type clock;
|
||||
typedef time_schedulable<clock::time_point> item_type;
|
||||
|
||||
private:
|
||||
typedef schedulable_queue<item_type::time_point_type> queue_item_time;
|
||||
|
||||
public:
|
||||
struct current_thread_queue_type {
|
||||
std::shared_ptr<worker_interface> w;
|
||||
recursion r;
|
||||
queue_item_time q;
|
||||
};
|
||||
|
||||
private:
|
||||
#if defined(RXCPP_THREAD_LOCAL)
|
||||
static current_thread_queue_type*& current_thread_queue() {
|
||||
static RXCPP_THREAD_LOCAL current_thread_queue_type* q;
|
||||
return q;
|
||||
}
|
||||
#else
|
||||
static rxu::thread_local_storage<current_thread_queue_type>& current_thread_queue() {
|
||||
static rxu::thread_local_storage<current_thread_queue_type> q;
|
||||
return q;
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
static bool owned() {
|
||||
return !!current_thread_queue();
|
||||
}
|
||||
static const std::shared_ptr<worker_interface>& get_worker_interface() {
|
||||
return current_thread_queue()->w;
|
||||
}
|
||||
static recursion& get_recursion() {
|
||||
return current_thread_queue()->r;
|
||||
}
|
||||
static bool empty() {
|
||||
if (!current_thread_queue()) {
|
||||
std::terminate();
|
||||
}
|
||||
return current_thread_queue()->q.empty();
|
||||
}
|
||||
static queue_item_time::const_reference top() {
|
||||
if (!current_thread_queue()) {
|
||||
std::terminate();
|
||||
}
|
||||
return current_thread_queue()->q.top();
|
||||
}
|
||||
static void pop() {
|
||||
auto& state = current_thread_queue();
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
state->q.pop();
|
||||
if (state->q.empty()) {
|
||||
// allow recursion
|
||||
state->r.reset(true);
|
||||
}
|
||||
}
|
||||
static void push(item_type item) {
|
||||
auto& state = current_thread_queue();
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
if (!item.what.is_subscribed()) {
|
||||
return;
|
||||
}
|
||||
state->q.push(std::move(item));
|
||||
// disallow recursion
|
||||
state->r.reset(false);
|
||||
}
|
||||
static std::shared_ptr<worker_interface> ensure(std::shared_ptr<worker_interface> w) {
|
||||
if (!!current_thread_queue()) {
|
||||
std::terminate();
|
||||
}
|
||||
// create and publish new queue
|
||||
current_thread_queue() = new current_thread_queue_type();
|
||||
current_thread_queue()->w = w;
|
||||
return w;
|
||||
}
|
||||
static std::unique_ptr<current_thread_queue_type> create(std::shared_ptr<worker_interface> w) {
|
||||
std::unique_ptr<current_thread_queue_type> result(new current_thread_queue_type());
|
||||
result->w = std::move(w);
|
||||
return result;
|
||||
}
|
||||
static void set(current_thread_queue_type* q) {
|
||||
if (!!current_thread_queue()) {
|
||||
std::terminate();
|
||||
}
|
||||
// publish new queue
|
||||
current_thread_queue() = q;
|
||||
}
|
||||
static void destroy(current_thread_queue_type* q) {
|
||||
delete q;
|
||||
}
|
||||
static void destroy() {
|
||||
if (!current_thread_queue()) {
|
||||
std::terminate();
|
||||
}
|
||||
#if defined(RXCPP_THREAD_LOCAL)
|
||||
destroy(current_thread_queue());
|
||||
#else
|
||||
destroy(current_thread_queue().get());
|
||||
#endif
|
||||
current_thread_queue() = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct current_thread : public scheduler_interface
|
||||
{
|
||||
private:
|
||||
typedef current_thread this_type;
|
||||
current_thread(const this_type&);
|
||||
|
||||
typedef detail::action_queue queue_type;
|
||||
|
||||
struct derecurser : public worker_interface
|
||||
{
|
||||
private:
|
||||
typedef current_thread this_type;
|
||||
derecurser(const this_type&);
|
||||
public:
|
||||
derecurser()
|
||||
{
|
||||
}
|
||||
virtual ~derecurser()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
queue_type::push(queue_type::item_type(now(), scbl));
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
queue_type::push(queue_type::item_type(when, scbl));
|
||||
}
|
||||
};
|
||||
|
||||
struct current_worker : public worker_interface
|
||||
{
|
||||
private:
|
||||
typedef current_thread this_type;
|
||||
current_worker(const this_type&);
|
||||
public:
|
||||
current_worker()
|
||||
{
|
||||
}
|
||||
virtual ~current_worker()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
schedule(now(), scbl);
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
if (!scbl.is_subscribed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// check ownership
|
||||
if (queue_type::owned()) {
|
||||
// already has an owner - delegate
|
||||
queue_type::get_worker_interface()->schedule(when, scbl);
|
||||
return;
|
||||
}
|
||||
|
||||
// take ownership
|
||||
queue_type::ensure(std::make_shared<derecurser>());
|
||||
}
|
||||
// release ownership
|
||||
RXCPP_UNWIND_AUTO([]{
|
||||
queue_type::destroy();
|
||||
});
|
||||
|
||||
const auto& recursor = queue_type::get_recursion().get_recurse();
|
||||
std::this_thread::sleep_until(when);
|
||||
if (scbl.is_subscribed()) {
|
||||
scbl(recursor);
|
||||
}
|
||||
if (queue_type::empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop until queue is empty
|
||||
for (
|
||||
auto next = queue_type::top().when;
|
||||
(std::this_thread::sleep_until(next), true);
|
||||
next = queue_type::top().when
|
||||
) {
|
||||
auto what = queue_type::top().what;
|
||||
|
||||
queue_type::pop();
|
||||
|
||||
if (what.is_subscribed()) {
|
||||
what(recursor);
|
||||
}
|
||||
|
||||
if (queue_type::empty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<current_worker> wi;
|
||||
|
||||
public:
|
||||
current_thread()
|
||||
: wi(std::make_shared<current_worker>())
|
||||
{
|
||||
}
|
||||
virtual ~current_thread()
|
||||
{
|
||||
}
|
||||
|
||||
static bool is_schedule_required() { return !queue_type::owned(); }
|
||||
|
||||
inline bool is_tail_recursion_allowed() const {
|
||||
return queue_type::empty();
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
return worker(std::move(cs), wi);
|
||||
}
|
||||
};
|
||||
|
||||
inline const scheduler& make_current_thread() {
|
||||
static scheduler instance = make_scheduler<current_thread>();
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
118
3party/rxcpp/schedulers/rx-eventloop.hpp
Normal file
118
3party/rxcpp/schedulers/rx-eventloop.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_EVENT_LOOP_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_EVENT_LOOP_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
struct event_loop : public scheduler_interface
|
||||
{
|
||||
private:
|
||||
typedef event_loop this_type;
|
||||
event_loop(const this_type&);
|
||||
|
||||
struct loop_worker : public worker_interface
|
||||
{
|
||||
private:
|
||||
typedef loop_worker this_type;
|
||||
loop_worker(const this_type&);
|
||||
|
||||
typedef detail::schedulable_queue<
|
||||
typename clock_type::time_point> queue_item_time;
|
||||
|
||||
typedef queue_item_time::item_type item_type;
|
||||
|
||||
composite_subscription lifetime;
|
||||
worker controller;
|
||||
std::shared_ptr<const scheduler_interface> alive;
|
||||
|
||||
public:
|
||||
virtual ~loop_worker()
|
||||
{
|
||||
}
|
||||
loop_worker(composite_subscription cs, worker w, std::shared_ptr<const scheduler_interface> alive)
|
||||
: lifetime(cs)
|
||||
, controller(w)
|
||||
, alive(alive)
|
||||
{
|
||||
auto token = controller.add(cs);
|
||||
cs.add([token, w](){
|
||||
w.remove(token);
|
||||
});
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
controller.schedule(lifetime, scbl.get_action());
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
controller.schedule(when, lifetime, scbl.get_action());
|
||||
}
|
||||
};
|
||||
|
||||
mutable thread_factory factory;
|
||||
scheduler newthread;
|
||||
mutable std::atomic<std::size_t> count;
|
||||
composite_subscription loops_lifetime;
|
||||
std::vector<worker> loops;
|
||||
|
||||
public:
|
||||
event_loop()
|
||||
: factory([](std::function<void()> start){
|
||||
return std::thread(std::move(start));
|
||||
})
|
||||
, newthread(make_new_thread())
|
||||
, count(0)
|
||||
{
|
||||
auto remaining = std::max(std::thread::hardware_concurrency(), unsigned(4));
|
||||
while (remaining--) {
|
||||
loops.push_back(newthread.create_worker(loops_lifetime));
|
||||
}
|
||||
}
|
||||
explicit event_loop(thread_factory tf)
|
||||
: factory(tf)
|
||||
, newthread(make_new_thread(tf))
|
||||
, count(0)
|
||||
{
|
||||
auto remaining = std::max(std::thread::hardware_concurrency(), unsigned(4));
|
||||
while (remaining--) {
|
||||
loops.push_back(newthread.create_worker(loops_lifetime));
|
||||
}
|
||||
}
|
||||
virtual ~event_loop()
|
||||
{
|
||||
loops_lifetime.unsubscribe();
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
return worker(cs, std::make_shared<loop_worker>(cs, loops[++count % loops.size()], this->shared_from_this()));
|
||||
}
|
||||
};
|
||||
|
||||
inline scheduler make_event_loop() {
|
||||
static scheduler instance = make_scheduler<event_loop>();
|
||||
return instance;
|
||||
}
|
||||
inline scheduler make_event_loop(thread_factory tf) {
|
||||
return make_scheduler<event_loop>(tf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
84
3party/rxcpp/schedulers/rx-immediate.hpp
Normal file
84
3party/rxcpp/schedulers/rx-immediate.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_IMMEDIATE_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_IMMEDIATE_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
struct immediate : public scheduler_interface
|
||||
{
|
||||
private:
|
||||
typedef immediate this_type;
|
||||
immediate(const this_type&);
|
||||
|
||||
struct immediate_worker : public worker_interface
|
||||
{
|
||||
private:
|
||||
typedef immediate_worker this_type;
|
||||
immediate_worker(const this_type&);
|
||||
public:
|
||||
virtual ~immediate_worker()
|
||||
{
|
||||
}
|
||||
immediate_worker()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
if (scbl.is_subscribed()) {
|
||||
// allow recursion
|
||||
recursion r(true);
|
||||
scbl(r.get_recurse());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
std::this_thread::sleep_until(when);
|
||||
if (scbl.is_subscribed()) {
|
||||
// allow recursion
|
||||
recursion r(true);
|
||||
scbl(r.get_recurse());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<immediate_worker> wi;
|
||||
|
||||
public:
|
||||
immediate()
|
||||
: wi(std::make_shared<immediate_worker>())
|
||||
{
|
||||
}
|
||||
virtual ~immediate()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
return worker(std::move(cs), wi);
|
||||
}
|
||||
};
|
||||
|
||||
inline const scheduler& make_immediate() {
|
||||
static scheduler instance = make_scheduler<immediate>();
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
182
3party/rxcpp/schedulers/rx-newthread.hpp
Normal file
182
3party/rxcpp/schedulers/rx-newthread.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_NEW_THREAD_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_NEW_THREAD_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
typedef std::function<std::thread(std::function<void()>)> thread_factory;
|
||||
|
||||
struct new_thread : public scheduler_interface
|
||||
{
|
||||
private:
|
||||
typedef new_thread this_type;
|
||||
new_thread(const this_type&);
|
||||
|
||||
struct new_worker : public worker_interface
|
||||
{
|
||||
private:
|
||||
typedef new_worker this_type;
|
||||
|
||||
typedef detail::action_queue queue_type;
|
||||
|
||||
new_worker(const this_type&);
|
||||
|
||||
struct new_worker_state : public std::enable_shared_from_this<new_worker_state>
|
||||
{
|
||||
typedef detail::schedulable_queue<
|
||||
typename clock_type::time_point> queue_item_time;
|
||||
|
||||
typedef queue_item_time::item_type item_type;
|
||||
|
||||
virtual ~new_worker_state()
|
||||
{
|
||||
}
|
||||
|
||||
explicit new_worker_state(composite_subscription cs)
|
||||
: lifetime(cs)
|
||||
{
|
||||
}
|
||||
|
||||
composite_subscription lifetime;
|
||||
mutable std::mutex lock;
|
||||
mutable std::condition_variable wake;
|
||||
mutable queue_item_time q;
|
||||
std::thread worker;
|
||||
recursion r;
|
||||
};
|
||||
|
||||
std::shared_ptr<new_worker_state> state;
|
||||
|
||||
public:
|
||||
virtual ~new_worker()
|
||||
{
|
||||
}
|
||||
|
||||
explicit new_worker(std::shared_ptr<new_worker_state> ws)
|
||||
: state(ws)
|
||||
{
|
||||
}
|
||||
|
||||
new_worker(composite_subscription cs, thread_factory& tf)
|
||||
: state(std::make_shared<new_worker_state>(cs))
|
||||
{
|
||||
auto keepAlive = state;
|
||||
|
||||
state->lifetime.add([keepAlive](){
|
||||
std::unique_lock<std::mutex> guard(keepAlive->lock);
|
||||
auto expired = std::move(keepAlive->q);
|
||||
keepAlive->q = new_worker_state::queue_item_time{};
|
||||
if (!keepAlive->q.empty()) std::terminate();
|
||||
keepAlive->wake.notify_one();
|
||||
|
||||
if (keepAlive->worker.joinable() && keepAlive->worker.get_id() != std::this_thread::get_id()) {
|
||||
guard.unlock();
|
||||
keepAlive->worker.join();
|
||||
}
|
||||
else {
|
||||
keepAlive->worker.detach();
|
||||
}
|
||||
});
|
||||
|
||||
state->worker = tf([keepAlive](){
|
||||
|
||||
// take ownership
|
||||
queue_type::ensure(std::make_shared<new_worker>(keepAlive));
|
||||
// release ownership
|
||||
RXCPP_UNWIND_AUTO([]{
|
||||
queue_type::destroy();
|
||||
});
|
||||
|
||||
for(;;) {
|
||||
std::unique_lock<std::mutex> guard(keepAlive->lock);
|
||||
if (keepAlive->q.empty()) {
|
||||
keepAlive->wake.wait(guard, [keepAlive](){
|
||||
return !keepAlive->lifetime.is_subscribed() || !keepAlive->q.empty();
|
||||
});
|
||||
}
|
||||
if (!keepAlive->lifetime.is_subscribed()) {
|
||||
break;
|
||||
}
|
||||
auto& peek = keepAlive->q.top();
|
||||
if (!peek.what.is_subscribed()) {
|
||||
keepAlive->q.pop();
|
||||
continue;
|
||||
}
|
||||
auto when = peek.when;
|
||||
if (clock_type::now() < when) {
|
||||
keepAlive->wake.wait_until(guard, when);
|
||||
continue;
|
||||
}
|
||||
auto what = peek.what;
|
||||
keepAlive->q.pop();
|
||||
keepAlive->r.reset(keepAlive->q.empty());
|
||||
guard.unlock();
|
||||
what(keepAlive->r.get_recurse());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
schedule(now(), scbl);
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
if (scbl.is_subscribed()) {
|
||||
std::unique_lock<std::mutex> guard(state->lock);
|
||||
state->q.push(new_worker_state::item_type(when, scbl));
|
||||
state->r.reset(false);
|
||||
}
|
||||
state->wake.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
mutable thread_factory factory;
|
||||
|
||||
public:
|
||||
new_thread()
|
||||
: factory([](std::function<void()> start){
|
||||
return std::thread(std::move(start));
|
||||
})
|
||||
{
|
||||
}
|
||||
explicit new_thread(thread_factory tf)
|
||||
: factory(tf)
|
||||
{
|
||||
}
|
||||
virtual ~new_thread()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
return worker(cs, std::make_shared<new_worker>(cs, factory));
|
||||
}
|
||||
};
|
||||
|
||||
inline scheduler make_new_thread() {
|
||||
static scheduler instance = make_scheduler<new_thread>();
|
||||
return instance;
|
||||
}
|
||||
inline scheduler make_new_thread(thread_factory tf) {
|
||||
return make_scheduler<new_thread>(tf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
214
3party/rxcpp/schedulers/rx-runloop.hpp
Normal file
214
3party/rxcpp/schedulers/rx-runloop.hpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_RUN_LOOP_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_RUN_LOOP_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct run_loop_state : public std::enable_shared_from_this<run_loop_state>
|
||||
{
|
||||
typedef scheduler::clock_type clock_type;
|
||||
|
||||
typedef detail::schedulable_queue<
|
||||
clock_type::time_point> queue_item_time;
|
||||
|
||||
typedef queue_item_time::item_type item_type;
|
||||
typedef queue_item_time::const_reference const_reference_item_type;
|
||||
|
||||
virtual ~run_loop_state()
|
||||
{
|
||||
}
|
||||
|
||||
run_loop_state()
|
||||
{
|
||||
}
|
||||
|
||||
composite_subscription lifetime;
|
||||
mutable std::mutex lock;
|
||||
mutable queue_item_time q;
|
||||
recursion r;
|
||||
std::function<void(clock_type::time_point)> notify_earlier_wakeup;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct run_loop_scheduler : public scheduler_interface
|
||||
{
|
||||
private:
|
||||
typedef run_loop_scheduler this_type;
|
||||
run_loop_scheduler(const this_type&);
|
||||
|
||||
struct run_loop_worker : public worker_interface
|
||||
{
|
||||
private:
|
||||
typedef run_loop_worker this_type;
|
||||
|
||||
run_loop_worker(const this_type&);
|
||||
|
||||
public:
|
||||
std::weak_ptr<detail::run_loop_state> state;
|
||||
|
||||
virtual ~run_loop_worker()
|
||||
{
|
||||
}
|
||||
|
||||
explicit run_loop_worker(std::weak_ptr<detail::run_loop_state> ws)
|
||||
: state(ws)
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
schedule(now(), scbl);
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
if (scbl.is_subscribed()) {
|
||||
auto st = state.lock();
|
||||
std::unique_lock<std::mutex> guard(st->lock);
|
||||
const bool need_earlier_wakeup_notification = st->notify_earlier_wakeup &&
|
||||
(st->q.empty() || when < st->q.top().when);
|
||||
st->q.push(detail::run_loop_state::item_type(when, scbl));
|
||||
st->r.reset(false);
|
||||
if (need_earlier_wakeup_notification) st->notify_earlier_wakeup(when);
|
||||
guard.unlock(); // So we can't get attempt to recursively lock the state
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::weak_ptr<detail::run_loop_state> state;
|
||||
|
||||
public:
|
||||
explicit run_loop_scheduler(std::weak_ptr<detail::run_loop_state> ws)
|
||||
: state(ws)
|
||||
{
|
||||
}
|
||||
virtual ~run_loop_scheduler()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
auto lifetime = state.lock()->lifetime;
|
||||
auto token = lifetime.add(cs);
|
||||
cs.add([=](){lifetime.remove(token);});
|
||||
return worker(cs, create_worker_interface());
|
||||
}
|
||||
|
||||
std::shared_ptr<worker_interface> create_worker_interface() const {
|
||||
return std::make_shared<run_loop_worker>(state);
|
||||
}
|
||||
};
|
||||
|
||||
class run_loop
|
||||
{
|
||||
private:
|
||||
typedef run_loop this_type;
|
||||
// don't allow this instance to copy/move since it owns current_thread queue
|
||||
// for the thread it is constructed on.
|
||||
run_loop(const this_type&);
|
||||
run_loop(this_type&&);
|
||||
|
||||
typedef detail::action_queue queue_type;
|
||||
|
||||
typedef detail::run_loop_state::item_type item_type;
|
||||
typedef detail::run_loop_state::const_reference_item_type const_reference_item_type;
|
||||
|
||||
std::shared_ptr<detail::run_loop_state> state;
|
||||
std::shared_ptr<run_loop_scheduler> sc;
|
||||
|
||||
public:
|
||||
typedef scheduler::clock_type clock_type;
|
||||
run_loop()
|
||||
: state(std::make_shared<detail::run_loop_state>())
|
||||
, sc(std::make_shared<run_loop_scheduler>(state))
|
||||
{
|
||||
// take ownership so that the current_thread scheduler
|
||||
// uses the same queue on this thread
|
||||
queue_type::ensure(sc->create_worker_interface());
|
||||
}
|
||||
~run_loop()
|
||||
{
|
||||
state->lifetime.unsubscribe();
|
||||
|
||||
std::unique_lock<std::mutex> guard(state->lock);
|
||||
|
||||
// release ownership
|
||||
queue_type::destroy();
|
||||
|
||||
auto expired = std::move(state->q);
|
||||
if (!state->q.empty()) std::terminate();
|
||||
}
|
||||
|
||||
clock_type::time_point now() const {
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
composite_subscription get_subscription() const {
|
||||
return state->lifetime;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
std::unique_lock<std::mutex> guard(state->lock);
|
||||
return state->q.empty();
|
||||
}
|
||||
|
||||
const_reference_item_type peek() const {
|
||||
std::unique_lock<std::mutex> guard(state->lock);
|
||||
return state->q.top();
|
||||
}
|
||||
|
||||
void dispatch() const {
|
||||
std::unique_lock<std::mutex> guard(state->lock);
|
||||
if (state->q.empty()) {
|
||||
return;
|
||||
}
|
||||
auto& peek = state->q.top();
|
||||
if (!peek.what.is_subscribed()) {
|
||||
state->q.pop();
|
||||
return;
|
||||
}
|
||||
if (clock_type::now() < peek.when) {
|
||||
return;
|
||||
}
|
||||
auto what = peek.what;
|
||||
state->q.pop();
|
||||
state->r.reset(state->q.empty());
|
||||
guard.unlock();
|
||||
what(state->r.get_recurse());
|
||||
}
|
||||
|
||||
scheduler get_scheduler() const {
|
||||
return make_scheduler(sc);
|
||||
}
|
||||
|
||||
void set_notify_earlier_wakeup(std::function<void(clock_type::time_point)> const& f) {
|
||||
std::unique_lock<std::mutex> guard(state->lock);
|
||||
state->notify_earlier_wakeup = f;
|
||||
}
|
||||
};
|
||||
|
||||
inline scheduler make_run_loop(const run_loop& r) {
|
||||
return r.get_scheduler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
52
3party/rxcpp/schedulers/rx-sameworker.hpp
Normal file
52
3party/rxcpp/schedulers/rx-sameworker.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_SAME_WORKER_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_SAME_WORKER_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
struct same_worker : public scheduler_interface
|
||||
{
|
||||
private:
|
||||
typedef same_worker this_type;
|
||||
same_worker(const this_type&);
|
||||
|
||||
rxsc::worker controller;
|
||||
|
||||
public:
|
||||
explicit same_worker(rxsc::worker w)
|
||||
: controller(std::move(w))
|
||||
{
|
||||
}
|
||||
virtual ~same_worker()
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return controller.now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
// use different lifetime
|
||||
auto inner_lifetime = controller.get_subscription();
|
||||
auto token = inner_lifetime.add(cs);
|
||||
cs.add([inner_lifetime, token](){inner_lifetime.remove(token);});
|
||||
return worker(cs, controller);
|
||||
}
|
||||
};
|
||||
|
||||
inline scheduler make_same_worker(rxsc::worker w) {
|
||||
return make_scheduler<same_worker>(std::move(w));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
617
3party/rxcpp/schedulers/rx-test.hpp
Normal file
617
3party/rxcpp/schedulers/rx-test.hpp
Normal file
@@ -0,0 +1,617 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_TEST_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_TEST_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class test_type : public scheduler_interface
|
||||
{
|
||||
public:
|
||||
|
||||
typedef scheduler_interface::clock_type clock_type;
|
||||
|
||||
struct test_type_state : public virtual_time<long, long>
|
||||
{
|
||||
typedef virtual_time<long, long> base;
|
||||
|
||||
using base::schedule_absolute;
|
||||
using base::schedule_relative;
|
||||
|
||||
clock_type::time_point now() const {
|
||||
return to_time_point(clock_now);
|
||||
}
|
||||
|
||||
virtual void schedule_absolute(long when, const schedulable& a) const
|
||||
{
|
||||
if (when <= base::clock_now)
|
||||
when = base::clock_now + 1;
|
||||
|
||||
return base::schedule_absolute(when, a);
|
||||
}
|
||||
|
||||
virtual long add(long absolute, long relative) const
|
||||
{
|
||||
return absolute + relative;
|
||||
}
|
||||
|
||||
virtual clock_type::time_point to_time_point(long absolute) const
|
||||
{
|
||||
return clock_type::time_point(std::chrono::milliseconds(absolute));
|
||||
}
|
||||
|
||||
virtual long to_relative(clock_type::duration d) const
|
||||
{
|
||||
return static_cast<long>(std::chrono::duration_cast<std::chrono::milliseconds>(d).count());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
mutable std::shared_ptr<test_type_state> state;
|
||||
|
||||
public:
|
||||
struct test_type_worker : public worker_interface
|
||||
{
|
||||
mutable std::shared_ptr<test_type_state> state;
|
||||
|
||||
typedef test_type_state::absolute absolute;
|
||||
typedef test_type_state::relative relative;
|
||||
|
||||
test_type_worker(std::shared_ptr<test_type_state> st)
|
||||
: state(std::move(st))
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return state->now();
|
||||
}
|
||||
|
||||
virtual void schedule(const schedulable& scbl) const {
|
||||
state->schedule_absolute(state->clock(), scbl);
|
||||
}
|
||||
|
||||
virtual void schedule(clock_type::time_point when, const schedulable& scbl) const {
|
||||
state->schedule_relative(state->to_relative(when - now()), scbl);
|
||||
}
|
||||
|
||||
void schedule_absolute(absolute when, const schedulable& scbl) const {
|
||||
state->schedule_absolute(when, scbl);
|
||||
}
|
||||
|
||||
void schedule_relative(relative when, const schedulable& scbl) const {
|
||||
state->schedule_relative(when, scbl);
|
||||
}
|
||||
|
||||
bool is_enabled() const {return state->is_enabled();}
|
||||
absolute clock() const {return state->clock();}
|
||||
|
||||
void start() const
|
||||
{
|
||||
state->start();
|
||||
}
|
||||
|
||||
void stop() const
|
||||
{
|
||||
state->stop();
|
||||
}
|
||||
|
||||
void advance_to(absolute time) const
|
||||
{
|
||||
state->advance_to(time);
|
||||
}
|
||||
|
||||
void advance_by(relative time) const
|
||||
{
|
||||
state->advance_by(time);
|
||||
}
|
||||
|
||||
void sleep(relative time) const
|
||||
{
|
||||
state->sleep(time);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
subscriber<T, rxt::testable_observer<T>> make_subscriber() const;
|
||||
};
|
||||
|
||||
public:
|
||||
test_type()
|
||||
: state(std::make_shared<test_type_state>())
|
||||
{
|
||||
}
|
||||
|
||||
virtual clock_type::time_point now() const {
|
||||
return state->now();
|
||||
}
|
||||
|
||||
virtual worker create_worker(composite_subscription cs) const {
|
||||
return worker(cs, std::make_shared<test_type_worker>(state));
|
||||
}
|
||||
|
||||
bool is_enabled() const {return state->is_enabled();}
|
||||
long clock() {
|
||||
return state->clock();
|
||||
}
|
||||
|
||||
clock_type::time_point to_time_point(long absolute) const {
|
||||
return state->to_time_point(absolute);
|
||||
}
|
||||
|
||||
std::shared_ptr<test_type_worker> create_test_type_worker_interface() const {
|
||||
return std::make_shared<test_type_worker>(state);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
rxt::testable_observable<T> make_hot_observable(std::vector<rxn::recorded<std::shared_ptr<rxn::detail::notification_base<T>>>> messages) const;
|
||||
|
||||
template<class T>
|
||||
rxt::testable_observable<T> make_cold_observable(std::vector<rxn::recorded<std::shared_ptr<rxn::detail::notification_base<T>>>> messages) const;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class mock_observer
|
||||
: public rxt::detail::test_subject_base<T>
|
||||
{
|
||||
typedef typename rxn::notification<T> notification_type;
|
||||
typedef rxn::recorded<typename notification_type::type> recorded_type;
|
||||
|
||||
public:
|
||||
explicit mock_observer(std::shared_ptr<test_type::test_type_state> sc)
|
||||
: sc(sc)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<test_type::test_type_state> sc;
|
||||
std::vector<recorded_type> m;
|
||||
|
||||
virtual void on_subscribe(subscriber<T>) const {
|
||||
std::terminate();
|
||||
}
|
||||
virtual std::vector<rxn::subscription> subscriptions() const {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
virtual std::vector<recorded_type> messages() const {
|
||||
return m;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
subscriber<T, rxt::testable_observer<T>> test_type::test_type_worker::make_subscriber() const
|
||||
{
|
||||
typedef typename rxn::notification<T> notification_type;
|
||||
typedef rxn::recorded<typename notification_type::type> recorded_type;
|
||||
|
||||
auto ts = std::make_shared<mock_observer<T>>(state);
|
||||
|
||||
return rxcpp::make_subscriber<T>(rxt::testable_observer<T>(ts, make_observer_dynamic<T>(
|
||||
// on_next
|
||||
[ts](T value)
|
||||
{
|
||||
ts->m.push_back(
|
||||
recorded_type(ts->sc->clock(), notification_type::on_next(value)));
|
||||
},
|
||||
// on_error
|
||||
[ts](rxu::error_ptr e)
|
||||
{
|
||||
ts->m.push_back(
|
||||
recorded_type(ts->sc->clock(), notification_type::on_error(e)));
|
||||
},
|
||||
// on_completed
|
||||
[ts]()
|
||||
{
|
||||
ts->m.push_back(
|
||||
recorded_type(ts->sc->clock(), notification_type::on_completed()));
|
||||
})));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
class cold_observable
|
||||
: public rxt::detail::test_subject_base<T>
|
||||
{
|
||||
typedef cold_observable<T> this_type;
|
||||
std::shared_ptr<test_type::test_type_state> sc;
|
||||
typedef rxn::recorded<typename rxn::notification<T>::type> recorded_type;
|
||||
mutable std::vector<recorded_type> mv;
|
||||
mutable std::vector<rxn::subscription> sv;
|
||||
mutable worker controller;
|
||||
|
||||
public:
|
||||
|
||||
cold_observable(std::shared_ptr<test_type::test_type_state> sc, worker w, std::vector<recorded_type> mv)
|
||||
: sc(sc)
|
||||
, mv(std::move(mv))
|
||||
, controller(w)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Iterator>
|
||||
cold_observable(std::shared_ptr<test_type::test_type_state> sc, worker w, Iterator begin, Iterator end)
|
||||
: sc(sc)
|
||||
, mv(begin, end)
|
||||
, controller(w)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_subscribe(subscriber<T> o) const {
|
||||
sv.push_back(rxn::subscription(sc->clock()));
|
||||
auto index = sv.size() - 1;
|
||||
|
||||
for (auto& message : mv) {
|
||||
auto n = message.value();
|
||||
sc->schedule_relative(message.time(), make_schedulable(
|
||||
controller,
|
||||
[n, o](const schedulable&) {
|
||||
if (o.is_subscribed()) {
|
||||
n->accept(o);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
auto sharedThis = std::static_pointer_cast<const this_type>(this->shared_from_this());
|
||||
o.add([sharedThis, index]() {
|
||||
sharedThis->sv[index] = rxn::subscription(sharedThis->sv[index].subscribe(), sharedThis->sc->clock());
|
||||
});
|
||||
}
|
||||
|
||||
virtual std::vector<rxn::subscription> subscriptions() const {
|
||||
return sv;
|
||||
}
|
||||
|
||||
virtual std::vector<recorded_type> messages() const {
|
||||
return mv;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
rxt::testable_observable<T> test_type::make_cold_observable(std::vector<rxn::recorded<std::shared_ptr<rxn::detail::notification_base<T>>>> messages) const
|
||||
{
|
||||
auto co = std::make_shared<cold_observable<T>>(state, create_worker(composite_subscription()), std::move(messages));
|
||||
return rxt::testable_observable<T>(co);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
class hot_observable
|
||||
: public rxt::detail::test_subject_base<T>
|
||||
{
|
||||
typedef hot_observable<T> this_type;
|
||||
std::shared_ptr<test_type::test_type_state> sc;
|
||||
typedef rxn::recorded<typename rxn::notification<T>::type> recorded_type;
|
||||
typedef subscriber<T> observer_type;
|
||||
mutable std::vector<recorded_type> mv;
|
||||
mutable std::vector<rxn::subscription> sv;
|
||||
mutable std::list<observer_type> observers;
|
||||
mutable worker controller;
|
||||
|
||||
public:
|
||||
|
||||
hot_observable(std::shared_ptr<test_type::test_type_state> sc, worker w, std::vector<recorded_type> mv)
|
||||
: sc(sc)
|
||||
, mv(mv)
|
||||
, controller(w)
|
||||
{
|
||||
for (auto& message : mv) {
|
||||
auto n = message.value();
|
||||
sc->schedule_absolute(message.time(), make_schedulable(
|
||||
controller,
|
||||
[this, n](const schedulable&) {
|
||||
auto local = this->observers;
|
||||
for (auto& o : local) {
|
||||
if (o.is_subscribed()) {
|
||||
n->accept(o);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~hot_observable() {}
|
||||
|
||||
virtual void on_subscribe(observer_type o) const {
|
||||
auto olocation = observers.insert(observers.end(), o);
|
||||
|
||||
sv.push_back(rxn::subscription(sc->clock()));
|
||||
auto index = sv.size() - 1;
|
||||
|
||||
auto sharedThis = std::static_pointer_cast<const this_type>(this->shared_from_this());
|
||||
o.add([sharedThis, index, olocation]() {
|
||||
sharedThis->sv[index] = rxn::subscription(sharedThis->sv[index].subscribe(), sharedThis->sc->clock());
|
||||
sharedThis->observers.erase(olocation);
|
||||
});
|
||||
}
|
||||
|
||||
virtual std::vector<rxn::subscription> subscriptions() const {
|
||||
return sv;
|
||||
}
|
||||
|
||||
virtual std::vector<recorded_type> messages() const {
|
||||
return mv;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
rxt::testable_observable<T> test_type::make_hot_observable(std::vector<rxn::recorded<std::shared_ptr<rxn::detail::notification_base<T>>>> messages) const
|
||||
{
|
||||
auto worker = create_worker(composite_subscription());
|
||||
auto shared = std::make_shared<hot_observable<T>>(state, worker, std::move(messages));
|
||||
return rxt::testable_observable<T>(shared);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
struct is_create_source_function
|
||||
{
|
||||
struct not_void {};
|
||||
template<class CF>
|
||||
static auto check(int) -> decltype((*(CF*)nullptr)());
|
||||
template<class CF>
|
||||
static not_void check(...);
|
||||
|
||||
static const bool value = is_observable<decltype(check<rxu::decay_t<F>>(0))>::value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class test : public scheduler
|
||||
{
|
||||
std::shared_ptr<detail::test_type> tester;
|
||||
public:
|
||||
|
||||
explicit test(std::shared_ptr<detail::test_type> t)
|
||||
: scheduler(std::static_pointer_cast<scheduler_interface>(t))
|
||||
, tester(t)
|
||||
{
|
||||
}
|
||||
|
||||
typedef detail::test_type::clock_type clock_type;
|
||||
|
||||
static const long created_time = 100;
|
||||
static const long subscribed_time = 200;
|
||||
static const long unsubscribed_time = 1000;
|
||||
|
||||
template<class T>
|
||||
struct messages
|
||||
{
|
||||
typedef typename rxn::notification<T> notification_type;
|
||||
typedef rxn::recorded<typename notification_type::type> recorded_type;
|
||||
typedef rxn::subscription subscription_type;
|
||||
|
||||
messages() {}
|
||||
|
||||
template<typename U>
|
||||
static recorded_type next(long ticks, U value) {
|
||||
return recorded_type(ticks, notification_type::on_next(std::move(value)));
|
||||
}
|
||||
|
||||
static recorded_type completed(long ticks) {
|
||||
return recorded_type(ticks, notification_type::on_completed());
|
||||
}
|
||||
|
||||
template<typename Exception>
|
||||
static recorded_type error(long ticks, Exception&& e) {
|
||||
return recorded_type(ticks, notification_type::on_error(std::forward<Exception>(e)));
|
||||
}
|
||||
|
||||
static rxn::subscription subscribe(long subscribe, long unsubscribe) {
|
||||
return rxn::subscription(subscribe, unsubscribe);
|
||||
}
|
||||
};
|
||||
|
||||
class test_worker : public worker
|
||||
{
|
||||
std::shared_ptr<detail::test_type::test_type_worker> tester;
|
||||
public:
|
||||
|
||||
~test_worker() {
|
||||
}
|
||||
|
||||
explicit test_worker(composite_subscription cs, std::shared_ptr<detail::test_type::test_type_worker> t)
|
||||
: worker(cs, std::static_pointer_cast<worker_interface>(t))
|
||||
, tester(t)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_enabled() const {return tester->is_enabled();}
|
||||
long clock() const {return tester->clock();}
|
||||
|
||||
void schedule_absolute(long when, const schedulable& a) const {
|
||||
tester->schedule_absolute(when, a);
|
||||
}
|
||||
|
||||
void schedule_relative(long when, const schedulable& a) const {
|
||||
tester->schedule_relative(when, a);
|
||||
}
|
||||
|
||||
template<class Arg0, class... ArgN>
|
||||
auto schedule_absolute(long when, Arg0&& a0, ArgN&&... an) const
|
||||
-> typename std::enable_if<
|
||||
(detail::is_action_function<Arg0>::value ||
|
||||
is_subscription<Arg0>::value) &&
|
||||
!is_schedulable<Arg0>::value>::type {
|
||||
tester->schedule_absolute(when, make_schedulable(*this, std::forward<Arg0>(a0), std::forward<ArgN>(an)...));
|
||||
}
|
||||
|
||||
template<class Arg0, class... ArgN>
|
||||
auto schedule_relative(long when, Arg0&& a0, ArgN&&... an) const
|
||||
-> typename std::enable_if<
|
||||
(detail::is_action_function<Arg0>::value ||
|
||||
is_subscription<Arg0>::value) &&
|
||||
!is_schedulable<Arg0>::value>::type {
|
||||
tester->schedule_relative(when, make_schedulable(*this, std::forward<Arg0>(a0), std::forward<ArgN>(an)...));
|
||||
}
|
||||
|
||||
void advance_to(long time) const
|
||||
{
|
||||
tester->advance_to(time);
|
||||
}
|
||||
|
||||
void advance_by(long time) const
|
||||
{
|
||||
tester->advance_by(time);
|
||||
}
|
||||
|
||||
void sleep(long time) const
|
||||
{
|
||||
tester->sleep(time);
|
||||
}
|
||||
|
||||
template<class T, class F>
|
||||
auto start(F createSource, long created, long subscribed, long unsubscribed) const
|
||||
-> subscriber<T, rxt::testable_observer<T>>
|
||||
{
|
||||
struct state_type
|
||||
: public std::enable_shared_from_this<state_type>
|
||||
{
|
||||
typedef decltype(createSource()) source_type;
|
||||
|
||||
std::unique_ptr<source_type> source;
|
||||
subscriber<T, rxt::testable_observer<T>> o;
|
||||
|
||||
explicit state_type(subscriber<T, rxt::testable_observer<T>> o)
|
||||
: source()
|
||||
, o(o)
|
||||
{
|
||||
}
|
||||
};
|
||||
auto state = std::make_shared<state_type>(this->make_subscriber<T>());
|
||||
|
||||
schedule_absolute(created, [createSource, state](const schedulable&) {
|
||||
state->source.reset(new typename state_type::source_type(createSource()));
|
||||
});
|
||||
schedule_absolute(subscribed, [state](const schedulable&) {
|
||||
state->source->subscribe(state->o);
|
||||
});
|
||||
schedule_absolute(unsubscribed, [state](const schedulable&) {
|
||||
state->o.unsubscribe();
|
||||
});
|
||||
|
||||
tester->start();
|
||||
|
||||
return state->o;
|
||||
}
|
||||
|
||||
template<class T, class F>
|
||||
auto start(F&& createSource, long unsubscribed) const
|
||||
-> subscriber<T, rxt::testable_observer<T>>
|
||||
{
|
||||
return start<T>(std::forward<F>(createSource), created_time, subscribed_time, unsubscribed);
|
||||
}
|
||||
|
||||
template<class T, class F>
|
||||
auto start(F&& createSource) const
|
||||
-> subscriber<T, rxt::testable_observer<T>>
|
||||
{
|
||||
return start<T>(std::forward<F>(createSource), created_time, subscribed_time, unsubscribed_time);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
struct start_traits
|
||||
{
|
||||
typedef decltype((*(F*)nullptr)()) source_type;
|
||||
typedef typename source_type::value_type value_type;
|
||||
typedef subscriber<value_type, rxt::testable_observer<value_type>> subscriber_type;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
auto start(F createSource, long created, long subscribed, long unsubscribed) const
|
||||
-> typename std::enable_if<detail::is_create_source_function<F>::value, start_traits<F>>::type::subscriber_type
|
||||
{
|
||||
return start<rxu::value_type_t<start_traits<F>>>(std::move(createSource), created, subscribed, unsubscribed);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
auto start(F createSource, long unsubscribed) const
|
||||
-> typename std::enable_if<detail::is_create_source_function<F>::value, start_traits<F>>::type::subscriber_type
|
||||
{
|
||||
return start<rxu::value_type_t<start_traits<F>>>(std::move(createSource), created_time, subscribed_time, unsubscribed);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
auto start(F createSource) const
|
||||
-> typename std::enable_if<detail::is_create_source_function<F>::value, start_traits<F>>::type::subscriber_type
|
||||
{
|
||||
return start<rxu::value_type_t<start_traits<F>>>(std::move(createSource), created_time, subscribed_time, unsubscribed_time);
|
||||
}
|
||||
|
||||
void start() const {
|
||||
tester->start();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
subscriber<T, rxt::testable_observer<T>> make_subscriber() const {
|
||||
return tester->make_subscriber<T>();
|
||||
}
|
||||
};
|
||||
|
||||
clock_type::time_point now() const {
|
||||
return tester->now();
|
||||
}
|
||||
|
||||
test_worker create_worker(composite_subscription cs = composite_subscription()) const {
|
||||
return test_worker(cs, tester->create_test_type_worker_interface());
|
||||
}
|
||||
|
||||
bool is_enabled() const {return tester->is_enabled();}
|
||||
long clock() const {return tester->clock();}
|
||||
|
||||
clock_type::time_point to_time_point(long absolute) const {
|
||||
return tester->to_time_point(absolute);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
rxt::testable_observable<T> make_hot_observable(std::vector<rxn::recorded<std::shared_ptr<rxn::detail::notification_base<T>>>> messages) const{
|
||||
return tester->make_hot_observable(std::move(messages));
|
||||
}
|
||||
|
||||
template<class T, std::size_t size>
|
||||
auto make_hot_observable(const T (&arr) [size]) const
|
||||
-> decltype(tester->make_hot_observable(std::vector<T>())) {
|
||||
return tester->make_hot_observable(rxu::to_vector(arr));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto make_hot_observable(std::initializer_list<T> il) const
|
||||
-> decltype(tester->make_hot_observable(std::vector<T>())) {
|
||||
return tester->make_hot_observable(std::vector<T>(il));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
rxt::testable_observable<T> make_cold_observable(std::vector<rxn::recorded<std::shared_ptr<rxn::detail::notification_base<T>>>> messages) const {
|
||||
return tester->make_cold_observable(std::move(messages));
|
||||
}
|
||||
|
||||
template<class T, std::size_t size>
|
||||
auto make_cold_observable(const T (&arr) [size]) const
|
||||
-> decltype(tester->make_cold_observable(std::vector<T>())) {
|
||||
return tester->make_cold_observable(rxu::to_vector(arr));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto make_cold_observable(std::initializer_list<T> il) const
|
||||
-> decltype(tester->make_cold_observable(std::vector<T>())) {
|
||||
return tester->make_cold_observable(std::vector<T>(il));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline test make_test() {
|
||||
return test(std::make_shared<detail::test_type>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline identity_one_worker identity_test() {
|
||||
static identity_one_worker r(rxsc::make_test());
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
230
3party/rxcpp/schedulers/rx-virtualtime.hpp
Normal file
230
3party/rxcpp/schedulers/rx-virtualtime.hpp
Normal file
@@ -0,0 +1,230 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(RXCPP_RX_SCHEDULER_VIRTUAL_TIME_HPP)
|
||||
#define RXCPP_RX_SCHEDULER_VIRTUAL_TIME_HPP
|
||||
|
||||
#include "../rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace schedulers {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Absolute, class Relative>
|
||||
struct virtual_time_base : std::enable_shared_from_this<virtual_time_base<Absolute, Relative>>
|
||||
{
|
||||
private:
|
||||
typedef virtual_time_base<Absolute, Relative> this_type;
|
||||
virtual_time_base(const virtual_time_base&);
|
||||
|
||||
mutable bool isenabled;
|
||||
|
||||
public:
|
||||
typedef Absolute absolute;
|
||||
typedef Relative relative;
|
||||
|
||||
virtual ~virtual_time_base()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual_time_base()
|
||||
: isenabled(false)
|
||||
, clock_now(0)
|
||||
{
|
||||
}
|
||||
explicit virtual_time_base(absolute initialClock)
|
||||
: isenabled(false)
|
||||
, clock_now(initialClock)
|
||||
{
|
||||
}
|
||||
|
||||
mutable absolute clock_now;
|
||||
|
||||
typedef time_schedulable<long> item_type;
|
||||
|
||||
virtual absolute add(absolute, relative) const =0;
|
||||
|
||||
virtual typename scheduler_base::clock_type::time_point to_time_point(absolute) const =0;
|
||||
virtual relative to_relative(typename scheduler_base::clock_type::duration) const =0;
|
||||
|
||||
virtual item_type top() const =0;
|
||||
virtual void pop() const =0;
|
||||
virtual bool empty() const =0;
|
||||
|
||||
public:
|
||||
|
||||
virtual void schedule_absolute(absolute, const schedulable&) const =0;
|
||||
|
||||
virtual void schedule_relative(relative when, const schedulable& a) const {
|
||||
auto at = add(clock_now, when);
|
||||
return schedule_absolute(at, a);
|
||||
}
|
||||
|
||||
bool is_enabled() const {return isenabled;}
|
||||
absolute clock() const {return clock_now;}
|
||||
|
||||
void start() const
|
||||
{
|
||||
if (!isenabled) {
|
||||
isenabled = true;
|
||||
rxsc::recursion r;
|
||||
r.reset(false);
|
||||
while (!empty() && isenabled) {
|
||||
auto next = top();
|
||||
pop();
|
||||
if (next.what.is_subscribed()) {
|
||||
if (next.when > clock_now) {
|
||||
clock_now = next.when;
|
||||
}
|
||||
next.what(r.get_recurse());
|
||||
}
|
||||
}
|
||||
isenabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void stop() const
|
||||
{
|
||||
isenabled = false;
|
||||
}
|
||||
|
||||
void advance_to(absolute time) const
|
||||
{
|
||||
if (time < clock_now) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
if (time == clock_now) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isenabled) {
|
||||
isenabled = true;
|
||||
rxsc::recursion r;
|
||||
while (!empty() && isenabled) {
|
||||
auto next = top();
|
||||
if (next.when <= time) {
|
||||
pop();
|
||||
if (!next.what.is_subscribed()) {
|
||||
continue;
|
||||
}
|
||||
if (next.when > clock_now) {
|
||||
clock_now = next.when;
|
||||
}
|
||||
next.what(r.get_recurse());
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
isenabled = false;
|
||||
clock_now = time;
|
||||
}
|
||||
else {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void advance_by(relative time) const
|
||||
{
|
||||
auto dt = add(clock_now, time);
|
||||
|
||||
if (dt < clock_now) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
if (dt == clock_now) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isenabled) {
|
||||
advance_to(dt);
|
||||
}
|
||||
else {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void sleep(relative time) const
|
||||
{
|
||||
auto dt = add(clock_now, time);
|
||||
|
||||
if (dt < clock_now) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
clock_now = dt;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<class Absolute, class Relative>
|
||||
struct virtual_time : public detail::virtual_time_base<Absolute, Relative>
|
||||
{
|
||||
typedef detail::virtual_time_base<Absolute, Relative> base;
|
||||
|
||||
typedef typename base::item_type item_type;
|
||||
|
||||
typedef detail::schedulable_queue<
|
||||
typename item_type::time_point_type> queue_item_time;
|
||||
|
||||
mutable queue_item_time q;
|
||||
|
||||
public:
|
||||
virtual ~virtual_time()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual_time()
|
||||
{
|
||||
}
|
||||
explicit virtual_time(typename base::absolute initialClock)
|
||||
: base(initialClock)
|
||||
{
|
||||
}
|
||||
|
||||
virtual item_type top() const {
|
||||
return q.top();
|
||||
}
|
||||
virtual void pop() const {
|
||||
q.pop();
|
||||
}
|
||||
virtual bool empty() const {
|
||||
return q.empty();
|
||||
}
|
||||
|
||||
using base::schedule_absolute;
|
||||
using base::schedule_relative;
|
||||
|
||||
virtual void schedule_absolute(typename base::absolute when, const schedulable& a) const
|
||||
{
|
||||
// use a separate subscription here so that a's subscription is not affected
|
||||
auto run = make_schedulable(
|
||||
a.get_worker(),
|
||||
composite_subscription(),
|
||||
[a](const schedulable& scbl) {
|
||||
rxsc::recursion r;
|
||||
r.reset(false);
|
||||
if (scbl.is_subscribed()) {
|
||||
scbl.unsubscribe(); // unsubscribe() run, not a;
|
||||
a(r.get_recurse());
|
||||
}
|
||||
});
|
||||
q.push(item_type(when, run));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user