private void SubscribeNotify(DispatchChangesEnumerable notifier)
        {
            Contract.Requires(notifier != null);
            Contract.Requires(source != null);

            var reset = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);

            subscription.SetDisposableIndirectly(() =>
                                                 source.Subscribe(
                                                     _ => notifier.OnCollectionChanged(reset),
                                                     () => PresentationTraceSources.DataBindingSource.TraceEvent(
                                                         TraceEventType.Verbose,
                                                         0,
                                                         "Subscription: Notification binding completed.")));
        }
Ejemplo n.º 2
0
        internal static IObservable <byte[]> ReadToEndObservable(this Stream stream, byte[] buffer)
        {
            Contract.Requires(stream != null);
            Contract.Requires(stream.CanRead);
            Contract.Requires(buffer != null);
            Contract.Requires(buffer.Length > 0);
            Contract.Ensures(Contract.Result <IObservable <byte[]> >() != null);

            return(Observable.Create <byte[]>(
                       observer =>
            {
                var subscription = new SerialDisposable();

                return new CompositeDisposable(
                    subscription,
                    Scheduler.Immediate.Schedule(
                        self =>
                {
                    bool continueReading = true;

                    subscription.SetDisposableIndirectly(() =>
                                                         stream.ReadObservable(buffer).SubscribeSafe(
                                                             data =>
                    {
                        if (data.Length > 0)
                        {
                            observer.OnNext(data);
                        }
                        else
                        {
                            continueReading = false;
                        }
                    },
                                                             observer.OnError,
                                                             () =>
                    {
                        if (continueReading)
                        {
                            self();
                        }
                        else
                        {
                            observer.OnCompleted();
                        }
                    }));
                }));
            }));
        }
Ejemplo n.º 3
0
        public static IObservable <TSource> Serve <TSource>(this Func <IObservable <TSource> > sourceFactory, int maxConcurrent, Func <Exception, bool> onError)
        {
            Contract.Requires(sourceFactory != null);
            Contract.Requires(maxConcurrent > 0);
            Contract.Requires(onError != null);
            Contract.Ensures(Contract.Result <IObservable <TSource> >() != null);

            return(Observable.Create <TSource>(
                       observer =>
            {
                var disposables = new CompositeDisposable();

                for (int i = 0; i < maxConcurrent; i++)
                {
                    var current = new SerialDisposable();

                    disposables.Add(current);
                    disposables.Add(
                        Scheduler.CurrentThread.Schedule(self =>
                                                         current.SetDisposableIndirectly(() =>
                                                                                         sourceFactory().SubscribeSafe(
                                                                                             observer.OnNext,
                                                                                             ex =>
                    {
                        if (onError(ex))
                        {
                            self();
                        }
                        else
                        {
                            observer.OnError(ex);
                        }
                    },
                                                                                             self))));
                }

                return disposables;
            }));
        }
Ejemplo n.º 4
0
        protected override IObservable <IParseResult <TResult> > Parse()
#endif
        {
            return(Observable.Create <IParseResult <TResult> >(
                       observer =>
            {
                var cursor = Cursor;

                IObservableParser <TSource, TResult> compiledGrammar;

#if !SILVERLIGHT && !PORT_45 && !PORT_40
                using (ParserTraceSources.TraceGrammarCompilation())
                {
                    compiledGrammar = grammar(cursor);
                }
#else
                compiledGrammar = grammar(cursor);
#endif

                var subscription = new SerialDisposable();

                /* This scheduler is only used for recursion.  It must be Immediate.
                 *
                 * In testing, using a scheduler such as CurrentThread would cause the
                 * ambiguous lab to miss values due to the order in which the ambiguous
                 * parser enqueues the subscription to the underyling source and the
                 * memoizing cursor, in relation to this parser's scheduling of recursion.
                 */
                var schedule = Scheduler.Immediate.Schedule(
                    self =>
                {
#if !SILVERLIGHT && !PORT_45 && !PORT_40
                    traceContext.TraceBeginIteration();
#endif

                    bool hasResult = false;

                    subscription.SetDisposableIndirectly(
                        () => compiledGrammar.Parse(cursor).SubscribeSafe(
                            result =>
                    {
                        var lookAhead = result as ILookAheadParseResult <TResult>;

                        if (lookAhead != null)
                        {
                            lookAhead.OnCompleted(success: true);
                            return;
                        }

                        hasResult = true;

#if !SILVERLIGHT && !PORT_45 && !PORT_40
                        traceContext.TraceResult(result);
#endif

                        observer.OnNext(result);

                        if (!cursor.AtEndOfSequence)
                        {
                            cursor.Move(result.Length);
                        }
                    },
                            observer.OnError,
                            () =>
                    {
                        if (!hasResult)
                        {
                            cursor.MoveToEnd();
                        }

                        if (cursor.AtEndOfSequence)
                        {
                            observer.OnCompleted();
                        }
                        else
                        {
                            self();
                        }
                    }));
                });

                return new CompositeDisposable(schedule, subscription);
            }));
        }
        public static IObservable <Either <TSource, TException> > OnErrorResumeNext <TSource, TException>(
            this IEnumerator <IObservable <TSource> > sources,
            Func <TException, IEnumerator <IObservable <TSource> > > handler,
            Func <TException, TimeSpan> backOffSelector)
            where TException : Exception
        {
            Contract.Requires(sources != null);
            Contract.Requires(handler != null);
            Contract.Requires(backOffSelector != null);
            Contract.Ensures(Contract.Result <IObservable <Either <TSource, TException> > >() != null);

            return(EitherObservable.Create <TSource, TException>(
                       observer =>
            {
                bool movedNext;
                IObservable <TSource> current = null;

                Func <bool> moveNext = () =>
                {
                    try
                    {
                        movedNext = sources.MoveNext();

                        if (movedNext)
                        {
                            current = sources.Current;

                            Contract.Assume(current != null);

                            return true;
                        }
                    }
                    catch (Exception ex)
                    {
                        observer.OnError(ex);
                    }

                    return false;
                };

                if (!moveNext())
                {
                    observer.OnCompleted();

                    return sources;
                }

                var subscription = new SerialDisposable();
                var sourcesDisposable = new SerialDisposable();

                sourcesDisposable.Disposable = sources;

                var disposable = Scheduler.CurrentThread.Schedule(
                    TimeSpan.Zero,
                    self =>
                {
                    subscription.SetDisposableIndirectly(() =>
                                                         current.SubscribeSafe(
                                                             observer.OnNextLeft,
                                                             ex =>
                    {
                        var typedError = ex as TException;

                        if (typedError == null)
                        {
                            observer.OnError(ex);
                        }
                        else
                        {
                            observer.OnNextRight(typedError);

                            IEnumerator <IObservable <TSource> > next;

                            try
                            {
                                next = handler(typedError);
                            }
                            catch (Exception ex2)
                            {
                                observer.OnError(ex2);
                                return;
                            }

                            Contract.Assume(next != null);

                            if (sources != next)
                            {
                                sources = next;

                                sourcesDisposable.Disposable = sources;
                            }

                            if (moveNext())
                            {
                                TimeSpan delay;

                                try
                                {
                                    delay = backOffSelector(typedError);
                                }
                                catch (Exception ex2)
                                {
                                    observer.OnError(ex2);
                                    return;
                                }

                                if (delay < TimeSpan.Zero)
                                /* Feature that allows callers to indicate when an exception is fatal based on its type */
                                {
                                    observer.OnError(ex);
                                }
                                else
                                {
                                    self(delay);
                                }
                            }
                            else
                            {
                                observer.OnCompleted();
                            }
                        }
                    },
                                                             () =>
                    {
                        if (moveNext())
                        {
                            self(TimeSpan.Zero);
                        }
                        else
                        {
                            observer.OnCompleted();
                        }
                    }));
                });

                return new CompositeDisposable(subscription, disposable, sourcesDisposable);
            }));
        }
Ejemplo n.º 6
0
        public static IObservable <TResult> Consume <TSource, TResult>(
            this IObservable <TSource> source, Func <TSource, IObservable <TResult> > consumerSelector)
        {
            return(Observable.Create <TResult>(
                       observer =>
            {
                object gate = new object();
                var consumingSubscription = new SerialDisposable();
                var schedule = new SerialDisposable();

                TSource lastSkippedNotification = default(TSource);
                bool hasSkippedNotification = false;
                bool consuming = false;
                bool stopped = false;

                var subscription = source.Subscribe(
                    value =>
                {
                    lock (gate)
                    {
                        if (consuming)
                        {
                            lastSkippedNotification = value;
                            hasSkippedNotification = true;
                            return;
                        }
                        else
                        {
                            consuming = true;
                            hasSkippedNotification = false;
                        }
                    }

                    var additionalData = value;

                    schedule.Disposable = Scheduler.Immediate.Schedule(
                        self =>
                    {
                        IObservable <TResult> observable;

                        try
                        {
                            observable = consumerSelector(additionalData);
                        }
                        catch (Exception ex)
                        {
                            observer.OnError(ex);
                            return;
                        }

                        consumingSubscription.SetDisposableIndirectly(
                            () => observable.Subscribe(
                                observer.OnNext,
                                observer.OnError,
                                () =>
                        {
                            bool consumeAgain = false;
                            bool completeNow = false;

                            lock (gate)
                            {
                                /* The hasSkippedNotification field avoids a race condition between source notifications and the consuming observable
                                 * calling OnCompleted that could cause data to become available without any active consumer.  The solution is to
                                 * check whether additional notifications were skipped before the consuming observable completed.  If so, then we
                                 * try to consume once more; and if there isn't any data available, because it was already consumed before the previous
                                 * observable completed, then the new observable will be empty.  If no additional notifications are received
                                 * from the source before the new observable completes, then hasSkippedNotification will be false the second time
                                 * around and there will be no active consumer until the next source notification.
                                 *
                                 * This behavior reactively mimics typical interactive consumer behavior; e.g., lock and loop if queue.Count > 0.
                                 */
                                consuming = hasSkippedNotification;

                                if (consuming)
                                {
                                    additionalData = lastSkippedNotification;
                                    hasSkippedNotification = false;
                                    consumeAgain = true;
                                }
                                else
                                {
                                    completeNow = stopped;
                                }
                            }

                            if (consumeAgain)
                            {
                                self();
                            }
                            else if (completeNow)
                            {
                                observer.OnCompleted();
                            }
                        }));
                    });
                },
                    observer.OnError,
                    () =>
                {
                    bool completeNow;

                    lock (gate)
                    {
                        stopped = true;

                        completeNow = !consuming;
                    }

                    if (completeNow)
                    {
                        observer.OnCompleted();
                    }
                });

                return new CompositeDisposable(consumingSubscription, subscription, schedule);
            }));
        }
        public IObservable <IParseResult <IObservable <TResult> > > Parse(IObservableCursor <TSource> source)
        {
            return(Observable.Create <IParseResult <IObservable <TResult> > >(
                       observer =>
            {
                int matchCount = 0;
                int remainingLength = 0;

                Action <Action> iterate = moveNext =>
                {
                    bool hasResult = false;
                    int length = 0;

                    var branch = source.Branch();

                    var values = parser.Parse(branch)
                                 .Finally(branch.Dispose)
                                 .Select(result =>
                    {
                        if (!hasResult)
                        {
                            matchCount++;
                            hasResult = true;
                        }

                        length = Math.Max(length, result.Length);

                        return result.Value;
                    })
                                 .Do(
                        __ => { },
                        () =>
                    {
                        /* We must respect the greediness of the results unless the length is zero since the
                         * cursor would have already moved to the following element.  It is acceptable to ignore
                         * zero-length results because marking an entirely non-greedy parser as ambiguous would
                         * otherwise cause the parser to continously parse the first element indefinitely.
                         */
                        if (length > 0)
                        {
                            remainingLength = length - 1;
                        }
                        else if (remainingLength > 0)
                        {
                            remainingLength--;
                        }

                        moveNext();
                    });

                    observer.OnNext(ParseResult.Create(values, length: 1));
                };

                Action complete = () =>
                {
                    if (remainingLength > 0)
                    {
                        observer.OnNext(ObservableParseResult.SuccessMany <TResult>(remainingLength));
                    }

                    observer.OnCompleted();
                };

                var untilSubscription = new SerialDisposable();

                var schedule = Scheduler.Immediate.Schedule(
                    self =>
                {
                    if (!source.AtEndOfSequence &&
                        (untilCount == unlimitedCount || matchCount < untilCount))
                    {
                        if (untilParser == null)
                        {
                            iterate(self);
                        }
                        else
                        {
                            untilSubscription.SetDisposableIndirectly(() =>
                                                                      untilParser.Parse(source).Any().Subscribe(
                                                                          any =>
                            {
                                if (!any)
                                {
                                    iterate(self);
                                }
                                else
                                {
                                    complete();
                                }
                            },
                                                                          observer.OnError));
                        }
                    }
                    else
                    {
                        complete();
                    }
                });

                return new CompositeDisposable(schedule, untilSubscription);
            }));
        }
Ejemplo n.º 8
0
        private static IObservableParser <TSource, IObservable <TResult> > AtLeast <TSource, TSeparator, TResult>(
            this IObservableParser <TSource, TResult> parser,
            string name,
            int count,
            int maximum = -1,
            IObservableParser <TSource, TSeparator> separator = null,
            bool nonGreedy = false)
        {
            Contract.Requires(parser != null);
            Contract.Requires(!string.IsNullOrEmpty(name));
            Contract.Requires(count >= 0);
            Contract.Requires(maximum == -1 || maximum >= count);
            Contract.Requires(maximum != 0);
            Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null);

            // TODO: Update this method to properly support multi-result parsers.

            /* The current implementation just uses Math.Max for the lengths and aggregates all of the results into a single list.
             * The correct behavior is more like a SelectMany query, so consider using the new SelectMany overload that AllObservableParser uses.
             */

            /* This method is optimized to prevent stack overflows due to two factors: recursion and using Skip to move the source cursor.
             *
             * The previous implementation used recursive calls to NoneOrMore, in which there was a linear relationship between the number
             * of stack frames and the number of elements in the input sequence.  As an input sequence grew and the parser continued matching
             * elements, the number of calls to the Skip operator (via the Remainder extension) grew linearly, and so did the number of branches
             * due to NoneOrMore using the Or operator, which not only added the Or operator to the stack but added all of the calls to the
             * quantified parser between the stack frames that Or added, for every subsequent element in the sequence that the parser matched.
             */
            return(parser.Yield <TSource, TResult, IObservable <TResult> >(
                       name,
                       (source, observer) =>
            {
                Contract.Requires(source.IsForwardOnly);

                var branch = source.Branch();

                var list = new List <TResult>();

                int total = 0;
                int totalLength = 0;

                Action onCompleted = () =>
                {
                    if (total >= count)
                    {
                        observer.OnNext(new ParseResult <IObservable <TResult> >(list.ToObservable(Scheduler.Immediate), totalLength));
                    }

                    observer.OnCompleted();
                };

                var subscription = new SerialDisposable();

                Func <IDisposable> start = () =>
                                           Scheduler.Immediate.Schedule(self =>
                {
                    bool hasResult = false;
                    bool hasSeparatorResult = false;

                    int length = 0;
                    int separatorLength = 0;

                    Action currentCompleted = () =>
                    {
                        if (hasResult)
                        {
                            totalLength += length + separatorLength;

                            if (total < (maximum == -1 ? count : maximum))
                            {
                                total++;
                            }

                            if (total != maximum && (separator == null || hasSeparatorResult))
                            {
                                if (nonGreedy && total >= count)
                                {
                                    var lookAhead = new LookAheadParseResult <IObservable <TResult> >(list.ToObservable(Scheduler.Immediate), totalLength);

                                    subscription.SetDisposableIndirectly(() => new CompositeDisposable(
                                                                             lookAhead,
                                                                             lookAhead.Subscribe(success =>
                                    {
                                        if (success)
                                        {
                                            onCompleted();
                                        }
                                        else
                                        {
                                            self();
                                        }
                                    })));

                                    observer.OnNext(lookAhead);
                                    return;
                                }
                                else
                                {
                                    self();
                                    return;
                                }
                            }
                        }

                        onCompleted();
                    };

                    subscription.SetDisposableIndirectly(() =>
                                                         parser.Parse(branch).SubscribeSafe(
                                                             result =>
                    {
                        hasResult = true;
                        length = Math.Max(length, result.Length);

                        list.Add(result.Value);
                    },
                                                             observer.OnError,
                                                             () =>
                    {
                        branch.Move(length);

                        if (separator == null)
                        {
                            currentCompleted();
                        }
                        else
                        {
                            subscription.SetDisposableIndirectly(() =>
                                                                 separator.Parse(branch).SubscribeSafe(
                                                                     separatorResult =>
                            {
                                hasSeparatorResult = true;
                                separatorLength = Math.Max(separatorLength, separatorResult.Length);
                            },
                                                                     observer.OnError,
                                                                     () =>
                            {
                                branch.Move(separatorLength);

                                currentCompleted();
                            }));
                        }
                    }));
                });

                if (nonGreedy && count == 0)
                {
                    var startSubscription = new SingleAssignmentDisposable();

                    var lookAhead = new LookAheadParseResult <IObservable <TResult> >(Observable.Empty <TResult>(), length: 0);

                    var lookAheadSubscription = lookAhead.Subscribe(success =>
                    {
                        if (success)
                        {
                            onCompleted();
                        }
                        else
                        {
                            startSubscription.Disposable = start();
                        }
                    });

                    observer.OnNext(lookAhead);

                    return new CompositeDisposable(branch, subscription, lookAhead, lookAheadSubscription, startSubscription);
                }
                else
                {
                    var startSubscription = start();

                    return new CompositeDisposable(branch, subscription, startSubscription);
                }
            }));
        }