TPL And Asynchronous Programming

This series follows my study notes on TPL and asynchronous programming. A special thanks to Jon Skeets and Jeffrey Richter for their wonderful books,which has been the inspirations behind this series.

Asynchronous Code – Behind the Scenes – 001

If you were to ask me what was the biggest milestone in .Net development, then my choice would definetly be .Net 5.0 – especially the introduction of the async/await. The more you learn about the underlying working, you cannot but stop and admire the efforts done by the lang uage developers to make our life easier.

In this mini series on asynchronous programming in .Net, we will delve deeper into the fascinating world of async await and learn more about what happens behind the scenes. I thought i would structure it as a mini-series rather than a single monolythic post as this is a vast topic (at least for an average developer like me).

Setting the Stage

Let us begin by writing a simple async method, which we would then decompile using ILSpy to know what happens beneath. We will keep the base code as simple as possible.

class Program
{
    static void Main(string[] args)
    {
        Foo(10);
    }

    static async Task Foo(int delay)
    {
        await Task.Delay(delay);
        Console.WriteLine(delay);
        await Bar();
    }

    static async Task Bar()
    {
        await Task.Delay(100);
    }
}

For the demonstration purpose, we will use ILSpy for decompiling our code, but please feel free to choose any decompiler you are comfortable with. In case you are using ILSpy, please ensure you have the following settings unchecked.

View->Options->Decompiler->C# 5.0-> Decompile async methods

This would ensure we could view the decompiled async code and the associated state machine. Okay, now let us see what ILSpy has to offer us after decompiling our code. We will read the code in parts, so that it is easier for us to understand the whole structure.

  1. Stub Method
  2. State Machine structure
  3. MoveNext Method method

Stub Method

As you would be already aware, the async methods are implemented with the help of State Machines internally.

Stubs are methods which has the same signature as your original async method and is responsible for creating the state machine. Let us check the decompiled stub method.

[AsyncStateMachine(typeof(<Foo>d__1))]
[DebuggerStepThrough]
private static Task Foo(int delay)
{
	<Foo>d__1 stateMachine = new <Foo>d__1();
	stateMachine.delay = delay;
	stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
	stateMachine.<>1__state = -1;
	stateMachine.<>t__builder.Start(ref stateMachine);
	return stateMachine.<>t__builder.Task;
}

[AsyncStateMachine(typeof(<Bar>d__2))]
[DebuggerStepThrough]
private static Task Bar()
{
	<Bar>d__2 stateMachine = new <Bar>d__2();
	stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
	stateMachine.<>1__state = -1;
	stateMachine.<>t__builder.Start(ref stateMachine);
	return stateMachine.<>t__builder.Task;
}

Let us skip the attributes for a moment (we will come back to very shortly), and first concentrate on signature of both Stub methods. As mentioned earlier, both shares the same signature with their original async methods.

The Stub Method is responsible for creating/initializing the State Machine and Starting it. The state machines are initialized with following

  • Parameters as Fields

Any parameter in the original async method are added as Fields in the State Machine. For example, if you inspect the following code from Foo Stub Method.

stateMachine.delay = delay;

The Foo method, if you remember, accepted a single parameter of Type int and was called delay. This parameter would be now added a field in the State Machine.

  • Type of Builder

The type of Builder varies depending on the return type of the Method in question.

Return TypeBuilder
TaskAsyncTaskMethodBuilder
Task<TResult>AsyncTaskMethodBuilder<T>
voidAsyncVoidMethodBuilder
Custom Task TypeBuilder specified by AsyncTaskMethodBuilderAttribute

In the above scenario, both methods returns Task. For the same reason, the both uses an AsyncTaskMethodBuilder. Please note that the Custom Task Type was introduced only with C# 7. Prior to C# 7, only the first 3 builders were applicable.

The attribute AsyncStateMachine points to the method’s particular state machine and aids in tooling.

  • State of the State Machine

A async method could be in either the following states – Not Started – Executing – Paused – Completed (Successfully or Faulted)

Out of these, the most important state for the State Machine is the Paused State. While in the Executing State, the async method is pretty much like synchronous code as it passes through each instruction. The CPU would keep track of the currently executing step via Instruction Pointer.

However, the state machine comes into picture immediately as the method pauses when it reaches an await (incomplete) expression. Please note that this is applicable only for async expression that has not been completed. In case the awaited expression is completed, the code works similiar to synchronous code and the state machine would not be brought into picture.

Each time the state has to be paused, the state is recorded so that once the operation awaited is completed, the method could be continued.

As one can expected, the Stub method would like to set the initial state of the state machine to Not Started.

The State Property of the state machine handles the current state with following value codes

ValueDescription
-1Not Started
-2Completed (Successfully/Faulted)
Any other valuePaused at an await Expression

As one can observe, these are the values which the Stub method initializes the State Machine with.

<Foo>d__1 stateMachine = new <Foo>d__1();
stateMachine.delay = delay;
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);

Additional, it also starts the State Machine using the Builder.Start method, passing a reference to the State Machine created. The important point to note here is that the stateMachine is passed as reference to the method. This is because both StateMachine and AsyncTaskBuilder are mutable value types, and passing the instance by reference ensures no local copy are created. One can notice a lot of optimization done by the compiler.

The final step of the Stub method is of course, to return the Task object. The Task object is created by the Builder, who also ensures the Task’s state is changed accordingly as the method execution progresses.

return stateMachine.<>t__builder.Task;

When the Builder.Start method is invoked, it begins executing the MoveNext method (we will discuss it later) untill the method reaches an incomplete await expression. At this point, the MoveNext would return the Task, following up which, the Start method also returns. The task is then returned to the Caller method.

That’s it for the Stub Method, in the next section, we will look into the State Machine structure.

Revisiting Exception Handling in async methods

It is interesting to observe how the Exceptions behave in async code. While the .Net framework attempts to ensure the exprerience of handling failures in async methods are as similar to the synchronous methods, there are subtle differences which is worth understanding.

Let us examine the following code.

async Task<string> Foo()
{
	var client = new HttpClient();
	try
	{
		return await client.GetStringAsync("http://www.InvalidUrl.com");
	}
	catch(HttpRequestException ex)
	{
		Console.WriteLine($"Exception of Type {ex.GetType()} has been raised");
	}
	catch(AggregateException ex)
	{
		Console.WriteLine($"Exception of Type {ex.GetType()} has been raised");
	}
	catch(Exception ex)
	{
		Console.WriteLine($"Exception of Type {ex.GetType()} has been raised");
	}
	return default;
}

What could be the output of the above code ? Specifically, what type of an exception would be caught assuming the Url is invalid ?

Before we get to the answer, let us examine how the returned Task<string> object indicate the failure.

Property/MethodIndication
StatusFaulted
IsFaultedtrue
ExceptionAggregateException
Wait()Throws AggregateException
ResultThrows AggregateException
await TaskThrows First Exception with AggregateException

The last 3 rows holds importance to our question. Let us reexamnine the method call.

return client.GetStringAsync("http://www.InvalidUrl.com").

In the above case, we would recieve an AggregateException with an HttpRequestException within it.

When Exception causes Status |= Faulted

As mentioned above, the status of Task would be set to Faulted in most cases, except for one particular kind of Exception,the OperationCancelledException.

Let us write some code before we discuss this further.

async void Main()
{
	var task = Foo();
	try
	{
		await task;
	}
	catch(Exception Ex)
	{
		Console.WriteLine($"Task Status:{task.Status}, Exception:{Ex.Message}");
	}
}

async Task Foo()
{
	throw new OperationCanceledException();
}

Examining the output

Task Status:Canceled, Exception:The operation was canceled.

The TPL uses OperationCanceledException when a Token from CancelationTokenSource is canceled by the calling method. If a method like the code above, decides to throw this special exception, then instead of the Status being set to Faulted, the Status is set to Canceled.

Lazy Exceptions

There is another aspect of Exception Handling in async methods that are worth examining. An async task would not directly throw an exception, instead it would return a faulted Task. Significance of the method could be better understood with help of a bit of code.

async void Main()
{
	try
	{
		var fooTask = Foo(-3);
		Console.WriteLine("Task is not awaited yet");
		await fooTask;
		Console.WriteLine("Task Completed");
	}
	catch(ArgumentException ex)
	{
		Console.WriteLine($"{nameof(ArgumentException)} has been raised");
	}
}


public async Task<string> Foo(int value)
{
	Console.WriteLine($"Method {nameof(Foo)} Invoked");
	if(value<0)
	{
		throw new ArgumentException();
	}
	
	Console.WriteLine($"Method {nameof(Foo)} mocking real task via Delay");
	await Task.Delay(1000);
	return default;
}

The Foo() method has a precondition check which validates if the passed arguement is a positive number. Otherwise, it raises an ArgumentException. With the example code, the method invoker is passing a negative value to the method, and should hit the precondition block.

Let us examine the output and discuss further.

Method Foo Invoked
Task is not awaited yet
ArgumentException has been raised

As you can observe, the message “Task is not awaited yet” is displayed before the exception thrown. This is because exceptions would not be raised untill the task is awaited (or completed). This lazy nature of evaluation of exceptions could be useful at most times, but in times such as above, where preconditions needs to be evaluated and the developer would prefer an early evaluation, this would need a slight workaround.

The idea, similar to how we made iterator methods to evalute exceptions early (and as John Skeets mentions in his invaluable book series C# in Depth), lies in introducing a synchronous method which does the arguement validation, and which in-turn calls the original method. If the original method is moved as an internal method of proposed method, the original method can now safely assume that the arguements are validated.

public Task<string> Foo(int value)
{
	Console.WriteLine($"Method {nameof(Foo)} Invoked");
	if(value<0)
	{
		throw new ArgumentException();
	}
	
	async Task<string> FooAsync()
	{
		Console.WriteLine($"Method {nameof(Foo)} mocking real task via Delay");
		await Task.Delay(1000);
		return default;
	}
	return FooAsync();
}

This ensures the validation and subsequent exception is evaluated early. Let’s hit F5 and run our code now.

Method Foo Invoked
ArgumentException has been raised

As observed, the exception has been evaluated early and we get the much desired result.

That’s all for now, see you soon again

Dragging Shapes in Wpf

One of the projects I have in mind as my side-projects needs ability to drag-move shapes on a Canvas. I thought it would idea to explore the possibilities of achieving it via test projects first before starting the real one. So the goal of this article would be
* Create a Rectangle in a Canvas
* Support ability to Drag-move it within the Canvas boundary

Let’s begin by defining the basic UI including our Canvas and Rectangle. We would be using Caliburn Micro for supporting MVVM in the example.

<Canvas Grid.Row="2" Background="AliceBlue" >
<Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" />
</Canvas>

Our ViewModel, at the moment looks like,

public class ShellViewModel:PropertyChangedBase
{
public double Left { get; set; } = 10;
public double Top { get; set; } = 10;
}

The Left and Top are double properties which will have the position of the Rectange with respect to the Canvas.

The next step is to capture the Mouse Position each time the User holds the Left Mouse Button down and drags the object. For this there are few things needs to be done. We need to detect when the Left Mouse Button is pressed down and released. Also, we need to trace the Mouse Position when the User holds the Left Mouse Down within the rectangle.

To detect the mouse position, we will write a Behavior.

public class MouseMoveBehavior:Behavior<Canvas>
{
public double MouseTop
{
get { return (double)GetValue(MouseTopProperty); }
set { SetValue(MouseTopProperty, value); }
}

public static readonly DependencyProperty MouseTopProperty =
DependencyProperty.Register("MouseTop", typeof(double), typeof(MouseMoveBehavior), new PropertyMetadata(0d));

public double MouseLeft
{
get { return (double)GetValue(MouseLeftProperty); }
set { SetValue(MouseLeftProperty, value); }
}

public static readonly DependencyProperty MouseLeftProperty =
DependencyProperty.Register("MouseLeft", typeof(double), typeof(MouseMoveBehavior), new PropertyMetadata(0d));

protected override void OnAttached()
{
AssociatedObject.MouseMove += AssociatedObject_MouseMove;
}

private void AssociatedObject_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
var currentPosition = e.GetPosition(AssociatedObject);
MouseLeft = currentPosition.X;
MouseTop = currentPosition.Y;
}

protected override void OnDetaching()
{
AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
}
}

The Behavior MouseMoveBehavior defines two Dependency Properties named MouseTop and MouseLeft indicating the X-Y cordinates of the mouse position. The behavior intends to capture the Mouse position using the MouseEventArgs.GetPosition and update the dependency properties with the Position.X and Position.Y values.

So, we have our behavior in place. Now is the time to update our Xaml to use our Behavior.

<Canvas Grid.Row="2" Background="AliceBlue" cal:Message.Attach="[Event MouseMove]=[Action MouseMove];
[Event PreviewMouseLeftButtonUp]=[Action MouseUp];[Event MouseLeave]=[Action MouseUp]" >
<i:Interaction.Behaviors>
<behaviors:MouseMoveBehavior MouseLeft="{Binding Left,Mode=TwoWay}" MouseTop="{Binding Top,Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" cal:Message.Attach="[Event PreviewMouseLeftButtonDown]=[Action MouseDown]" />
</Canvas>

As you can observe, we are updating the Left and Top properties by capturing them in Behavior and assigning the the values of current mouse position. This would ensure the the shape would move along the mouse pointer.

But you want to update the Left and Top position only when the Mouse Left Button is pressed and dragged, not when it is freely moved. To help us achieve this, we will introduce three additional properties in our ViewModel. Let us rewrite our ViewModel. This would also explain why we are capturing the Mouse Events in the XAML, which was intensionally not explained till this point.

public class ShellViewModel:PropertyChangedBase
{
public double Left { get; set; } = 10;
public double Top { get; set; } = 10;

public double CurrentMouseX { get; set; }
public double CurrentMouseY { get; set; }

public bool IsShapeCaptured { get; set; }

public void MouseDown()
{
IsShapeCaptured = true;
NotifyOfPropertyChange(nameof(IsShapeCaptured));
}

public void MouseMove()
{
if (!IsShapeCaptured) return;
Left = CurrentMouseX;
Top = CurrentMouseY;
NotifyOfPropertyChange(nameof(Left));
NotifyOfPropertyChange(nameof(Top));
}

public void MouseUp()
{
IsShapeCaptured = false;
NotifyOfPropertyChange(nameof(IsShapeCaptured));
}
}

The IsShapeCaptured is enabled only when the Mouse Left Button is pressed. As soon as the Mouse Button is released, you are resetting the IsShapeCaptured flag. The MouseMove method ensures that the Left & Top properties (which are bound to the position of Shape) are updated only when the IsShapeCaptured is set.

I guesss that bit of code is quite self explanatory. This is one way to achieve the drag/drop functionality, but obviously there could be others. Will provide another solution in coming days.