title: TPL dataflow working with async actions
body: <p>I'm experimenting with TPL dataflow by porting some old socket code over for using TPL dataflow and the new async features. Although the API feels rock solid, my code still ends up feeling messy. I'm wondering if I'm missing something here.</p>
  
  <p>My requirements is as follows: A socket class exposes: Open, Close, Send and Receive methods. All return a Task and therefore are async. Open and Close are atomic. Send and Receive can work next to each other although both can only handle 1 command a time.</p>
  
  <p>Logically this brings me to the next piece of code for internal control:</p>
  
  <pre><code>// exposing an exclusive scheduler for connectivity related tasks and a parallel scheduler where send and receive can work with
  private readonly ConcurrentExclusiveSchedulerPair exclusiveConnectionSchedulerPair;
  private readonly ActionBlock&lt;Action&gt; connectionBlock;
  private readonly ActionBlock&lt;Action&gt; sendBlock;
  private readonly ActionBlock&lt;Action&gt; receiveBlock;
  
  // within the constructor:
  this.exclusiveConnectionSchedulerPair = new ConcurrentExclusiveSchedulerPair();
  this.connectionBlock = new ActionBlock&lt;Action&gt;(action =&gt; action(), new ExecutionDataflowBlockOptions()  { TaskScheduler = exclusiveConnectionSchedulerPair.ExclusiveScheduler });
  this.sendBlock = new ActionBlock&lt;Action&gt;(action =&gt; action(), new ExecutionDataflowBlockOptions()    { TaskScheduler = exclusiveConnectionSchedulerPair.ConcurrentScheduler });
  this.receiveBlock = new ActionBlock&lt;Action&gt;(action =&gt; action(), new ExecutionDataflowBlockOptions() { TaskScheduler = exclusiveConnectionSchedulerPair.ConcurrentScheduler });
  </code></pre>
  
  <p>So far all good. I can safely send actions to the Send and Receive block without having to worry about a running connection related action in the meantime. Also ActionBlock ensures that multiple calls to send are synchronized (idem for receive, close and open).</p>
  
  <p>The problem is that there is no easy way for an action to communicate a task back to the poster. Right now i'm using a TaskCompletionSource to communicate a result back. Like:</p>
  
  <pre><code>public Task Send(ArraySegment&lt;byte&gt; buffer, CancellationToken cancellationToken)
  {
      TaskCompletionSource&lt;object&gt; resultCompletionSource = new TaskCompletionSource&lt;object&gt;();
  
      sendBlock.Post(async () =&gt;
      {
          if (!tcpClient.Connected)
              throw new InvalidOperationException("Cant send when not open");
          else
          {
              await sendStream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
              resultCompletionSource.SetResult(null);
          }
      });
  
      return resultCompletionSource.Task;
  }
  </code></pre>
  
  <p>which just feels ugly and clumsy. My question is: <strong>Is there a way to synchronize the workflow using TPL without having to use a TaskCompletionSource for communication in between?</strong></p>
  
  <p>Thanks!</p>