Exemplo n.º 1
0
        /// <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
        }
Exemplo n.º 2
0
        /// <summary>
        /// Limit the number of simultaneously executing "limitedSequences" to maxCount.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="source"></param>
        /// <param name="limitedSequence"></param>
        /// <param name="maxCount"></param>
        /// <param name="sched"></param>
        /// <returns></returns>
        public static IObservable <U> LimitGlobally <T, U> (this IObservable <T> source, Func <IObservable <T>, IObservable <U> > limitedSequence, int maxCount, IScheduler sched = null)
        {
            var counter = new LimitGlobalCounter(maxCount);

            return(source.LimitGlobally(limitedSequence, counter, sched));
        }