Skip to content

Possible bug in System.Reactive.Windows.Forms/Reactive/Concurrency/ControlScheduler.cs #269

@forgetaboutit

Description

@forgetaboutit

Environment: Application running on .NET Framework 4.6 and Rx 2.2.5.0 including the System.Reactive.Windows.Forms shim (code of the current Rx release 3.0.0 and master seems to exhibit the same problem).

I ran into a situation at work where a Form instance has already been closed/disposed of when the ControlScheduler (via ObserveOn(form)) tries to BeginInvoke on it.

ControlScheduler.Schedule<TState>(TState, Func<IScheduler, TState, IDisposable>) explicitly checks in the delegate to BeginInvoke if the control has not been disposed of yet. It doesn't perform this check, however, before scheduling on the control's event loop. I would classify this as a race condition. To my understanding, cancelling subscriptions is best-effort, which leads to the problem I observed (no pun intended).

I fixed the problem by creating a custom scheduler cloned from ControlScheduler and checking the _control field for IsDisposed before calling BeginInvoke.

Current code:

var d = new SingleAssignmentDisposable();

_control.BeginInvoke(new Action(() =>
{
    if (!d.IsDisposed)
        d.Disposable = action(this, state);
}));

My fix:

var d = new SingleAssignmentDisposable();

// Prevent scheduling on disposed controls
if (_control.IsDisposed) return d;

_control.BeginInvoke(new Action(() =>
{
    if (!d.IsDisposed)
        d.Disposable = action(this, state);
}));

I'm not sure if the current behavior is accidential or intentional. It'd be great if an expert on the topic could comment on whether my current fix is a good idea and if it should be fixed in Rx itself or handled by user code.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions