278 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // 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_SUBJECT_HPP)
 | |
| #define RXCPP_RX_SUBJECT_HPP
 | |
| 
 | |
| #include "../rx-includes.hpp"
 | |
| 
 | |
| namespace rxcpp {
 | |
| 
 | |
| namespace subjects {
 | |
| 
 | |
| namespace detail {
 | |
| 
 | |
| template<class T>
 | |
| class multicast_observer
 | |
| {
 | |
|     typedef subscriber<T> observer_type;
 | |
|     typedef std::vector<observer_type> list_type;
 | |
| 
 | |
|     struct mode
 | |
|     {
 | |
|         enum type {
 | |
|             Invalid = 0,
 | |
|             Casting,
 | |
|             Disposed,
 | |
|             Completed,
 | |
|             Errored
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     struct state_type
 | |
|         : public std::enable_shared_from_this<state_type>
 | |
|     {
 | |
|         explicit state_type(composite_subscription cs)
 | |
|             : current(mode::Casting)
 | |
|             , lifetime(cs)
 | |
|         {
 | |
|         }
 | |
|         std::mutex lock;
 | |
|         typename mode::type current;
 | |
|         rxu::error_ptr error;
 | |
|         composite_subscription lifetime;
 | |
|     };
 | |
| 
 | |
|     struct completer_type
 | |
|         : public std::enable_shared_from_this<completer_type>
 | |
|     {
 | |
|         ~completer_type()
 | |
|         {
 | |
|         }
 | |
|         completer_type(std::shared_ptr<state_type> s, const std::shared_ptr<completer_type>& old, observer_type o)
 | |
|             : state(s)
 | |
|         {
 | |
|             retain(old);
 | |
|             observers.push_back(o);
 | |
|         }
 | |
|         completer_type(std::shared_ptr<state_type> s, const std::shared_ptr<completer_type>& old)
 | |
|             : state(s)
 | |
|         {
 | |
|             retain(old);
 | |
|         }
 | |
|         void retain(const std::shared_ptr<completer_type>& old) {
 | |
|             if (old) {
 | |
|                 observers.reserve(old->observers.size() + 1);
 | |
|                 std::copy_if(
 | |
|                     old->observers.begin(), old->observers.end(),
 | |
|                     std::inserter(observers, observers.end()),
 | |
|                     [](const observer_type& o){
 | |
|                         return o.is_subscribed();
 | |
|                     });
 | |
|             }
 | |
|         }
 | |
|         std::shared_ptr<state_type> state;
 | |
|         list_type observers;
 | |
|     };
 | |
| 
 | |
|     // this type prevents a circular ref between state and completer
 | |
|     struct binder_type
 | |
|         : public std::enable_shared_from_this<binder_type>
 | |
|     {
 | |
|         explicit binder_type(composite_subscription cs)
 | |
|             : state(std::make_shared<state_type>(cs))
 | |
|             , id(trace_id::make_next_id_subscriber())
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         std::shared_ptr<state_type> state;
 | |
| 
 | |
|         trace_id id;
 | |
| 
 | |
|         // used to avoid taking lock in on_next
 | |
|         mutable std::weak_ptr<completer_type> current_completer;
 | |
| 
 | |
|         // must only be accessed under state->lock
 | |
|         mutable std::shared_ptr<completer_type> completer;
 | |
|     };
 | |
| 
 | |
|     std::shared_ptr<binder_type> b;
 | |
| 
 | |
| public:
 | |
|     typedef subscriber<T, observer<T, detail::multicast_observer<T>>> input_subscriber_type;
 | |
| 
 | |
|     explicit multicast_observer(composite_subscription cs)
 | |
|         : b(std::make_shared<binder_type>(cs))
 | |
|     {
 | |
|         std::weak_ptr<binder_type> binder = b;
 | |
|         b->state->lifetime.add([binder](){
 | |
|             auto b = binder.lock();
 | |
|             if (b && b->state->current == mode::Casting){
 | |
|                 b->state->current = mode::Disposed;
 | |
|                 b->current_completer.reset();
 | |
|                 b->completer.reset();
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     trace_id get_id() const {
 | |
|         return b->id;
 | |
|     }
 | |
|     composite_subscription get_subscription() const {
 | |
|         return b->state->lifetime;
 | |
|     }
 | |
|     input_subscriber_type get_subscriber() const {
 | |
|         return make_subscriber<T>(get_id(), get_subscription(), observer<T, detail::multicast_observer<T>>(*this));
 | |
|     }
 | |
|     bool has_observers() const {
 | |
|         std::unique_lock<std::mutex> guard(b->state->lock);
 | |
|         return b->completer && !b->completer->observers.empty();
 | |
|     }
 | |
|     template<class SubscriberFrom>
 | |
|     void add(const SubscriberFrom& sf, observer_type o) const {
 | |
|         trace_activity().connect(sf, o);
 | |
|         std::unique_lock<std::mutex> guard(b->state->lock);
 | |
|         switch (b->state->current) {
 | |
|         case mode::Casting:
 | |
|             {
 | |
|                 if (o.is_subscribed()) {
 | |
|                     std::weak_ptr<binder_type> binder = b;
 | |
|                     o.add([=](){
 | |
|                         auto b = binder.lock();
 | |
|                         if (b) {
 | |
|                             std::unique_lock<std::mutex> guard(b->state->lock);
 | |
|                             b->completer = std::make_shared<completer_type>(b->state, b->completer);
 | |
|                         }
 | |
|                     });
 | |
|                     b->completer = std::make_shared<completer_type>(b->state, b->completer, o);
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case mode::Completed:
 | |
|             {
 | |
|                 guard.unlock();
 | |
|                 o.on_completed();
 | |
|                 return;
 | |
|             }
 | |
|             break;
 | |
|         case mode::Errored:
 | |
|             {
 | |
|                 auto e = b->state->error;
 | |
|                 guard.unlock();
 | |
|                 o.on_error(e);
 | |
|                 return;
 | |
|             }
 | |
|             break;
 | |
|         case mode::Disposed:
 | |
|             {
 | |
|                 guard.unlock();
 | |
|                 o.unsubscribe();
 | |
|                 return;
 | |
|             }
 | |
|             break;
 | |
|         default:
 | |
|             std::terminate();
 | |
|         }
 | |
|     }
 | |
|     template<class V>
 | |
|     void on_next(V v) const {
 | |
|         auto current_completer = b->current_completer.lock();
 | |
|         if (!current_completer) {
 | |
|             std::unique_lock<std::mutex> guard(b->state->lock);
 | |
|             b->current_completer = b->completer;
 | |
|             current_completer = b->current_completer.lock();
 | |
|         }
 | |
|         if (!current_completer || current_completer->observers.empty()) {
 | |
|             return;
 | |
|         }
 | |
|         for (auto& o : current_completer->observers) {
 | |
|             if (o.is_subscribed()) {
 | |
|                 o.on_next(v);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     void on_error(rxu::error_ptr e) const {
 | |
|         std::unique_lock<std::mutex> guard(b->state->lock);
 | |
|         if (b->state->current == mode::Casting) {
 | |
|             b->state->error = e;
 | |
|             b->state->current = mode::Errored;
 | |
|             auto s = b->state->lifetime;
 | |
|             auto c = std::move(b->completer);
 | |
|             b->current_completer.reset();
 | |
|             guard.unlock();
 | |
|             if (c) {
 | |
|                 for (auto& o : c->observers) {
 | |
|                     if (o.is_subscribed()) {
 | |
|                         o.on_error(e);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             s.unsubscribe();
 | |
|         }
 | |
|     }
 | |
|     void on_completed() const {
 | |
|         std::unique_lock<std::mutex> guard(b->state->lock);
 | |
|         if (b->state->current == mode::Casting) {
 | |
|             b->state->current = mode::Completed;
 | |
|             auto s = b->state->lifetime;
 | |
|             auto c = std::move(b->completer);
 | |
|             b->current_completer.reset();
 | |
|             guard.unlock();
 | |
|             if (c) {
 | |
|                 for (auto& o : c->observers) {
 | |
|                     if (o.is_subscribed()) {
 | |
|                         o.on_completed();
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             s.unsubscribe();
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| 
 | |
| }
 | |
| 
 | |
| template<class T>
 | |
| class subject
 | |
| {
 | |
|     detail::multicast_observer<T> s;
 | |
| 
 | |
| public:
 | |
|     typedef subscriber<T, observer<T, detail::multicast_observer<T>>> subscriber_type;
 | |
|     typedef observable<T> observable_type;
 | |
|     subject()
 | |
|         : s(composite_subscription())
 | |
|     {
 | |
|     }
 | |
|     explicit subject(composite_subscription cs)
 | |
|         : s(cs)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     bool has_observers() const {
 | |
|         return s.has_observers();
 | |
|     }
 | |
| 
 | |
|     composite_subscription get_subscription() const {
 | |
|         return s.get_subscription();
 | |
|     }
 | |
| 
 | |
|     subscriber_type get_subscriber() const {
 | |
|         return s.get_subscriber();
 | |
|     }
 | |
| 
 | |
|     observable<T> get_observable() const {
 | |
|         auto keepAlive = s;
 | |
|         return make_observable_dynamic<T>([=](subscriber<T> o){
 | |
|             keepAlive.add(keepAlive.get_subscriber(), std::move(o));
 | |
|         });
 | |
|     }
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 | |
| #endif
 |