none
Asynchronous Dispose patterns

    Question

  • Another "best practice" question. :)

    I'm planning to build some larger reusable components that can be part of a dataflow network, and I'm curious about disposal patterns.

    On my first attempt, I actually implemented IDisposable with cancellation semantics. But this didn't add much more than the CancellationToken already passed into the constructor, and these types are likely to be used where a "using" statement doesn't make sense (e.g., just linked into a mesh). Also, I didn't like the semantics of "you have to await this thing before it goes out of scope".

    Then I thought: there's already an "asynchronous dispose" pattern used by the dataflow blocks! [Complete | Fault | CancellationToken] will (asynchronously) complete the Completion task.

    My larger components are not individual blocks (they expose multiple block endpoints as properties). Even though they're not blocks, would a "Task Completion { get; }" be a good convention for asynchronous disposal notification? If so, should I also add Complete() and Fault()? And if I do that, should I implement IDataflowBlock? That seems wrong, but IDataflowBlock really only specifies asynchronous Disposal semantics...

    More info:

    I'm starting with two larger components: a child process (with I/O redirected into dataflow blocks) and a client TCP/IP socket (with I/O redirected into dataflow blocks). Both of these have clear completion semantics without an explicit Complete()/Fault(): a child process is complete when it has exited and all stdout and stderr have been read; and a TCP/IP socket is complete when its sending dataflow block is completed, the disconnect operation has completed (which does require an end-user request), and all receiving data has been read.

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible


    Monday, July 23, 2012 2:36 AM

Answers

  • As you say, IDataflowBlock is fairly generic in that it's not directly tied to TPL Dataflow via the members it exposes: Complete, Fault, and Completion.  As a result, we contemplated naming this something broader, like IAsyncCompletable, but we stuck with the IDataflowBlock nomenclature for this particular interface in order to tie this exact interface more closely with dataflow blocks, and because it's not always the case that asynchronously completable things can be forcefully completed from the outside or forcefully faulted from the outside, e.g. ConcurrentExclusiveSchedulerPair has a Task-returning Completion member and a Complete() method, but no Fault method because it doesn't make as much sense with this type to fault it externally.  For your own types, it'd be perfectly reasonable to expose any subset of this triad of members, either just a Completion member or also one or both of Complete or Fault.

    Thursday, November 29, 2012 7:50 PM
    Owner

All replies

  • I think that it is OK to keep using the semantic of Completion, Complete and Fault.

    you can consider using the CancellationTokenSource.CreateLinkedTokenSource API in order to consolidate 
    the different cancellation option into a single consolidate cancellation.

    or for absolute customization of your completion task you can consider using the TaskCompletionSource API which
    is the most flexible pattern for having a task semantic.


    Bnaya Eshet

    Saturday, August 11, 2012 7:07 PM
  • As you say, IDataflowBlock is fairly generic in that it's not directly tied to TPL Dataflow via the members it exposes: Complete, Fault, and Completion.  As a result, we contemplated naming this something broader, like IAsyncCompletable, but we stuck with the IDataflowBlock nomenclature for this particular interface in order to tie this exact interface more closely with dataflow blocks, and because it's not always the case that asynchronously completable things can be forcefully completed from the outside or forcefully faulted from the outside, e.g. ConcurrentExclusiveSchedulerPair has a Task-returning Completion member and a Complete() method, but no Fault method because it doesn't make as much sense with this type to fault it externally.  For your own types, it'd be perfectly reasonable to expose any subset of this triad of members, either just a Completion member or also one or both of Complete or Fault.

    Thursday, November 29, 2012 7:50 PM
    Owner