2

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?

Pellared
  • 1,242
  • 2
  • 14
  • 29
  • 1
    Can You post some code please? – ntohl Jul 22 '15 at 13:01
  • @ntohl Done - but I do not know if this is the right code that reproduces the issue. But it probably was something like this... – Pellared Jul 22 '15 at 13:30
  • Please don't make us try and guess if your code is right or not. Please post the correct code but first test it yourself. – Enigmativity Jul 22 '15 at 14:57
  • This is what I am doing all the time and this is why I have not posted the code in the beginning. However the code looked all the time like this. The only things that might have changed are the Thread.Sleeps (the intervals and its positions in code). – Pellared Jul 23 '15 at 07:35
  • That is working, and not throwing anything for me, but I would like to warn You about the code, where the `//Thread.Sleep(...)` comment is, will be executed after each `Console.WriteLine(n)`. – ntohl Jul 23 '15 at 08:00
  • Also watch out, that the last commands after the sleep will run after mass dispose. I don't know if it's a problem. – ntohl Jul 23 '15 at 12:22
  • @ntohl These are just some tries to reproduce the the problem... I have added more information regarding the Reactive Extensions implementation - I do not understand how is it even possible that this exception was thrown! Am I missing some knowledge regarding .NET Runtime or there is some kind of bug in the Runtime or Compiler? – Pellared Jul 27 '15 at 10:59
  • I've closed your other question, where you get an ObjectDisposedException in the same method with the same example code. It is highly likely that you're basically just disposing of a subscription before you're done with it, ie. before all the code executing in relation to it has completed. It is a race condition. The exception occurs in AsyncLock.Wait in both questions, it is most likely that you have disposed of the lock, and then some code tries to call Wait on it. – Lasse V. Karlsen Jul 28 '15 at 06:26
  • @LasseV.Karlsen Yes we are disposing the subscription before the handler finished the job. However I still do not understand why we have this exception. The AsyncLock is not a disposable! Are you sure that RX is not race condition is possible? Moreover why you have closed the other question? Different exception and the code is also different! In this question I have a "demo" where I try to reproduce the Exception that comes from the second question... – Pellared Jul 28 '15 at 08:09
  • 1
    This should reproduce the race condition's exception, but it does not: `while (true) { var x = Observable.Interval(TimeSpan.FromMilliseconds(0)).Repeat(); var y = x.Subscribe(_ => Console.Write("a")); y.Dispose(); }`. It sometimes write a, sometimes not, but not throwing exception. – ntohl Jul 28 '15 at 09:33
  • @ntohl Exactly. Generally even the existing "demo" code should reproduce the race condition. – Pellared Jul 28 '15 at 10:24
  • @ntohl Your answer with throwing Exception from the subscription was correct! The EventViewer does not show the whole stack trace (which could be seen in the console), Do you know why? I have the same problem as here: https://connect.microsoft.com/VisualStudio/feedback/details/799828/net-runtime-sends-incomplete-stacktrace-to-event-viewer – Pellared Jul 29 '15 at 09:59
  • @Pellared My answer was about selector is null in one case. Should I undelete the answer? When You started as a console application, You found where is the selector null? – ntohl Jul 29 '15 at 11:13

1 Answers1

1

It is possible to reproduce the exception. You have to pass a selector function, that is just null. Maybe You forgot to initialize in every case the function that is in the Select block. Or the instance of the class is not existing anymore, when the scheduler wants to run it. This is the way how it is possible:

Func<long, int> selector = null;
var x =  Observable.Interval(TimeSpan.FromSeconds(1)).Select(selector);
ntohl
  • 2,067
  • 1
  • 28
  • 32