Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keeping references to `IDisposable` when using the Reactive Extensions for .NET: always, never, or sometimes?

Up until now I have zealously kept every reference to the IDisposable returned from any .Subscribe(...), .Connect(...), etc, method within Rx. I've done this because of my fear that a garbage collections will dispose the disposable if I don't keep the reference.

However, I did a test in LINQPad where I did some calls GC.Collect() on a .Subscribe(...) where I didn't keep the reference and guess what? The world didn't end and the subscription ran to completion.

In further testing I found that my subscription was disposed of immediately after .OnComplete() without my intervention.

This leads me to understand that, at least for .Subscribe(...), that the only reason to keep a reference to the subscription is to force the subscription to end before its normal completion. It's more like a cancellation token.

So are all Rx disposables used for cancelling rather than keeping alive?

What, then, are the rules for hanging on to an IDisposable?

like image 944
Enigmativity Avatar asked Mar 08 '11 02:03

Enigmativity


3 Answers

No need to hold on to the IDisposable objects unless you want to unsubscribe from an observable source in the future. Similar for the Schedule methods on IScheduler, whose returned IDisposable objects can be used to cancel the scheduled action.

The garbage collector doesn't care about IDisposable object directly, nor do we implement finalizers on any of our objects, so basically lifetime management of subscriptions etc. is entirely up to you in the world of Rx. Compare it to Win32 handles if you want, with Dispose being the moral equivalent to CloseHandle.

Trivia: at some point during the design of Rx, the cancelable operations returned an Action whose invocation would cause the cancellation. Quite functionally inspired in nature, but less obvious to some. So, we decided to go with an interface that already represents a notion of resource management, and IDisposable was the obvious choice. This said, it departs a little from typical usage of the interface elsewhere in the .NET Framework:

  • You'll often just drop the IDisposable object on the floor, in particular for infinite sequences you never unsubscribe from.
  • In most cases, you won't use the using statement for Rx IDisposable objects, due to the inherent asynchronous nature of the framework.

Hope this helps,

-Bart (Rx team)

like image 188
Bart De Smet Avatar answered Nov 16 '22 20:11

Bart De Smet


The IDisposable returned by IScheduler.Schedule() is only useful if you want to cancel the scheduled action (that is, before it's happened)

The IDisposable returned by IObservable.Subscribe and IConnectableObservable.Connect are equivalent, in that disposing either will terminate the subscription to the source observable.

As for garbage collection, while Rx makes it a little more difficult to gauge, it's still bound by the rules of the GC. If the source of your observable is rooted (like the event of a UI control), then you don't need to worry about it being GC'd.

If your source is a Observable.Interval or something else is basically a recursive IScheduler call, then the scheduler should keep it alive (ie. the thread pool, task pool, or dispatcher) since the observable is rooted to the scheduler (via IScheduler.Schedule).

like image 39
Richard Szalay Avatar answered Nov 16 '22 21:11

Richard Szalay


Well, firstly I think its important to point out the garbage collection and disposal of objects / the IDisposable interface are completely separate - in particular the garbage collector will never directly dispose of an object by calling the Dispose method of an IDisposable (unless the finalizer for that object does this itself).

As for when you should hang on to an IDisposable, you should maintain a reference to any IDisposable object that you need to dispose - it sounds like I'm stating the obvious and thats because I am! :-). Unless the disposable objects lifetime is longer than a single method the using keyword is normally used:

using (var myDisposableObject = GetSomeDisposableObject())
{
    myDisposableObject.DoThings();
}

This limits the scope of myDisposableObject (which helps avoid attempting to use an object that has been disposed) and ensures that the object is correctly disposed of even if an exception is thrown.

It may be that for a certain class / API you don't need to (or shouldn't) dispose of an object that is returned to you, however that is entirely up to the API / class that returned that disposable object.

like image 4
Justin Avatar answered Nov 16 '22 21:11

Justin