Implementing an IAsyncResult

Contents

  • Overview
  • Defining IAsyncResult
  • The BeginXXX/EndXXX Contract
  • Principles for BeginXXX/EndXXX implementation
  • Purpose for our AsyncResult
  • AsyncResult Members
  • AsyncState
  • AsyncWaitHandle Implementation
  • TAsyncResult End<TAsyncResult>(IAsyncResult result)
  • void Complete(bool completedSynchronously) and void Complete(bool completedSynchronously,Exception ex)
  • Exception Handling
  • Handling Exceptions on the Callback thread
  • Using and Building your AsyncResult
  • Guidelines for building our AsyncResult
  • Sequence Diagram
  • Using the AsyncResult
  • Advanced Patterns
  • Chaining AsyncResults
  • Sequence of Execution
  • When to avoid Chaining
  • AsyncResult without the Begin/End Semantics
  • References
  • Source – AsyncResult.cs

Overview

Code complexities have always been a common issue that we face when writing non-blocking code. This series of articles tries to help in laying down some details that would hopefully help in building applications that can leverage asynchronous patterns more easily.First and foremost we need to understand that there is no magic wand to make your code asynchronous and non-blocking. Secondly if the synchronous path is already non-blocking and would not benefit from the overhead then leave it synchronous and look around your other parts of your application for optimization.There are frameworks that you can consider today like TPL (Task Parallel Library) or even Windows the Workflow Foundation as an option to building your application. Here we look at how to build one of these from scratch.

Defining IAsyncResult

MSDN defines IAsyncResult as follows – “Represents the state of an asynchronous operation“. Let us build on with this as our start point. We need to focus on its implementation since this will form the foundation for us to build our derived AsyncResults.

One of the key aspects of our AsyncResult would be to provide set of features that could be used to expose an operation as Begin/End sequence.

The BeginXXX/EndXXX Contract

  1. An AsyncResult would be returned by a BeginXXX method that would or would not complete synchronously.
  2. EndXXX is used to retrieve the return value of the operation.
  3. EndXXX can be called either before completion or after completion of our operation.

This also implicitly gives the signature of our Begin and End operations.

  • IAsyncResult BeginXXX(, AsyncCallback callback, object state)
  • T EndXXX(IAsyncResult result)
    • Where T is void or any data that you would like to return.

Principles for BeginXXX/EndXXX implementation

  1. Begins should return as soon as possible.
    1. When you invoke BeginXXX, its equivalent to saying that the method has delegated its work to someone else. If delegation is going to take longer, it is better to do the work by itself. So the idea is to offload the work to some other thread in most cases or invoke an underlying layers BeginYYY assuming that it follows a similar principle of quickly returning. WCF leverages the layering quite extensively.

       

  2. EndXXX should not be invoked unless completed.
    1. Calling End explicitly means you are going to block your thread and wait for the result. You will find that when invoking end our AsyncResult actually calls IAsyncResult.WaitHandle.WaitOne() which would cause initialization of the WaitHandle which is actually lazy initiated. If we didn’t call EndXXX it wouldn’t be allocated at all and hence proves to be quite optimized for fast operations.
    2. The appropriate way to invoke End to retrieve your result is on the Callback function. This is because the operation has already completed and hence End would return almost instantaneously with the result.

Purpose for our AsyncResult

  1. It provides a base implementation for IAsyncResult with and optimum synchronous path.
  2. It enables waiting on an AsyncResult as a synchronization primitive. This is similar to the join semantics when we cannot proceed further without the result of the operation.
  3. It should enable implementing simpler AsyncResults that do not require Begin/End semantics. These usually delegate work to other more basic AsyncResults.

AsyncResult Members

AsyncState

This holds the state for the AsyncResult. This would be returned back to the caller so that they can use this state to correlate operations.

  • This state passed in by the user during begin would be obtained in the Callback.

AsyncWaitHandle Implementation

This is quite straight forward and we can see that would be a direct implementation of the interface. We use a ManualResetEvent which is exposed as the AsyncWaitHandle. For a quick optimization we make sure that we have a lazy initialization for the ManualResetEvent. We need to create this handle only if we plan to wait on the result before it has completed so we make sure that there is no overhead in just creating an AsyncResult. ManualResetEvents are heavy objects and hence can be costly to create. This optimization also means that the cost of creating an instance of AsyncResult would not be more than that of a simple object.

[code:c#]public WaitHandle AsyncWaitHandle{    get    {        if (manualResetEvent != null)        {            return manualResetEvent;        }        lock (ThisLock)        {            if (manualResetEvent == null)            {                manualResetEvent = new ManualResetEvent(isCompleted);            }        }        return manualResetEvent;    }}[/code]

TAsyncResult End<TAsyncResult>(IAsyncResult result)

This can be used for either of the following:

  1. If the work has been completed then it will flag itself and has ended. This will be used to return will return a Typed AsyncResult.
  2. If result.IsComplete == false then we wait on the WaitHandle for completion.

A client may decide that it needs the operation to be completed before it can proceed any further and hence can decide to wait on this. This highly un-optimized and ideally we should call EndXXX only after the operation is completed and on the callback thread.

void Complete(bool completedSynchronously) andvoid Complete(bool completedSynchronously,Exception ex)

This is the method that would be used to signal that the operation has been completed. When the asynchronous operation completes we need to invoke the callback passed so the complete method would mark the AsyncResult as completed and invoke the callback.If the client is waiting on the AsyncWaitHandle through the end method then the complete would also signal this handle. Ideally we don’t want this handle to be created but if it is that means the client has decided to wait on it.The completedSynchronously flag would be used to determine if the operations completed synchronous or if it is completing through the callback thread. If it is through the callback thread we need to propagate the exception as the client would need this. This is because on the synchronous path you can bubble up the exception directly.

Exception Handling

Exception handling for asynchronous code path is bit different compared to the synchronous path.

  1. Synchronous Completion from BeginXXX
    1. Exception handling on this path is pretty much similar to how your normally do exception handling.
    2. You can bubble up your exceptions as it would be thrown on the current thread.
  2. Asynchronous completion on the Callback function.
    1. Basically when an exception is thrown from a callback thread, you should not let it bubble up to the caller. Usually your caller would be almost at the top of the callstack of a worker thread or an I/O thread. Your application would most likely crash because there wouldn’t be an exception handler to catch this kind of an exception.

Handling Exceptions on the Callback thread

To best handle an exception that happened from the callback thread, you would need to record the exception as a state somewhere, for example, in the AsyncResult as showing in the sample as a completion exception. At some point, when the End method is invoked, this exception will be thrown thus causes the exception to be thrown on the caller’s thread.So as a practice we can encapsulate our HandleCompletion() in a try catch and pass the exception on using our AsyncResult’s Complete(bool,exception) method and this would be thrown from the AsyncResult.End() method.

Using and Building your AsyncResult

We need to make sure that the overhead of using an AsyncResult is minimal even in the synchronous path. For this we could follow some typical practices.

Guidelines for building our AsyncResult

  1. The constructor is also your operation initiator.
  2. The completion callback would handle the asynchronous path. I.E. if the operation didn’t complete synchronously.
  3. Completion callback should also handle exceptions from HandleCompletion()
  4. The result is retrieved from the underlying operation result using HandleCompletion() which would be called in the synchronous or asynchronous path.
  5. Use a static delegate for your Completion callback. This will avoid the overhead of delegate creation with each construction of your AsyncResult.

Sequence Diagram

This is the path for synchronous completion.


Using the AsyncResult

The pattern below would pretty much encompass our concrete AsyncResult implementation.

[code:c#]class OperationXAsyncResult : AsyncResult{    static AsyncCallback onComplete = new AsyncCallback(OnComplete);    public OperationXAsyncResult(AsyncCallback callback, object state)        : base(callback, state)    {        IAsyncResult result = OperationHelper.BeginOperation(onComplete, this);        if (result.CompletedSynchronously)        {            this.HandleComplete(result);            base.Complete(true);        }    }    static void OnComplete(IAsyncResult result)    {        // This is only for the asynchronous path and you have to handle exceptions        // since Handle() might fail and we need to pass the exception back up.        if (result.CompletedSynchronously)            return;        OperationXAsyncResult thisPtr = result as OperationXAsyncResult;        Exception completionException = null;        try        {            thisPtr.HandleComplete(result);        }        catch (Exception ex)        {            completionException = ex;        }        base.Complete(false, completionException);    }    void HandleComplete(IAsyncResult copyResult)    {        // Get the result from the operation        OperationHelper.EndComplete(copyResult);    }}[/code]

Advanced Patterns

Chaining AsyncResults

When doing a number of Asynchronous operations, we usually end up having to chain operations that mimic sequential execution. This means that we need to invoke the BeginXXX of our next operation on completing the EndXXX of our first one.This pattern is what is referred to as a Chained AsyncResult. Basically this an implementation technique which we can use to wire up a set of Begin’s and End’s to obtain a logical sequence of execution without blocking in any manner.The diagram below shows a simple service which requires two steps. First it processes the data and then it sends the response back. In an ideal case if we can do non blocking processing like issuing asynchronous query execution to the database and chain the result on to a similar non-blocking send or chain a second operation.

Sequence of Execution

This demonstrates how AsyncResult chaining can enable wiring up operations to obtain logically sequential steps with a set of symmetric begin and ends.

When to avoid Chaining

Using or building chained Async results would not be viable sometimes.

  1. You usually need to have symmetric Begin and End’s for the operations. If not then you end pu having very complicated method definitions for using different method signatures and a generic approach might not be viable.
  2. You need to define an object that acts like a state bag to hold and pass your results in case you want to pass results from one operation to the next.
  3. The code complexity is higher and not recommended when there is no repeatable pattern in calls and you should preferable fall back to the default Begin/End conventions.

AsyncResult without the Begin/End Semantics

When implementing an AsyncResult, another pattern that we observe is that we do not require the Begin/End semantics primarily since we always depend on the callback and would never actually need to call the End on the result. We can provide such a capability as well to our AsyncResult by providing a completion method with the following contracts

  1. The completion method would be called synchronously or asynchronously.
  2. The completion can succeed or fail and hence we assume success is notified either though a Boolean or an exception.
  3. For this we expect the completion wrapper to return a Boolean so would have the signature asbool AsyncCompletion(IAsyncResult result);

A sample AsyncResult implementation would look something like this

[code:c#]class SendMessageAsyncResult:AsyncResult{    Socket socket;    public SendMessageAsyncResult(Socket socket,        byte[] buffer,        AsyncCallback callback, object state):        base(callback,state)    {        this.socket = socket;        AsyncCompletion onCompletion = this.PrepareAsyncCompletion(CompletionCallback);        IAsyncResult result = socket.BeginSend(buffer, 0, buffer.Length,                                  SocketFlags.None, onCompletion, this);    }    bool CompletionCallback(IAsyncResult result)    {        SendMessageAsyncResult ar = (SendMessageAsyncResult)result.AsyncState;        socket.EndSend(result);        return true;    }}[/code]

Please refer the sample for usage example and an implementation of the IAsyncResult.

References

 

Source – AsyncResult.cs

AsyncResult.cs (6.15 kb)