/// <summary> /// Attaches to. /// </summary> /// <param name="instance">The instance.</param> /// <returns></returns> public IConnectableObservable <T> AttachTo(IConnectableObservable <T> instance) { IObservable <T> monitorStream = instance.Do(PublishOnNext, PublishError, PublishComplete); return(new ConnectableWraper(monitorStream, instance)); }
public IObservable <ResourceEvent <TResource> > GetResource(ResourceStreamType type) { var childScheduler = new EventLoopScheduler(); // dedicated thread for the child on which all messages are syncrhonized return(Observable.Defer(async() => { AddSubscriber(); _logger.LogTrace("Subscriber awaiting cache synchronization before attaching"); var isCacheSynchronized = await _cacheSynchronized.Task; if (!isCacheSynchronized) // really this only happens if the reset is the master completes before first reset, in which case the downstream subscriber gets nothing { return Observable.Empty <ResourceEvent <TResource> >(); } // we use lock to pause any processing of the broadcaster while we're attaching to the stream so proper alignment can be made _logger.LogTrace("Subscriber attaching to broadcaster"); return Observable.Create <ResourceEvent <TResource> >(observer => { var broadcasterAttachment = Disposable.Empty; var cacheSnapshot = _cache.Snapshot(); if (type.HasFlag(ResourceStreamType.List)) { _logger.LogTrace($"Flushing contents of cache version {cacheSnapshot.Version}"); _cache.Values .ToReset(type == ResourceStreamType.ListWatch) .ToObservable() .Concat(Observable.Never <ResourceEvent <TResource> >()) .ObserveOn(Scheduler.Immediate) .Subscribe(observer); } if (type.HasFlag(ResourceStreamType.Watch)) { broadcasterAttachment = _masterObservable // we could be ahead of broadcaster because we initialized from cache which gets updated before the message are sent to broadcaster // this logic realigns us at the correct point with the broadcaster .Do(x => _logger.LogTrace($"Received from broadcaster {x}")) .SkipWhile(x => x.MessageNumber <= cacheSnapshot.Version) .Select(x => x.Value) .Do(x => _logger.LogTrace($"Aligned with broadcaster {x}")) .SubscribeOn(_masterScheduler) .ObserveOn(childScheduler) .Subscribe(observer, () => { _logger.LogTrace("Child OnComplete"); RemoveSubscriber(); }); } else { observer.OnCompleted(); } // let broadcaster know we're done attaching to stream so it can resume it's regular work _logger.LogTrace("Finished attaching to stream - signalling to resume"); lock (_lock) { _waitingSubscribers.Signal(); } return broadcasterAttachment; }) .ObserveOn(childScheduler) .SubscribeOn(childScheduler); }) .SubscribeOn(childScheduler) // ensures that when we attach master observer it's done on child thread, as we plan on awaiting cache synchronization .Do(_ => _logger.LogTrace($"Shared informer out: {_}"))); }