public void OnCompleted () { CheckDisposed (); if (!done) { done = true; if (n != null) observers.ForEach ((o) => n.Accept (o)); var cmp = Notification.CreateOnCompleted<T> (); observers.ForEach ((o) => cmp.Accept (o)); } }
protected override void ProcessNotification(Notification notification) { var clone = Factory.GetDatabase(notification.Uri.DatabaseName).GetItem(notification.Uri.ToDataUri()); if (ShouldProcess(clone.GetProviderPath(), $"{Action} changes on clone that were introduced on original item")) { switch (Action) { case (NotificationAction.Accept): notification.Accept(clone); break; case (NotificationAction.Reject): notification.Reject(clone); break; case (NotificationAction.Dismiss): try { // dismiss does not seem to be universally supported by all Sitecore versions // wrapper introduced no avoid late method binding error DismissNotification(notification, clone); } catch (Exception ex) { WriteError(ex, ErrorIds.MethodNotFound, ErrorCategory.NotImplemented, notification); } break; } } }
public void OnCompleted_AcceptActionWithResult() { var n1 = new Notification <int> .OnCompleted(); var res = n1.Accept(x => { Assert.Fail(); return(null); }, _ => { Assert.Fail(); return(null); }, () => "OK"); Assert.AreEqual("OK", res); }
public void OnNext_AcceptObserverWithResult() { var n1 = new Notification <int> .OnNext(42); var res = n1.Accept(new AcceptObserver(x => "OK", _ => { Assert.Fail(); return(null); }, () => { Assert.Fail(); return(null); })); Assert.AreEqual("OK", res); }
public void OnNext_AcceptObserver() { var con = new CheckOnNextObserver(); var n1 = new Notification <int> .OnNext(42); n1.Accept(con); Assert.AreEqual(42, con.Value); }
public void OnError (Exception error) { CheckDisposed (); if (!done) { done = true; n = Notification.CreateOnError<T> (error); observers.ForEach ((o) => n.Accept (o)); } }
public void OnCompleted_AcceptAction() { var obs = false; var n1 = new Notification <int> .OnCompleted(); n1.Accept(x => { Assert.Fail(); }, _ => { Assert.Fail(); }, () => { obs = true; }); Assert.IsTrue(obs); }
public void OnCompleted_AcceptObserver() { var obs = new CheckOnCompletedObserver(); var n1 = new Notification <int> .OnCompleted(); n1.Accept(obs); Assert.IsTrue(obs.Completed); }
public void OnError_AcceptActionWithResult() { var ex = new Exception(); var n1 = new Notification <int> .OnError(ex); var res = n1.Accept(x => { Assert.Fail(); return(null); }, x => "OK", () => { Assert.Fail(); return(null); }); Assert.AreEqual("OK", res); }
public void AcceptNotification(Notification notification) { notification.Accept(); Notifications.Remove(notification); RaisePropertyChanged("Notifications"); if (notification.Type == NotificationType.REQUEST_FOR_ACCESS) { GrantAccess(notification); } }
public void OnError_AcceptAction() { var ex = new Exception(); var obs = false; var n1 = new Notification <int> .OnError(ex); n1.Accept(x => { Assert.Fail(); }, _ => { obs = true; }, () => { Assert.Fail(); }); Assert.IsTrue(obs); }
public void OnError_AcceptObserver() { var ex = new Exception(); var obs = new CheckOnErrorObserver(); var n1 = new Notification <int> .OnError(ex); n1.Accept(obs); Assert.AreSame(ex, obs.Error); }
private void LoopRec(Notification notification, Action <Notification> self) { notification.Accept(_observer); if (notification.Kind != NotificationKind.OnNext) { return; } notification = notification.Acknowldge(); if (notification == null || notification.Kind == null) { return; } self(notification); }
public static IObservable <List <T> > ObserveWithBuffer <T>(this IObservable <T> source, IScheduler scheduler) { return(Observable.Create <List <T> >(observer => { Notification <List <T> > outsideNotification = null; var gate = new object(); bool active = false; var cancelable = new MultipleAssignmentDisposable(); var disposable = source.Materialize().Subscribe(thisNotification => { bool wasNotAlreadyActive; lock (gate) { wasNotAlreadyActive = !active; active = true; if (outsideNotification == null) { outsideNotification = Notification.CreateOnNext(new List <T>()); } outsideNotification.Value.Add(thisNotification.Value); } if (wasNotAlreadyActive) { cancelable.Disposable = scheduler.Schedule(self => { Notification <List <T> > localNotification = null; lock (gate) { localNotification = outsideNotification; outsideNotification = null; } localNotification.Accept(observer); bool hasPendingNotification = false; lock (gate) { hasPendingNotification = active = (outsideNotification != null); } if (hasPendingNotification) { self(); } }); } }); return new CompositeDisposable(disposable, cancelable); })); }
public static IObservable <T> ThrottleResponsive <T>(this IObservable <T> source, TimeSpan minInterval) { return(Observable.Create <T>(o => { object gate = new Object(); Notification <T> last = null, lastNonTerminal = null; DateTime referenceTime = DateTime.UtcNow - minInterval; var delayedReplay = new SerialDisposable(); return new CompositeDisposable(source.Materialize().Subscribe(x => { lock (gate) { var elapsed = DateTime.UtcNow - referenceTime; if (elapsed >= minInterval && delayedReplay.Disposable == null) { referenceTime = DateTime.UtcNow; x.Accept(o); } else { if (x.Kind == NotificationKind.OnNext) { lastNonTerminal = x; } last = x; if (delayedReplay.Disposable == null) { delayedReplay.Disposable = Scheduler.Default.Schedule(minInterval - elapsed, () => { lock (gate) { referenceTime = DateTime.UtcNow; if (lastNonTerminal != null && lastNonTerminal != last) { lastNonTerminal.Accept(o); } last.Accept(o); last = lastNonTerminal = null; delayedReplay.Disposable = null; } }); } } } }), delayedReplay); })); }
/// <summary> /// Throttle a stream of notifications, only taking the last that occurs during a subscription. /// Extension from Lee Campbell on MSDN. /// </summary> public static IObservable <T> ObserveLatestOn <T>(this IObservable <T> source, IScheduler scheduler) { return(Observable.Create <T>(observer => { Notification <T> outsideNotification = null; var gate = new object(); var active = false; var cancelable = new MultipleAssignmentDisposable(); var disposable = source.Materialize() .Subscribe(thisNotification => { bool alreadyActive; lock (gate) { alreadyActive = active; active = true; outsideNotification = thisNotification; } if (!alreadyActive) { cancelable.Disposable = scheduler .Schedule(self => { Notification <T> localNotification = null; lock (gate) { localNotification = outsideNotification; outsideNotification = null; } localNotification.Accept(observer); var hasPendingNotification = false; lock (gate) { hasPendingNotification = active = (outsideNotification != null); } if (hasPendingNotification) { self(); } }); } }); return new CompositeDisposable(disposable, cancelable); })); }
/// <summary> /// Returns an observable sequence with a single notification. /// </summary> public static IObservable <T> ToObservable <T>(this Notification <T> notification, IScheduler scheduler) { if (notification == null) { throw new ArgumentNullException("notification"); } if (scheduler == null) { throw new ArgumentNullException("scheduler"); } return(new AnonymousObservable <T>(observer => scheduler.Schedule(() => { notification.Accept(observer); if (notification.Kind == NotificationKind.OnNext) { observer.OnCompleted(); } }))); }
private void EmitCompletion(Notification <TSource> completion) { try { completion.Accept(_observer); } finally { Dispose(); } }
/// <summary> /// Limit the # of items that move through the second sequence. This limits only in a subscription, not globally! /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source"></param> /// <param name="maxCount"></param> /// <returns></returns> /// <remarks> /// Copied from here: /// https://social.msdn.microsoft.com/Forums/en-US/379a027d-4a06-4abd-9255-d54c3807b50c/parallel-processing-of-incoming-events-observablesemaphoreobserveparallel-?forum=rx /// There are significant modifications because this was a very old method! /// </remarks> public static IObservable <T> Semaphore <T>(IObservable <T> source, int maxCount, IScheduler sched = null) { // By default use the thread pool (so this will be deferred, and can do multiple guys, which is what we want). sched = sched ?? Scheduler.Default; return(Observable.Create <T>(o => { var cancel = new CancellationDisposable(); var queue = new ConcurrentQueue <Notification <T> >(); var limitter = new SemaphoreSlim(maxCount); Notification <T> final = null; int pendingAccepts = 0; var subscription = source.Materialize().Subscribe( n => { if (cancel.Token.IsCancellationRequested) { return; } try { limitter.Wait(cancel.Token); } catch (OperationCanceledException) { return; } Interlocked.Increment(ref pendingAccepts); queue.Enqueue(n); sched.Schedule(() => { try { Notification <T> notification; queue.TryDequeue(out notification); if (notification.Kind != NotificationKind.OnNext) { final = notification; } else { notification.Accept(o); } } finally { limitter.Release(); if (Interlocked.Decrement(ref pendingAccepts) == 0) // try to go lock-free a long a possible { lock (queue) // take the queue as gate (only one thread should accept final notification) { if (final != null && // make sure the final call has been received pendingAccepts == 0 // and we are behind the decrement of that thread ) { final.Accept(o); final = null; } } } } }); }); return new CompositeDisposable(cancel, subscription); })); }
/// <summary> /// Limit the number of simultaniously executing "limitedSequences" to counter. Counter can be shared /// accross multiple calls. /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="U"></typeparam> /// <param name="source"></param> /// <param name="limitedSequence">The transformation</param> /// <param name="limitter">An instance of LimitGlobalCounter. Can be shared between various calls of this.</param> /// <param name="sched">IScheduler to execute this on. If left null, defaults to Scheduler.Default</param> /// <returns>The resulting sequence transformed by limitedSequence.</returns> /// <remarks> /// When the source sequence completes, the terminating sequence will complete after each sequence from limitedSequence compleats. /// If source OnErrors, or limitedSequence on errors, then no further items will be processed. Once all ongoing sequences complete, the /// first OnError will be passed on to the resulting sequence. /// </remarks> public static IObservable <U> LimitGlobally <T, U>(this IObservable <T> source, Func <IObservable <T>, IObservable <U> > limitedSequence, LimitGlobalCounter limitter, IScheduler sched = null) { // Use the default scheduler. sched = sched ?? Scheduler.Default; return(from item in source.ObserveOn(sched) from resultItem in (from limited in Observable.FromAsync(() => { Debug.WriteLine("Getting"); return limitter.WaitAsync(); }).WriteLine("Got") from convertedItem in limitedSequence(Observable.Return(item)) select convertedItem).Finally(() => { Debug.WriteLine("Releasing"); limitter.Release(); }) select resultItem); #if false // Create an observable that will track the various things that go wrong return(Observable.Create <U>(o => { var cancel = new CancellationDisposable(); // Monitor a cancel that comes along. var queue = new ConcurrentQueue <Notification <T> >(); // Everything that comes in get queued up as a materialized item Notification <U> limitSequenceError = null; // See failure symantics above Notification <T> sourceSequenceEndCondition = null; int pendingAccepts = 0; var subscription = source.Materialize() .Where(_ => !cancel.Token.IsCancellationRequested) .SelectMany(v => Observable.FromAsync(() => limitter.WaitAsync(cancel.Token)).WriteLine("Just past limiter with {0}", v.ToString()).Select(_ => v)) .Subscribe(n => { Interlocked.Increment(ref pendingAccepts); // If we are in error condition, don't pump anything more through. if (limitSequenceError != null || sourceSequenceEndCondition != null) { Interlocked.Decrement(ref pendingAccepts); limitter.Release(); return; } queue.Enqueue(n); sched.Schedule(() => { try { Notification <T> notification; queue.TryDequeue(out notification); Debug.WriteLine("About to process notification {0}", notification.ToString()); if (notification.Kind == NotificationKind.OnNext || notification.Kind == NotificationKind.OnError) { var sub = new Subject <T>(); var seq = limitedSequence(sub).Materialize().Subscribe( result => { Debug.WriteLine("Getting item from function sequence {0}", result.ToString()); // Did an error happen, or a completion happen before the source completed? // Ignore the completion - that is "normal". if (result.Kind == NotificationKind.OnError && limitSequenceError == null) { limitSequenceError = result; } else if (result.Kind == NotificationKind.OnNext) { result.Accept(o); } }, () => { // Our finally for this one. If this is the last one to get cleaned up, then we // move on. Debug.WriteLine("Function sequence has terminated! Releasing semaphore"); limitter.Release(); if (Interlocked.Decrement(ref pendingAccepts) == 0) // try to go lock-free a long a possible { lock (queue) // take the queue as gate (only one thread should accept final notification) { if (pendingAccepts == 0) { if (limitSequenceError != null) { // Something went wrong, terminate early! Debug.WriteLine("Going to pass error onto external sequence"); limitSequenceError.Accept(o); limitSequenceError = null; } else if (sourceSequenceEndCondition != null) { // We are done, so terminate. Debug.WriteLine("Going to pass on completed to external sequence"); o.OnCompleted(); } } } } } ); Debug.WriteLine("Going to pass notification to sequence {0}", notification.ToString()); notification.Accept(sub); sub.OnCompleted(); } else { // The sequence has terminated "normally". Debug.WriteLine("Input sequence has terminated normally."); Debug.Assert(sourceSequenceEndCondition == null); sourceSequenceEndCondition = notification; limitter.Release(); if (Interlocked.Decrement(ref pendingAccepts) == 0) { lock (queue) { if (pendingAccepts == 0) { Debug.WriteLine("Going to pass completed onto external sequence"); o.OnCompleted(); } } } } } catch (Exception e) { Debug.WriteLine("Erorr on LimitGlobally: no error should ever make it here: {0}", e.Message); } }); }, () => { Debug.WriteLine("Completed Source Sequence pending is {0}", pendingAccepts); }); return new CompositeDisposable(cancel, subscription); })); #endif }