none
DeclinePermanently

    Question

  • <Moved from channel9 forum>
    Shouldn't DeclinePermanently() instead be something like PostLast(params T[] lastItems) ? At some point, I am going to be the last poster. But doing this is not atomic in terms of atomically posting final item.

     

    Post(1);
    // Race here <= 
    DeclinePermanently();

     

    Another producer could (and eventually will) sneak in between the calls. The only way to avoid that is the caller using some extra lock mechanics, which your tryin to avoid. tia

    Friday, February 04, 2011 4:33 PM

Answers

  • Hi William-

    Thanks for the suggestion.  It's an interesting line of reasoning.

    The genesis of the feature is that we wanted there to be a general mechanism whereby external entities could say "we're done now".  You're right that if multiple producers are targeting a block, there could be a race between all of them posting and one of them calling DeclinePermanently, but that would be discouraged.  The idea behind DeclinePermanently is that all producers collectively inform the target that it won't be sent any more data... in a sense, this is less about the atomicity of the declining and more about letting the block know when it's ok to shut itself down, e.g. once it's done processing everything it was previously given and once it knows it won't be given anything new, it can transition to being completed.  So, in a system where there's just one producer for a target, that producer can call DeclinePermanently whenever it knows its done producing more data. In a system where there are multiple known producers, you can coordinate the producers to only inform the target of it not receiving any more data once all of the producers have completed, e.g.

        Task.Factory.ContinueWhenAll(
            new [] { producer1.CompletionTask, producer2.CompletionTask },
            _ => t.DeclinePermanently());

    And in a system where you have an unknown number of producers that may all be generating data at any arbitrary time, it's not valid for anyone to call DeclinePermanently (or even the suggested "PostLast" method), since you have no idea if or when another producer may come along and try to provide additional items.  Given the flexibility we wanted here, and the kinds of patterns we're trying to support, we felt that a separation of posting and informing of completion was warranted.  You could argue that we should provide both, i.e. the ability to atomically supply the last bits of data and decline... it's an interesting idea that we'll ponder.  Do you have a particular use case or scenario in mind where this would be important, given what I've described?  I'd love to hear about it.

    As an aside, there are also others ways a block can start declining.  For example, you can configure a JoinBlock to only produce a single output and not accept any more inputs after that.

    Finally, we are considering some modifications here, such as moving the DeclinePermanently method down to the base IDataflowBlock interface, and potentially renaming it to "Complete" or "RequestCompletion" or something like that.  This would have both Complete and CompletionTask on the same interface, and would also allow the Complete method to be used for pure sources.  Thoughts on that?

    Thanks!

    Friday, February 04, 2011 5:16 PM

All replies

  • Hi William-

    Thanks for the suggestion.  It's an interesting line of reasoning.

    The genesis of the feature is that we wanted there to be a general mechanism whereby external entities could say "we're done now".  You're right that if multiple producers are targeting a block, there could be a race between all of them posting and one of them calling DeclinePermanently, but that would be discouraged.  The idea behind DeclinePermanently is that all producers collectively inform the target that it won't be sent any more data... in a sense, this is less about the atomicity of the declining and more about letting the block know when it's ok to shut itself down, e.g. once it's done processing everything it was previously given and once it knows it won't be given anything new, it can transition to being completed.  So, in a system where there's just one producer for a target, that producer can call DeclinePermanently whenever it knows its done producing more data. In a system where there are multiple known producers, you can coordinate the producers to only inform the target of it not receiving any more data once all of the producers have completed, e.g.

        Task.Factory.ContinueWhenAll(
            new [] { producer1.CompletionTask, producer2.CompletionTask },
            _ => t.DeclinePermanently());

    And in a system where you have an unknown number of producers that may all be generating data at any arbitrary time, it's not valid for anyone to call DeclinePermanently (or even the suggested "PostLast" method), since you have no idea if or when another producer may come along and try to provide additional items.  Given the flexibility we wanted here, and the kinds of patterns we're trying to support, we felt that a separation of posting and informing of completion was warranted.  You could argue that we should provide both, i.e. the ability to atomically supply the last bits of data and decline... it's an interesting idea that we'll ponder.  Do you have a particular use case or scenario in mind where this would be important, given what I've described?  I'd love to hear about it.

    As an aside, there are also others ways a block can start declining.  For example, you can configure a JoinBlock to only produce a single output and not accept any more inputs after that.

    Finally, we are considering some modifications here, such as moving the DeclinePermanently method down to the base IDataflowBlock interface, and potentially renaming it to "Complete" or "RequestCompletion" or something like that.  This would have both Complete and CompletionTask on the same interface, and would also allow the Complete method to be used for pure sources.  Thoughts on that?

    Thanks!

    Friday, February 04, 2011 5:16 PM
  • For folks finding this answer via Google, this advice is out-of-date.  DeclineOnCompletion() no longer exists in modern TDF (shipped around same time as .NET 4.5, fall 2012). 

    For the new way, see: http://blogs.msdn.com/b/pfxteam/archive/2010/10/28/10081950.aspx

    If the link doesn't work, try "DataflowLinkOptions" and set "new DataflowLinkOptions() { PropagateCompletion = true }"

    Tuesday, December 18, 2012 4:19 AM