feat add rxcpp
This commit is contained in:
613
3party/rxcpp/rx-subscription.hpp
Normal file
613
3party/rxcpp/rx-subscription.hpp
Normal file
@@ -0,0 +1,613 @@
|
||||
// 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_SUBSCRIPTION_HPP)
|
||||
#define RXCPP_RX_SUBSCRIPTION_HPP
|
||||
|
||||
#include "rx-includes.hpp"
|
||||
|
||||
namespace rxcpp {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class F>
|
||||
struct is_unsubscribe_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 = std::is_same<decltype(check<rxu::decay_t<F>>(0)), void>::value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct tag_subscription {};
|
||||
struct subscription_base {typedef tag_subscription subscription_tag;};
|
||||
template<class T>
|
||||
class is_subscription
|
||||
{
|
||||
template<class C>
|
||||
static typename C::subscription_tag* check(int);
|
||||
template<class C>
|
||||
static void check(...);
|
||||
public:
|
||||
static const bool value = std::is_convertible<decltype(check<rxu::decay_t<T>>(0)), tag_subscription*>::value;
|
||||
};
|
||||
|
||||
template<class Unsubscribe>
|
||||
class static_subscription
|
||||
{
|
||||
typedef rxu::decay_t<Unsubscribe> unsubscribe_call_type;
|
||||
unsubscribe_call_type unsubscribe_call;
|
||||
static_subscription()
|
||||
{
|
||||
}
|
||||
public:
|
||||
static_subscription(const static_subscription& o)
|
||||
: unsubscribe_call(o.unsubscribe_call)
|
||||
{
|
||||
}
|
||||
static_subscription(static_subscription&& o)
|
||||
: unsubscribe_call(std::move(o.unsubscribe_call))
|
||||
{
|
||||
}
|
||||
static_subscription(unsubscribe_call_type s)
|
||||
: unsubscribe_call(std::move(s))
|
||||
{
|
||||
}
|
||||
void unsubscribe() const {
|
||||
unsubscribe_call();
|
||||
}
|
||||
};
|
||||
|
||||
class subscription : public subscription_base
|
||||
{
|
||||
class base_subscription_state : public std::enable_shared_from_this<base_subscription_state>
|
||||
{
|
||||
base_subscription_state();
|
||||
public:
|
||||
|
||||
explicit base_subscription_state(bool initial)
|
||||
: issubscribed(initial)
|
||||
{
|
||||
}
|
||||
virtual ~base_subscription_state() {}
|
||||
virtual void unsubscribe() {
|
||||
}
|
||||
std::atomic<bool> issubscribed;
|
||||
};
|
||||
public:
|
||||
typedef std::weak_ptr<base_subscription_state> weak_state_type;
|
||||
|
||||
private:
|
||||
template<class I>
|
||||
struct subscription_state : public base_subscription_state
|
||||
{
|
||||
typedef rxu::decay_t<I> inner_t;
|
||||
subscription_state(inner_t i)
|
||||
: base_subscription_state(true)
|
||||
, inner(std::move(i))
|
||||
{
|
||||
}
|
||||
virtual void unsubscribe() {
|
||||
if (issubscribed.exchange(false)) {
|
||||
trace_activity().unsubscribe_enter(*this);
|
||||
inner.unsubscribe();
|
||||
trace_activity().unsubscribe_return(*this);
|
||||
}
|
||||
}
|
||||
inner_t inner;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::shared_ptr<base_subscription_state> state;
|
||||
|
||||
friend bool operator<(const subscription&, const subscription&);
|
||||
friend bool operator==(const subscription&, const subscription&);
|
||||
|
||||
private:
|
||||
subscription(weak_state_type w)
|
||||
: state(w.lock())
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
explicit subscription(std::shared_ptr<base_subscription_state> s)
|
||||
: state(std::move(s))
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
public:
|
||||
|
||||
subscription()
|
||||
: state(std::make_shared<base_subscription_state>(false))
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
template<class U>
|
||||
explicit subscription(U u, typename std::enable_if<!is_subscription<U>::value, void**>::type = nullptr)
|
||||
: state(std::make_shared<subscription_state<U>>(std::move(u)))
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
template<class U>
|
||||
explicit subscription(U u, typename std::enable_if<!std::is_same<subscription, U>::value && is_subscription<U>::value, void**>::type = nullptr)
|
||||
// intentionally slice
|
||||
: state(std::move((*static_cast<subscription*>(&u)).state))
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
subscription(const subscription& o)
|
||||
: state(o.state)
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
subscription(subscription&& o)
|
||||
: state(std::move(o.state))
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
subscription& operator=(subscription o) {
|
||||
state = std::move(o.state);
|
||||
return *this;
|
||||
}
|
||||
bool is_subscribed() const {
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
return state->issubscribed;
|
||||
}
|
||||
void unsubscribe() const {
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
auto keepAlive = state;
|
||||
state->unsubscribe();
|
||||
}
|
||||
|
||||
weak_state_type get_weak() {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Atomically promote weak subscription to strong.
|
||||
// Calls std::terminate if w has already expired.
|
||||
static subscription lock(weak_state_type w) {
|
||||
return subscription(w);
|
||||
}
|
||||
|
||||
// Atomically try to promote weak subscription to strong.
|
||||
// Returns an empty maybe<> if w has already expired.
|
||||
static rxu::maybe<subscription> maybe_lock(weak_state_type w) {
|
||||
auto strong_subscription = w.lock();
|
||||
if (!strong_subscription) {
|
||||
return rxu::detail::maybe<subscription>{};
|
||||
} else {
|
||||
return rxu::detail::maybe<subscription>{subscription{std::move(strong_subscription)}};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const subscription& lhs, const subscription& rhs) {
|
||||
return lhs.state < rhs.state;
|
||||
}
|
||||
inline bool operator==(const subscription& lhs, const subscription& rhs) {
|
||||
return lhs.state == rhs.state;
|
||||
}
|
||||
inline bool operator!=(const subscription& lhs, const subscription& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
inline auto make_subscription()
|
||||
-> subscription {
|
||||
return subscription();
|
||||
}
|
||||
template<class I>
|
||||
auto make_subscription(I&& i)
|
||||
-> typename std::enable_if<!is_subscription<I>::value && !detail::is_unsubscribe_function<I>::value,
|
||||
subscription>::type {
|
||||
return subscription(std::forward<I>(i));
|
||||
}
|
||||
template<class Unsubscribe>
|
||||
auto make_subscription(Unsubscribe&& u)
|
||||
-> typename std::enable_if<detail::is_unsubscribe_function<Unsubscribe>::value,
|
||||
subscription>::type {
|
||||
return subscription(static_subscription<Unsubscribe>(std::forward<Unsubscribe>(u)));
|
||||
}
|
||||
|
||||
class composite_subscription;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct tag_composite_subscription_empty {};
|
||||
|
||||
class composite_subscription_inner
|
||||
{
|
||||
private:
|
||||
typedef subscription::weak_state_type weak_subscription;
|
||||
struct composite_subscription_state : public std::enable_shared_from_this<composite_subscription_state>
|
||||
{
|
||||
// invariant: cannot access this data without the lock held.
|
||||
std::set<subscription> subscriptions;
|
||||
// double checked locking:
|
||||
// issubscribed must be loaded again after each lock acquisition.
|
||||
// invariant:
|
||||
// never call subscription::unsubscribe with lock held.
|
||||
std::mutex lock;
|
||||
// invariant: transitions from 'true' to 'false' exactly once, at any time.
|
||||
std::atomic<bool> issubscribed;
|
||||
|
||||
~composite_subscription_state()
|
||||
{
|
||||
std::unique_lock<decltype(lock)> guard(lock);
|
||||
subscriptions.clear();
|
||||
}
|
||||
|
||||
composite_subscription_state()
|
||||
: issubscribed(true)
|
||||
{
|
||||
}
|
||||
composite_subscription_state(tag_composite_subscription_empty)
|
||||
: issubscribed(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Atomically add 's' to the set of subscriptions.
|
||||
//
|
||||
// If unsubscribe() has already occurred, this immediately
|
||||
// calls s.unsubscribe().
|
||||
//
|
||||
// cs.unsubscribe() [must] happens-before s.unsubscribe()
|
||||
//
|
||||
// Due to the un-atomic nature of calling 's.unsubscribe()',
|
||||
// it is possible to observe the unintuitive
|
||||
// add(s)=>s.unsubscribe() prior
|
||||
// to any of the unsubscribe()=>sN.unsubscribe().
|
||||
inline weak_subscription add(subscription s) {
|
||||
if (!issubscribed) { // load.acq [seq_cst]
|
||||
s.unsubscribe();
|
||||
} else if (s.is_subscribed()) {
|
||||
std::unique_lock<decltype(lock)> guard(lock);
|
||||
if (!issubscribed) { // load.acq [seq_cst]
|
||||
// unsubscribe was called concurrently.
|
||||
guard.unlock();
|
||||
// invariant: do not call unsubscribe with lock held.
|
||||
s.unsubscribe();
|
||||
} else {
|
||||
subscriptions.insert(s);
|
||||
}
|
||||
}
|
||||
return s.get_weak();
|
||||
}
|
||||
|
||||
// Atomically remove 'w' from the set of subscriptions.
|
||||
//
|
||||
// This does nothing if 'w' was already previously removed,
|
||||
// or refers to an expired value.
|
||||
inline void remove(weak_subscription w) {
|
||||
if (issubscribed) { // load.acq [seq_cst]
|
||||
rxu::maybe<subscription> maybe_subscription = subscription::maybe_lock(w);
|
||||
|
||||
if (maybe_subscription.empty()) {
|
||||
// Do nothing if the subscription has already expired.
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock<decltype(lock)> guard(lock);
|
||||
// invariant: subscriptions must be accessed under the lock.
|
||||
|
||||
if (issubscribed) { // load.acq [seq_cst]
|
||||
subscription& s = maybe_subscription.get();
|
||||
subscriptions.erase(std::move(s));
|
||||
} // else unsubscribe() was called concurrently; this becomes a no-op.
|
||||
}
|
||||
}
|
||||
|
||||
// Atomically clear all subscriptions that were observably added
|
||||
// (and not subsequently observably removed).
|
||||
//
|
||||
// Un-atomically call unsubscribe on those subscriptions.
|
||||
//
|
||||
// forall subscriptions in {add(s1),add(s2),...}
|
||||
// - {remove(s3), remove(s4), ...}:
|
||||
// cs.unsubscribe() || cs.clear() happens before s.unsubscribe()
|
||||
//
|
||||
// cs.unsubscribe() observed-before cs.clear ==> do nothing.
|
||||
inline void clear() {
|
||||
if (issubscribed) { // load.acq [seq_cst]
|
||||
std::unique_lock<decltype(lock)> guard(lock);
|
||||
|
||||
if (!issubscribed) { // load.acq [seq_cst]
|
||||
// unsubscribe was called concurrently.
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<subscription> v(std::move(subscriptions));
|
||||
// invariant: do not call unsubscribe with lock held.
|
||||
guard.unlock();
|
||||
std::for_each(v.begin(), v.end(),
|
||||
[](const subscription& s) {
|
||||
s.unsubscribe(); });
|
||||
}
|
||||
}
|
||||
|
||||
// Atomically clear all subscriptions that were observably added
|
||||
// (and not subsequently observably removed).
|
||||
//
|
||||
// Un-atomically call unsubscribe on those subscriptions.
|
||||
//
|
||||
// Switches to an 'unsubscribed' state, all subsequent
|
||||
// adds are immediately unsubscribed.
|
||||
//
|
||||
// cs.unsubscribe() [must] happens-before
|
||||
// cs.add(s) ==> s.unsubscribe()
|
||||
//
|
||||
// forall subscriptions in {add(s1),add(s2),...}
|
||||
// - {remove(s3), remove(s4), ...}:
|
||||
// cs.unsubscribe() || cs.clear() happens before s.unsubscribe()
|
||||
inline void unsubscribe() {
|
||||
if (issubscribed.exchange(false)) { // cas.acq_rel [seq_cst]
|
||||
std::unique_lock<decltype(lock)> guard(lock);
|
||||
|
||||
// is_subscribed can only transition to 'false' once,
|
||||
// does not need an extra atomic access here.
|
||||
|
||||
std::set<subscription> v(std::move(subscriptions));
|
||||
// invariant: do not call unsubscribe with lock held.
|
||||
guard.unlock();
|
||||
std::for_each(v.begin(), v.end(),
|
||||
[](const subscription& s) {
|
||||
s.unsubscribe(); });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
typedef std::shared_ptr<composite_subscription_state> shared_state_type;
|
||||
|
||||
protected:
|
||||
mutable shared_state_type state;
|
||||
|
||||
public:
|
||||
composite_subscription_inner()
|
||||
: state(std::make_shared<composite_subscription_state>())
|
||||
{
|
||||
}
|
||||
composite_subscription_inner(tag_composite_subscription_empty et)
|
||||
: state(std::make_shared<composite_subscription_state>(et))
|
||||
{
|
||||
}
|
||||
|
||||
composite_subscription_inner(const composite_subscription_inner& o)
|
||||
: state(o.state)
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
composite_subscription_inner(composite_subscription_inner&& o)
|
||||
: state(std::move(o.state))
|
||||
{
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
composite_subscription_inner& operator=(composite_subscription_inner o)
|
||||
{
|
||||
state = std::move(o.state);
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline weak_subscription add(subscription s) const {
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
return state->add(std::move(s));
|
||||
}
|
||||
inline void remove(weak_subscription w) const {
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
state->remove(std::move(w));
|
||||
}
|
||||
inline void clear() const {
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
state->clear();
|
||||
}
|
||||
inline void unsubscribe() {
|
||||
if (!state) {
|
||||
std::terminate();
|
||||
}
|
||||
state->unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
inline composite_subscription shared_empty();
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief controls lifetime for scheduler::schedule and observable<T, SourceOperator>::subscribe.
|
||||
|
||||
\ingroup group-core
|
||||
|
||||
*/
|
||||
class composite_subscription
|
||||
: protected detail::composite_subscription_inner
|
||||
, public subscription
|
||||
{
|
||||
typedef detail::composite_subscription_inner inner_type;
|
||||
public:
|
||||
typedef subscription::weak_state_type weak_subscription;
|
||||
|
||||
composite_subscription(detail::tag_composite_subscription_empty et)
|
||||
: inner_type(et)
|
||||
, subscription() // use empty base
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
composite_subscription()
|
||||
: inner_type()
|
||||
, subscription(*static_cast<const inner_type*>(this))
|
||||
{
|
||||
}
|
||||
|
||||
composite_subscription(const composite_subscription& o)
|
||||
: inner_type(o)
|
||||
, subscription(static_cast<const subscription&>(o))
|
||||
{
|
||||
}
|
||||
composite_subscription(composite_subscription&& o)
|
||||
: inner_type(std::move(o))
|
||||
, subscription(std::move(static_cast<subscription&>(o)))
|
||||
{
|
||||
}
|
||||
|
||||
composite_subscription& operator=(composite_subscription o)
|
||||
{
|
||||
inner_type::operator=(std::move(o));
|
||||
subscription::operator=(std::move(*static_cast<subscription*>(&o)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
static inline composite_subscription empty() {
|
||||
return detail::shared_empty();
|
||||
}
|
||||
|
||||
using subscription::is_subscribed;
|
||||
using subscription::unsubscribe;
|
||||
|
||||
using inner_type::clear;
|
||||
|
||||
inline weak_subscription add(subscription s) const {
|
||||
if (s == static_cast<const subscription&>(*this)) {
|
||||
// do not nest the same subscription
|
||||
std::terminate();
|
||||
//return s.get_weak();
|
||||
}
|
||||
auto that = this->subscription::state.get();
|
||||
trace_activity().subscription_add_enter(*that, s);
|
||||
auto w = inner_type::add(std::move(s));
|
||||
trace_activity().subscription_add_return(*that);
|
||||
return w;
|
||||
}
|
||||
|
||||
template<class F>
|
||||
auto add(F f) const
|
||||
-> typename std::enable_if<detail::is_unsubscribe_function<F>::value, weak_subscription>::type {
|
||||
return add(make_subscription(std::move(f)));
|
||||
}
|
||||
|
||||
inline void remove(weak_subscription w) const {
|
||||
auto that = this->subscription::state.get();
|
||||
trace_activity().subscription_remove_enter(*that, w);
|
||||
inner_type::remove(w);
|
||||
trace_activity().subscription_remove_return(*that);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const composite_subscription& lhs, const composite_subscription& rhs) {
|
||||
return static_cast<const subscription&>(lhs) < static_cast<const subscription&>(rhs);
|
||||
}
|
||||
inline bool operator==(const composite_subscription& lhs, const composite_subscription& rhs) {
|
||||
return static_cast<const subscription&>(lhs) == static_cast<const subscription&>(rhs);
|
||||
}
|
||||
inline bool operator!=(const composite_subscription& lhs, const composite_subscription& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline composite_subscription shared_empty() {
|
||||
static composite_subscription shared_empty = composite_subscription(tag_composite_subscription_empty());
|
||||
return shared_empty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<class T>
|
||||
class resource : public subscription_base
|
||||
{
|
||||
public:
|
||||
typedef typename composite_subscription::weak_subscription weak_subscription;
|
||||
|
||||
resource()
|
||||
: lifetime(composite_subscription())
|
||||
, value(std::make_shared<rxu::detail::maybe<T>>())
|
||||
{
|
||||
}
|
||||
|
||||
explicit resource(T t, composite_subscription cs = composite_subscription())
|
||||
: lifetime(std::move(cs))
|
||||
, value(std::make_shared<rxu::detail::maybe<T>>(rxu::detail::maybe<T>(std::move(t))))
|
||||
{
|
||||
auto localValue = value;
|
||||
lifetime.add(
|
||||
[localValue](){
|
||||
localValue->reset();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
T& get() {
|
||||
return value.get()->get();
|
||||
}
|
||||
composite_subscription& get_subscription() {
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
bool is_subscribed() const {
|
||||
return lifetime.is_subscribed();
|
||||
}
|
||||
weak_subscription add(subscription s) const {
|
||||
return lifetime.add(std::move(s));
|
||||
}
|
||||
template<class F>
|
||||
auto add(F f) const
|
||||
-> typename std::enable_if<detail::is_unsubscribe_function<F>::value, weak_subscription>::type {
|
||||
return lifetime.add(make_subscription(std::move(f)));
|
||||
}
|
||||
void remove(weak_subscription w) const {
|
||||
return lifetime.remove(std::move(w));
|
||||
}
|
||||
void clear() const {
|
||||
return lifetime.clear();
|
||||
}
|
||||
void unsubscribe() const {
|
||||
return lifetime.unsubscribe();
|
||||
}
|
||||
|
||||
protected:
|
||||
composite_subscription lifetime;
|
||||
std::shared_ptr<rxu::detail::maybe<T>> value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user