Synchronous Interval* considered harmful (or how Scheduler.Now can create a deadlock.)

已答覆 Synchronous Interval* considered harmful (or how Scheduler.Now can create a deadlock.)

  • Sunday, December 20, 2009 8:42 AM
     
      Has Code
    I can't seem to figure out how to get an Observable.Interval(Scheduler.Now), or .Timer() to terminate by using Take, TakeUntil, or other means. 

    In particular, this code never terminates:

          var intervalObservable = Observable.Interval(System.Threading.Scheduler.Now, TimeSpan.FromMilliseconds(10)).Take(50);
    
          intervalObservable
            .Finally(() => Console.WriteLine("End of sequence"))
            .Subscribe((count) => {
            Console.WriteLine("{0}", count);
          });

    And neither does this:

          var killswitch = new ReplaySubject<Unit>();
    
          var intervalObservable = Observable.Interval(System.Threading.Scheduler.Now, TimeSpan.FromMilliseconds(10)).TakeUntil(killswitch);
    
          intervalObservable.Subscribe((count) => {
            Console.WriteLine("{0}", count);
            killswitch.OnNext(new Unit());
          });

    I'm guessing additional checks are necessary in the code for Interval, Timer and others may be necessary, or Take() is not disposing of its subscription and causing the Interval to stop scheduling/waiting synchronously? 

All Replies

  • Sunday, December 20, 2009 6:32 PM
     
     Proposed Answer
    Yes, using Scheduler.Now to schedule something in the future is rather interesting.  It uses a Thread.Sleep to sleep until the right time, but if the observable it produces is infinite then it never terminates.  Perhaps, it would be better to throw if someone requests to do something in the future "now".  We'll discuss it again.
  • Sunday, December 20, 2009 7:04 PM
     
     
    Three* possible remedies that I can think of:

    First, .Take and .TakeUntil are eager unsubscribers yes? Could Interval and Timer be modified to do book-keeping on this to watch for that dispose and then stop scheduling? Or at least, stop scheduling until there's another subscriber.

    Second, could long-lasting observables implement IDisposable themselves? This would disconnect all subscribers, but I'm guessing these long-lasting observables have some resources that should be disposed of anyhow, when not in use.

    Third, could Schedule-in-the-future methods include overloads that take a CancellationToken? For synchronous schedulers that sleep, this would still block -a- thread, but you shouldn't be scheduling stuff synchronously on the UI thread anyway. But as soon as it wakes up, it can check the cancellation token and abort.

    I would like to keep the ability to schedule something in the future "now", because there are definite use cases for creating a background thread to do work, synchronously, using a scheduler or long-running observable.

    To simplify things for me, as I ran into some issues swapping my work processor with a scheduler, I thought of Scheduler.Now as "Scheduler.Sync" and Scheduler.Later as "Scheduler.Async". Once I got over that hurdle, everything fell into place.
    • Edited by Aaron Friel Sunday, December 20, 2009 7:32 PM Now 20% newer and more improved.
    •  
  • Sunday, December 20, 2009 11:29 PM
     
     
    I did think of another possible way to remedy the situation.  Stay tuned...
  • Sunday, December 20, 2009 11:35 PM
     
     
    *shakes fist* And I thought my list was exhaustive. ;)
  • Wednesday, December 23, 2009 12:31 AM
     
     Answered
    Our latest release fixes this issue. Thanks for reporting this!