Awaitable Pattern

How do you determine what types could be awaited ? That is one question that often comes to mind and the most common answer would be

  • Task
  • Task<TResult>
  • void – Though it should be strictly avoided

However, are we truely restricted to them ? What are the other Types that could be awaited ? The anwer lies in the awaitable pattern.

Awaitable Pattern

The awaitable pattern requires to have a parameterless instance or static non-void method GetAwaiter that returns an Awaitable Type.

public T GetAwaiter()

Where T, the awaiter Type implements
* INotifyCompletion or ICriticallyNotifyCompletion
* Has a boolean instance property IsCompleted
* Non-generic parameterless instance method GetResult

Approach 01 – Use TaskAwaiter

Let’s begin by an example that reusing Task or Task<TResult> awaiter instead of creating our own awaiter. For demonstration purpose, we assume a requirement where in we should be able to use the Process Class to execute given command asynchronously and return the result. Ideally, we should be able to do the following.

var result = await "dir"

The above command should be able to execute “dir” command using Process and return the result. We will begin by writing the GetAwaiter extension method for string.

public static class CommandExtension
{
public static TaskAwaiter GetAwaiter(this string command)
{
var tcs = new TaskCompletionSource();
var process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = $"/C {command}";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.EnableRaisingEvents = true;
process.Exited += (s, e) => tcs.TrySetResult(process.StandardOutput.ReadToEnd());
process.Start();
return tcs.Task.GetAwaiter();
}
}

The above code reuses the Awaiter for Task<TResult>. The method initiates the Process and use TaskCompletionSource to set the result in the Exited event of Process. If you examine the source code of TaskAwaiter , you can observe that it implements the ICriticallyNotifyCompletion interface and has the IsCompleted Property as well as GetResult method.

Let us now write some demonstrative code.

private async void btnDemoUsingTaskAwaiter_Click(object sender, EventArgs e)
{
AppendToLog($"Started Method {nameof(btnDemoUsingTaskAwaiter_Click)}");
await InvokeAsyncCall();
AppendToLog($"Continuing Method {nameof(btnDemoUsingTaskAwaiter_Click)}");
}
private async Task InvokeAsyncCall()
{
AppendToLog($"Starting Method {nameof(InvokeAsyncCall)}");
var result = await "dir";
AppendToLog($"Recieved Result, Continuing Method {nameof(InvokeAsyncCall)}");
AppendToLog(result);
AppendToLog($"Ending Method {nameof(InvokeAsyncCall)}");
}
public void AppendToLog(string message)
{
logText.Text += $"{Environment.NewLine}{message}";
}

Approach 02 – Implement Custom Awaiter

Let us now assume another situation where-in, the method is invoked in a non-UI thread, and the continuation requires you to update Controls in UI (in other words, needs UI Thread). For purpose of learning, let us find a solution for the problem by implementing a Custom Awaiter.

We will begin by defining our Custom Awaiter that satisfies the laws defined in the Awaitable Pattern section above.

public static class CommandExtension
{
public static UIThreadAwaiter GetAwaiter(this string command)
{
var tcs = new TaskCompletionSource();
Task.Run(() =>
{
var process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = $"/C {command}";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.EnableRaisingEvents = true;
process.Exited += (s, e) => tcs.TrySetResult(process.StandardOutput.ReadToEnd());

process.Start();
});

return new UIThreadAwaiter(tcs.Task.GetAwaiter().GetResult());
}
}
public class UIThreadAwaiter : INotifyCompletion
{
bool isCompleted = false;
string resultFromProcess;

public UIThreadAwaiter(string result)
{
resultFromProcess = result;
}
public bool IsCompleted => isCompleted;
public void OnCompleted(Action continuation)
{
if (Application.OpenForms[0].InvokeRequired)
Application.OpenForms[0].BeginInvoke((Delegate)continuation);

}

public string GetResult()
{
return resultFromProcess;
}
}

The UIThreadAwaiter implements the INotifyCompletion interface. As one could asssume from the code above, the ability to use UI Thread for continuation tasks are executed with the help of BeginInvoke.

Let us now write some demo code to demonstrate the custom awaiter.

private void btnExecuteOnDifferentThread_Click(object sender, EventArgs e)
{
AppendToLog($"Started Method {nameof(btnExecuteOnDifferentThread_Click)}");
Task.Run(() => InvokeAsyncCall()).ConfigureAwait(false);
AppendToLog($"Continuing Method {nameof(btnExecuteOnDifferentThread_Click)}");
}

private async Task InvokeAsyncCall()
{
var result = await "dir";
AppendToLog($"Recieved Result, Continuing Method {nameof(InvokeAsyncCall)}");
AppendToLog(result);
AppendToLog($"Ending Method {nameof(InvokeAsyncCall)}");
}
public void AppendToLog(string message)
{
try
{
txtLog.Text += $"{Environment.NewLine}{message}";
}
catch (Exception ex)
{
var errorMessage = $"Exception:{ex.Message}{Environment.NewLine}{Environment.NewLine}Message:{message}";
MessageBox.Show(errorMessage, "Error");
}
}

private async void btnExecuteOnSameThread_Click(object sender, EventArgs e)
{
AppendToLog($"Started Method {nameof(btnExecuteOnDifferentThread_Click)}");
await InvokeAsyncCall();
AppendToLog($"Continuing Method {nameof(btnExecuteOnDifferentThread_Click)}");
}

As demonstrated in examples above, the await keyword is not restricted to few types. Instead, we could create our Custom Awaiter which statisfies the Awaitable Pattern.

Complete code for this post is available on my Github

Early Exceptions for Iterator and async Methods

One of the first things you would need to be vary of while working with Iterator methods and async methods is exception handling. Both Iterator methods and async methods delay the execution of your code, right until you need the result, but it also results in delayed exceptions.

Late Exception With Iterator Methods

Let’s consider a typical example. The following method generates positive integers from 1 to ‘n’. Of course, you could do this with Enumerable.Range but, that’s not the whole point.

void Main()
{
var maxValue = 0;
var generatedCollection = GenerateSequence(maxValue);
Console.WriteLine("No Exception raised yet");
foreach(var item in generatedCollection)
Console.WriteLine(item);
}

IEnumerable<int> GenerateSequence(int maxValue)
{
if(lastNumber <= 0)
throw new Exception($"Parameter {nameof(maxValue)} should be positive, non-zero");

for(int i=0;i<maxValue;i++)
yield return i;
}

The above method has a precondition which validates if the maxValue parameter is a positive, non-zero number. However if one was to execute the code, one can notice that exception isn’t raised until the result is consumed. This is expected. But, there could be scenarios where one might require you to have methods raise early exceptions.

Early Exception With Iterator Methods

Let’s rewrite the method with a little bit of tweak1.

void Main()
{
var maxValue = 0;
var generatedCollection = GenerateSequence(maxValue);
Console.WriteLine("No Exception raised yet");
foreach(var item in generatedCollection)
Console.WriteLine(item);
}

IEnumerable<int> GenerateSequence(int lastNumber)
{
if(lastNumber <= 0)
throw new Exception($"Parameter {nameof(lastNumber)} should be positive, non-zero");

return Generate();
IEnumerable<int> Generate()
{
for(int i=0;i<lastNumber;i++)
yield return i;
}
}

Try executing the code, and you can notice that now it supports early exception. This very approach could be used for async method. Let’s do the problematic code first.

Late Exception with async Methods

async Task<int> Increment(int numberToPrint)
{
if(numberToPrint <= 0)
throw new Exception($"Parameter {nameof(numberToPrint)} should be positive, non-zero");

return await Task.FromResult(++numberToPrint);
}

// Client code
var value = 0;
var task = Increment(value);
Console.WriteLine("No Exception raised yet");
await task;

The problem with late exceptions is visible in the above code as well. The exception is not raised where the method is called (in code), but rather it is raised when the Task is awaited.

Early Exception with async Methods

Task<int> Increment(int numberToPrint)
{
if(numberToPrint <= 0)
throw new Exception($"Parameter {nameof(numberToPrint)} should be positive, non-zero");

return Increment();
async Task<int> Increment()
{
return await Task.FromResult(++numberToPrint);
}
}

// Client Code
var value = 0;
var task = Increment(value);
Console.WriteLine("No Exception raised yet");
await task;

Reference


  1. Reference : More Effective C# : version 7.0 

Allocation free ‘async’ Methods

Task Asynchronous Programming (TAP) model will go down as one of the landmark of C# language revolution. The typical method signature with return type Task/Task<T> has since then made significant appearances in our programming life.

But despite all its glorious functionalities, it needs to be noted that it comes at a certain cost – performance bottleneck, especially when you are working in tight loops in a memory constrained environment. Memory allocations of Task/Task<T> (note both are references types) impacts the performances adversely in critical paths. What developers longed for in such scenarios is a comparatively light-weight value type, which could be returned instead of the references types in the async methods. This was a limitation till C# 7.x happened.

Starting from C# 7, an async method could return any type that has an accessible ‘GetAwaiter’ method, which returns an object that implements INotifyCompletion and ICriticalNotifyCompletion Interfaces. The ValueTask<T> is a "goto" under such circumstances. Being a value type, it doesn’t require additional memory allocations. This is extremely useful when you already have the ‘cached result’ and the operation can be completed synchronously. But like Task, it comes at a cost, which we will discuss later in the article.

To begin with, let’s write some sample code to demonstrate the need of ValueTask<T> or rather, need for replacing Task<T>. Consider the following code.

public interface IWeatherService
{
  Task<double> GetWeather();
}

public class MockWeatherService : IWeatherService 
{
  public async Task<double> GetWeather() => await Task.FromResult(30);
}

The above code demonstrate a Mock Weather Service. In real world scenario, this might be accessing a Web API to fetch current weather details. For the sake of example, only the temperature is fetched, which is hard coded. Consider GetWeather to be invoked quite frequently (say in a loop) by the Client. There is a chance this might turn disastrous if this was executed in a memory constraint environment.

Ideally, you wouldn’t want to fetch from the Web API always. You would prefer to cache the result, and only fetch from the Web API only after specific interval (from the time the last value was cached). Despite doing the operation (fetch from cache) synchronously, you are allocating memory for the reference type Task<T>. One would have wished to avoid this scenario and yet use the async syntax.

This is possible by making use of the ValueTask<T> DataType. Let’s change our syntax a bit.

public interface ICachedWeatherService
{
  ValueTask<double> GetWeather();
}
public class MockCachedWeatherService : ICachedWeatherService
{
  private DateTime _lastAccessedTime = DateTime.MinValue;
  private double _lastAccessedValue = 30;
  public async ValueTask<double> GetWeather()
  {
    if(DateTime.Now - _lastAccessedTime < TimeSpan.FromSeconds(10))
    {
      return _lastAccessedValue;
    }
    _lastAccessedTime = DateTime.Now;
    return await Task.FromResult(30);
   }
}

In the above code, we have changed the signature of the method (return type) to ValueTask<T>. Since it implements all the necessary requirements that is needed for async method (as discussed earlier), we could still use the same signature in client to access the method, just like in Task based syntax.

Using Task Return Type

var _weatherService = new MockWeatherService();
           
for(int i = 0; i < 10; i++)
{
  await Task.Delay(5000);
  var currentWeather = await _weatherService.GetWeather();
  Console.WriteLine($"Weather @ {DateTime.Now.ToString("hh:mm:ss")}={currentWeather}");
}

Using ValueTask Return Type

var _cachedWeatherService = new MockCachedWeatherService();
for (int i = 0; i < 10; i++)
{
  await Task.Delay(5000);
  var currentWeather = await _cachedWeatherService.GetWeather();
  Console.WriteLine($"Weather @ {DateTime.Now.ToString("hh:mm:ss")}={currentWeather}");
}

Check Complete Code Sample

Difference is, when the method needs to return from the cache, it returns ValueTask avoiding the pain of creating the Task Reference Type. However, when it has to go through the Web API, it can still return Task<T>.

Even though ValueTask seem highly useful, you need to maintain caution and should rather use it only if there is considerable performance issues that were measured. The reason for this is that, despite the ValueTask helps you avoid unnecessary allocation, it comes with its own cost. The ValueTask is a two field structure compared to Task<T>, which despite being reference type has single field. More fields would result in more data to copy. It fact Microsoft warns us in the API documentation itself.

Quoting from Documentation

There are tradeoffs to using a ValueTask<TResult> instead of a Task<TResult>. For example, while a ValueTask<TResult> can help avoid an allocation in the case where the successful result is available synchronously, it also contains two fields whereas a Task<TResult> as a reference type is a single field. This means that a method call ends up returning two fields worth of data instead of one, which is more data to copy. It also means that if a method that returns one of these is awaited within an async method, the state machine for that async method will be larger due to needing to store the struct that’s two fields instead of a single reference.

Hence, stick onto Task/Task<T> for almost all your use, until performance analysis pushes you to use ValueTask<T>. Complete code samples described in the example is available in my Github

Lock for Async Methods

Despite the intended nature, we seldom might come across situation where we would want to place a lock around an async statement and make it behave differently, especially when accessed from different threads. Let’s try to mock the scenario first to demonstrate the issue.

The lock-async Problem

Consider the following code snippet.

void Main()
{
     Parallel.For(0,5,async (x,y)=>{await TestMethod();});
}

async Task<bool> TestMethod()
{
Console.WriteLine("In");
// Do some tasks here. For demonstration purpose we will use Task.Delay to mock a long running task.
await Task.Delay(2000);
Console.WriteLine("Out");
return await Task.FromResult(true);
}

The output of the above code would be as following, understandably allowing multiple threads to access the ‘intended long running process code‘.

In
In
In
In
In
Out
Out
Out
Out
Out

The most common workaround is placing a lock statement around the code. Try placing a lock around the await statement and you would surprised to know you are not allowed to do so.

“Cannot await in the body of a lock statement”

This is where the trouble starts.

The Solution

The workaround for the issue with Semaphore. Instead of lock, you could use a semaphore to ensure only one/limited threads access the async method at a time.

Let us rewrite the code with semaphore now.

void Main()
{
       Parallel.For(0,5,async (x,y)=>{await TestMethod();});
}
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);
async Task<bool> TestMethod()
{
await semaphoreSlim.WaitAsync();
try
{
Console.WriteLine("In");
await Task.Delay(2000);
Console.WriteLine("Out");
}
finally
{
semaphoreSlim.Release();
}
return await Task.FromResult(true);
}

As you can observe, the compile would accept this solution as desired. The output of following can seen below.

In
Out
In
Out
In
Out
In
Out
In
Out

Enjoy coding

Async : IProgress and Immutability

In the previous post, we explored IProgress interface briefly and understood how it helps us in reporting progress. It seemed a perfect fit choice, with little chances of human errors. However, there are things one needs to be aware of to ensure you do not end up with unrecognizable errors.

Let’s rewrite the code in earlier post with slight modification.

async Task InvokeLongRunningMethod()
{
  Console.WriteLine("Initiating method call");
  var progress = new Progress<MessageObject>(ProgressDisplay);
  var result = await LongRunningMethod(progress);
  Console.WriteLine($"Completed executing method with result = {result}");
}


void ProgressDisplay(MessageObject update)
{
  Console.WriteLine(update.Message);
}

async Task<bool> LongRunningMethod(IProgress<MessageObject> progress)
{
  var test = new MessageObject();
  for(int i=0;i<100;i++)
  {
    if(i%10==0)
   {
     test.msg = $"In Progress : {i}%";
     progress.Report(test);
   }
  }

  return true;
}

public class MessageObject
{
       public string Message{get;set;}
}

We have created a MessageObject Class, which has been passed on as the parameter for the IProgress interface parameter type T. So how does this change the equation. Let’s run the code and watch the output first.

Initiating method call
Completed executing method with result = True
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%
In Progress : 90%

As observed in the output, the progress shows 90% each time. Isn’t that strange. Well, not quite if you think a bit more. The IProgress.Report method calls are asynchronous and needn’t be quite completed at that moment. Since the object instance changes before the Report method is called again, the previous call too ends with same value. For this very reason, you need to ensure that the T parameter type is always immutable or you create a separate copy each time Report is invoked.

Reporting Progress in Async Method

Quite often you would need to provide some kind of progress from your long running asynchronous method, especially if it process multiple activities. Though there might be multiple ways to achieve it, Microsoft provides us with a simple and efficient interface IProgress, which enables us to achieve the result. Let’s hit the code straightaway.

void Main()
{
   InvokeLongRunningMethod();
}

async Task InvokeLongRunningMethod()
{
   Console.WriteLine("Initiating method call");
   var progress = new Progress<string>(ProgressDisplay);
   var result = await LongRunningMethod(progress);
   Console.WriteLine($"Completed executing method with result = {result}");
}


void ProgressDisplay(string update)
{
   Console.WriteLine(update);
}

async Task<bool> LongRunningMethod(IProgress<string> progress)
{

  for(int i=0;i<100;i++)
  {
     await Task.Delay(100);
     if(i%10==0)  progress.Report($"In Progress : {i}%");

  }
  return true;
}

The output would be

Initiating method call
In Progress : 0%
In Progress : 10%
In Progress : 20%
In Progress : 30%
In Progress : 40%
In Progress : 50%
In Progress : 60%
In Progress : 70%
In Progress : 80%
In Progress : 90%
Completed executing method with result = True

APM/EAP to TAP

Asynchronous Programming has evolved over the years, right from the APM to the extremely powerful TAP Model. One (or probably the only one) of the problem which developers find themselves in is to use legacy code which were written somewhere in between the evolution. Quite often, developers might be inclined to wrap the existing code to the latest TAP model. How do one do that ? Let’s examine each model and its conversion one by one.

Asynchronous Programming ModelĀ 

APM was the very first model introduced by the .Net designers, however it is no longer a recommended pattern. We will begin by writing an example code in APM, before figuring out how to convert it to TAP.

 

string filePath = @"yourfilepath";
byte[]  buffer = new byte[20000];
using(FileStream  fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous))
{
       Console.WriteLine(ReadFile(fs,buffer,0,buffer.Length));
}

private int ReadFile(Stream fs, byte[] buffer,int offset,int count)
{
  IAsyncResult result =  fs.BeginRead(buffer, 0, count, null, null);
  int numBytes = fs.EndRead(result);
  return numBytes;
}

The Task.Factory provides us an easy way to convert APM patterns to TAP. Let’s rewrite the above code in TAP now.

 

private Task<int> ReadAsync(Stream stream, byte[] buffer, int offset, int count) => Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, offset, count, null);

Event based Asynchronous Pattern

The second approach that Microsoft adopted was known as EAP. Let’s first examine a sample code for EAP.

Process p = new Process();
p.Exited += (e,args)=>
{
  Console.WriteLine("Executing Exited Event");
  Console.WriteLine($"Ended : {p.ExitTime}");
};
p.StartInfo.FileName = @"cmd";
p.EnableRaisingEvents = true;
p.Start();
Console.WriteLine($"Started : {DateTime.Now}");
Console.WriteLine("Invoke Process completed");

The conversion from EAP to TAP is facilitated using the TaskCompletionSource class.

Console.WriteLine($"Ended : {InvokeProcess().Result}");

Task<string> InvokeProcess()
{
       var tcs = new TaskCompletionSource<string>();
       Process p = new Process();
       p.Exited += (e,args)=>{
       Console.WriteLine("Executing Exited Event");
       tcs.SetResult("Result Data");
       };
       p.StartInfo.FileName = @"cmd";
       p.EnableRaisingEvents = true;
       p.Start();
       Console.WriteLine("Invoke Process completed");
       return tcs.Task;
}