-
Notifications
You must be signed in to change notification settings - Fork 782
Description
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.