NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Asynchronous Programming Model Design Pattern

As an example, consider the class in the following section.

Example Class

This class factorizes a number into its two prime factors.

// Class the factorizers the number
public class PrimeFactorizer
{
   public bool Factorize(
                int factorizableNum,  
                ref int primefactor1,
                ref int primefactor2)
   {
      primefactor1 = 1;
      primefactor2 = factorizableNum;

      // Factorize using a low tech approach
      for (int i=2;i<factorizableNum;i++)
      {
         if (0 == (factorizableNum % i))
         {
            primefactor1 = i;
            primefactor2 = factorizableNum / i;
            break;
         }
      }

      if (1 == primefactor1 )
         return false;
      else
         return true   ;
   }
}

Caller defines a delegate for the method

The following code snippet shows the caller defining a pattern for the invoking Factorize method asynchronously:

// Define the delegate
public delegate bool FactorizingCallback(
         int factorizableNum, 
         ref int primefactor1,
         ref int primefactor2);

// Creating an instance of the Factorizer
PrimeFactorizer pf = new PrimeFactorizer();

// Creating a delegate on the Factorize method on the Factorizer
FactorizingCallback fd = new FactorizingCallback(pf.Factorize);

Compiler and NGWS Runtime support

When the compiler emits the FactorizingCallback delegate class after parsing its definition at the first line above, it will generate the BeginInvoke and EndInvoke methods in addition to the Invoke method.

class FactorizingCallback : delegate
{
   public bool Invoke(
         int factorizableNum,  
         ref int primefactor1,
         ref int primefactor2);

   // Supplied by the compiler
   public IAsyncResult BeginInvoke(
         int factorizableNum,  
         ref int primefactor1,
         ref int primefactor2,
         AsyncCallback cb,
         Object AsyncObject
         );

   // Supplied by the compiler
   public bool EndInvoke(
         ref int primefactor1,
         ref int primefactor2,
         IAsyncResult ar);
}

Client side programming model

The following code snippet demonstrates the client-side programming model for invoking the Factorize method asynchronously.

Example

The class receives the results of the Async call on a callback.

public delegate bool FactorizingCallback(
         int factorizableNum, 
         ref int primefactor1,
         ref int primefactor2);

// Class that receives a callback when the the results are available
public class ProcessFactorizedNumber
{
   private int _ulNumber;

   public ProcessFactorizedNumber(int number)
   {
      _ulNumber = number;
   }

   // Note the qualifier one-way.
   [OneWayAttribute()]
   public void FactorizedResults(IAsyncResult ar)
   {
      int factor1=0, factor2=0; 

      // Extract the delegate from the AsyncResult  
      FactorizingCallback fd = (FactorizingCallback)ar.AsyncObject;

      // Obtain the result
      fd.EndInvoke(ref factor1, ref factor2, ar);

      // Output results
      Console.WriteLine("On CallBack: Factors of {0} : {1} {2}", 
                    _ulNumber, factor1, factor2);
   }
}

Async Variation 1 – call

// Async Variation 1
// The ProcessFactorizedNumber.FactorizedResults callback 
// is called when the call completes.
public void FactorizeNumber1()
{
   // Client code
   PrimeFactorizer pf = new PrimeFactorizer();
   FactorizingCallback fd = new FactorizingCallback(pf.Factorize);

   // Async Variation 1
   int factorizableNum = 1000589023, temp=0; 

   // Create an instance of the class which is going 
   // to called when the call completes
   ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum);

   // Define the AsyncCallback delegate
   AsyncCallback cb = new AsyncCallback(fc.FactorizedResults);

   // Can stuff any object as the state object
   Object state = new Object();

   // Asynchronously invoke the Factorize method on pf
   IAsyncResult ar = fd.BeginInvoke(
                        factorizableNum, 
                        ref temp, 
                        ref temp, 
                        cb, 
                        state); 

   //
   // Do some other useful work
   //. . .
}

Async Variation 2

// Async Variation 2
// Waits for the result
public void FactorizeNumber2()
{
   // Client code
   PrimeFactorizer pf = new PrimeFactorizer();
   FactorizingCallback fd = new FactorizingCallback(pf.Factorize);

   // Async Variation 1
   int factorizableNum = 1000589023, temp=0; 

   // Create an instance of the class which is going 
   // to called when the call completes
   ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum);

   // Define the AsyncCallback delegate
   AsyncCallback cb = 
   new AsyncCallback(fc.FactorizedResults);

   // Can stuff any object as the state object
   Object state = new Object();

   // Asynchronously invoke the Factorize method on pf
   IAsyncResult ar = fd.BeginInvoke(
                     factorizableNum, 
                     ref temp, 
                     ref temp, 
                     null, 
                     null); 

   ar.AsyncWaitHandle.WaitOne(10000, false);

   if (ar.IsCompleted)
   {
      int factor1=0, factor2=0; 

      // Obtain the result
      fd.EndInvoke(ref factor1, ref factor2, ar);

      // Output results

      Console.WriteLine("Sequencial : Factors of {0} : {1} {2}", 
                    factorizableNum, factor1, factor2);

   }
}

Note that if the ProcessFactorizedNumber is a context-bound class that requires synchronized / thread- affinity context, the callback is dispatched through the context dispatcher infrastructure. In other words, the callback itself may execute asynchronously with respect to its caller for such contexts. That is precisely the semantics of the one-way qualifier on method signatures. It means that any such method call may execute synchronously or asynchronously with respect to caller and the caller cannot make any assumptions about completion of such a call when execution control returns to it.

Also, calling EndInvoke before the asynchronous operation is complete will block the caller. Calling it second time with the same IAsyncResult is undefined.

Summary of Asynchronous Design Pattern

The server splits asynchronous operation into its two logical parts: the part that takes input from the client and kicks off the asynchronous operation and the part that supplies results of the asynchronous operation to the client. In addition to the input needed for the async operation, the first part also takes an AsyncCallback delegate to be called when the asynchronous operation is completed. The first part returns a waitable object that implements IAsyncResult interface used by the client to determine the status of the asynchronous operation. The Server typically also utilizes the waitable object it returned to the client to maintain any state associated with asynchronous operation. The client uses the second part to obtain the results of the asynchronous operation by supplying the waitable object.

Options available to client for initiating asynchronous operations:

  1. Supply the callback delegate when beginning asynchronous
  2. Not supply the callback delegate when beginning asynchronous operation.

Options available to the client for completing asynchronous operations are:

  1. Poll the returned IAsyncResult.IsCompleted property for completion.
  2. Attempt to complete the operation prematurely thereby blocking until the operation completes.
  3. Wait on the IAsyncResult object. The difference between this and previous option is that the client can use timeouts to wake up periodically.
  4. Complete the operation inside the Async callback routine.