When using Observable.Interval
I got a few times the following exception which causes application crash (the exception can be only found in Event Viewer - Error Source: .NET Runtime)
Application: RxPlayground.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.ArgumentNullException
Stack:
at System.Reactive.Concurrency.AsyncLock.Wait(System.Action)
at System.Reactive.Concurrency.DefaultScheduler+<>c__DisplayClass9`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].<SchedulePeriodic>b__6()
at System.Reactive.Concurrency.ConcurrencyAbstractionLayerImpl+PeriodicTimer.Tick(System.Object)
at System.Threading.TimerQueueTimer.CallCallbackInContext(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.TimerQueueTimer.CallCallback()
at System.Threading.TimerQueueTimer.Fire()
at System.Threading.TimerQueue.FireNextTimers()
at System.Threading.TimerQueue.AppDomainTimerCallback()
Have you ever had such problems when using Reactive Extensions? Do you know what could be the cause?
Here is some code that might caused the issue (however maybe it is not it because I cannot reproduce it...):
static void Main(string[] args)
{
var subscriptions = SubscribeToObservables().Take(10000).ToArray();
Console.WriteLine("BEGIN. Click Enter to Dispose...");
Console.ReadLine();
Console.WriteLine("DISPOSING");
foreach (var subscription in subscriptions)
{
subscription.Dispose();
}
Console.WriteLine("DISPOSED. Click Enter to finish...");
Console.ReadLine();
Console.WriteLine("END");
}
private static IEnumerable<IDisposable> SubscribeToObservables()
{
var x = Observable.Interval(TimeSpan.FromSeconds(1)).Select(n =>
{
//Thread.Sleep(TimeSpan.FromMinutes(1));
return n;
});
var sub = x.Subscribe(
n =>
{
Thread.Sleep(TimeSpan.FromMinutes(1));
Console.WriteLine(n);
});
yield return sub;
}
Edit: I have checked AsyncLock
class and all its references (https://github.com/Reactive-Extensions/Rx.NET/blob/master/Rx.NET/Source/System.Reactive.Core/Reactive/Concurrency/AsyncLock.cs) and I cannot see any possibility that the Action delegate could ever be null! In case of our exception we can see from the stacktrace that the Wait was invoked from the following code (https://github.com/Reactive-Extensions/Rx.NET/blob/master/Rx.NET/Source/System.Reactive.Core/Reactive/Concurrency/DefaultScheduler.cs):
public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
{
if (period < TimeSpan.Zero)
throw new ArgumentOutOfRangeException("period");
if (action == null)
throw new ArgumentNullException("action");
var state1 = state;
var gate = new AsyncLock();
var cancel = s_cal.StartPeriodicTimer(() =>
{
gate.Wait(() =>
{
state1 = action(state1);
});
}, period);
return Disposable.Create(() =>
{
cancel.Dispose();
gate.Dispose();
action = Stubs<TState>.I;
});
}
I have checked in IL DASM that System.Reactive.Concurrency.DefaultScheduler+<>c__DisplayClass9`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].b__6() is compiled for the following lambda:
() =>
{
gate.Wait(() =>
{
state1 = action(state1);
});
}
It looks like the lambda () => { state1 = action(state1); }
was passed as null. But how is it possible?