0% found this document useful (0 votes)
161 views24 pages

Asynchrony in C#5.0

The document discusses the introduction of asynchronous programming features in C# 5.0. It explains that C# 5.0 will allow developers to write asynchronous code using async and await keywords, which will simplify asynchronous code by handling the underlying state machines and continuations. It provides an example of converting synchronous code to asynchronous code using these new keywords. It also announces the upcoming availability of a C# 5.0 prototype compiler.

Uploaded by

sudu22
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
161 views24 pages

Asynchrony in C#5.0

The document discusses the introduction of asynchronous programming features in C# 5.0. It explains that C# 5.0 will allow developers to write asynchronous code using async and await keywords, which will simplify asynchronous code by handling the underlying state machines and continuations. It provides an example of converting synchronous code to asynchronous code using these new keywords. It also announces the upcoming availability of a C# 5.0 prototype compiler.

Uploaded by

sudu22
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

Asynchrony in C# 5, Part One

The designers of C# 2.0 realized that writing iterator logic was painful. So they added iterator blocks. That way the
compiler could figure out how to build a state machine that could store the continuation - the “what comes next” - in
state somewhere, hidden behind the scenes, so that you don’t have to write that code.

They also realized that writing little methods that make use of local variables was painful. So they added anonymous
methods.
methods That way the compiler could figure out how to hoist the locals to a closure class, so that you don't have to
write that code.

The designers of C# 3.0 realized that writing code that sorts, filters, joins, groups and summarizes complex data sets
was painful. So they added query comprehensions
comprehensions and all the rest of the LINQ features. That way the compiler could
figure out how to do the right object model calls to build the query, the expression trees, and so on.

The designers of C# 4.0 realized that interoperating with modern and legacy dynamic object models was painful. So
they added the dynamic type. That way the compiler could figure out how to generate the code at compile time that
does the analysis in the Dynamic Language Runtime at runtime.

The designers of C# 5.0 realized that writing asynchronous code is painful, in so many ways. Asynchronous code is
hard to reason about, and as we've seen, the transformation into a continuation is complex and leads to code replete
with mechanisms that obscure the meaning of the code.

This shall not stand.


stand.

I am pleased to announce that there will be a C# 5.0 (*), and that in C# 5.0 you’ll be able to take this synchronous
code:

void ArchiveDocuments(List<Url> urls)


{
for(int i = 0; i < urls.Count; ++i)
Archive(Fetch(urls[i]));
}

and, given reasonable implementations of the FetchAsync and ArchiveAsync methods, transform it into this code to
achieve the goal of sharing wait times as described yesterday:

async void ArchiveDocuments(List<Url> urls)


{
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
}

Where is the state machine code, the lambdas, the continuations, the checks to see if the task is already complete?
They’re all still there. Let the compiler generate all that stuff for you, just like you let the compiler generate code for
iterator blocks, closures, expression trees, query comprehensions and dynamic calls. The C# 5.0 code above is
essentially a syntactic sugar for the code I presented yesterday. That's a pretty sweet sugar!

I want to be quite clear on this point: the action of the code above will be logically the same as the action of yesterday's
code.
code Whenever a task is "awaited", the remainder of the current method is signed up as a continuation of the task, and
then control immediately returns to the caller. When the task completes, the continuation is invoked and the method
starts up where it was before.

If I’ve timed the posting of this article correctly then Anders is announcing this new language feature at the PDC right
about… now. You can watch it here.

We are as of right now making a Community


Community Technology Preview edition of the prototype C# 5.0 compiler available.
The prototype compiler will be of prototype quality; expect features to be rough around the edges still. The idea is to
give you a chance to try it and see what you think so that we can get early feedback.

I'll be posting more about this feature tomorrow and for quite some time after that; if you can't wait, or want to get
your hands on the prototype, you can get lots more information and fresh tasty compiler bits from
msdn.com/vstudio/async.

And of course, in keeping with our co-evolution strategy, there will be a Visual Basic 11 and it will also feature task-
based asynchrony. Check out the VB team blog for details, or read all about this feature at my colleague Lucian's blog.
(Lucian did much of the design and prototyping of this feature for both C# and VB; he, not I, is the expert on this so if
you have deep questions, you might want to ask him, not me.)

Tomorrow:
Tomorrow await? async? Task? what about AsyncThingy<T>? Tell me more!

(*) We are absolutely positively not announcing any dates or ship vehicles
vehicles at this time, so don't even ask. Even if I knew,
which I don't, and even if my knowledge had the faintest chance of being accurate, which it doesn't, I still wouldn't tell
you.
Asynchronous Programming in C# 5.0 part two: Whence await?

I want to start by being absolutely positively clear about two things, because our usability research has shown this to
be confusing. Remember our little program from last time?

async void ArchiveDocuments(List<Url> urls)


{
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
}

The two things are:

1) The “async” modifier on the method does not mean “this method is automatically scheduled to run on a worker
thread asynchronously”. It means the opposite of that; it means “this method contains control flow that involves
awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to
ensure that the asynchronous operations can resume this method at the right spot.” The whole point of async methods
it that you stay on the current thread as much as possible. They’re like coroutines: async methods bring single-
threaded cooperative multitasking to C#. (At a later date I’ll discuss the reasons behind requiring the async modifier
rather than inferring it.)

2) The “await” operator used twice in that method does not mean “this method now blocks the current thread until the
asynchronous operation returns”. That would be making the asynchronous operation back into a synchronous
operation, which is precisely what we are attempting to avoid. Rather, it means the opposite of that; it means “if the
task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and
then return to your caller immediately; the task will invoke the continuation when it completes.”

It is unfortunate that people’s intuition upon first exposure regarding what the “async” and “await” contextual keywords
mean is frequently the opposite of their actual meanings. Many attempts to come up with better keywords failed to find
anything better. If you have ideas for a keyword or combination of keywords that is short, snappy, and gets across the
correct ideas, I am happy to hear them. Some ideas that we already had and rejected for various reasons were:

wait for FetchAsync(…)


yield with FetchAsync(…)
yield FetchAsync(…)
while away the time FetchAsync(…)
hearken unto FetchAsync(…)
for sooth Romeo wherefore art thou FetchAsync(…)

Moving on. We’ve got a lot of ground to cover. The next thing I want to talk about is “what exactly are those ‘thingies’
that I handwaved about last time?”

Last time I implied that the C# 5.0 expression

document = await FetchAsync(urls[i])

gets realized as:

state = State.AfterFetch;
fetchThingy = FetchAsync(urls[i]);
if (fetchThingy.SetContinuation(archiveDocuments))
return;
AfterFetch: ;
document = fetchThingy.GetValue();

what’s the thingy?

In our model for asynchrony an asynchronous method typically returns a Task<T>; let’s assume for now that
FetchAsync returns a Task<Document>. (Again, I’ll discuss the reasons behind this "Task-based Asynchrony Pattern"
at a later date.) The actual code will be realized as:

fetchAwaiter = FetchAsync(urls[i]).GetAwaiter();
state = State.AfterFetch;
if (fetchAwaiter.BeginAwait(archiveDocuments))
return;
AfterFetch: ;
document = fetchAwaiter.EndAwait();

The call to FetchAsync creates and returns a Task<Document> - that is, an object which represents a “hot” running
task. Calling this method immediately returns a Task<Document> which is then somehow asynchronously fetches the
desired document. Perhaps it runs on another thread, or perhaps it posts itself to some Windows message queue on
this thread that some message loop is polling for information about work that needs to be done in idle time, or
whatever. That’s its business. What we know is that we need something to happen when it completes. (Again, I’ll
discuss single-threaded asynchrony at a later date.)

To make something happen when it completes, we ask the task for an Awaiter, which exposes two methods.
BeginAwait signs up a continuation for this task; when the task completes, a miracle happens: somehow the
continuation gets called. (Again, how exactly this is orchestrated is a subject for another day.) If BeginAwait returns
true then the continuation will be called; if not, then that’s because the task has already completed and there is no
need to use the continuation mechanism.

EndAwait extracts the result that was the result of the completed task.

We will provide implementations of BeginAwait and EndAwait on Task (for tasks that are logically void returning) and
Task<T> (for tasks that return a value). But what about asynchronous methods that do not return a Task or Task<T>
object? Here we’re going to use the same strategy we used for LINQ. In LINQ if you say

from c in customers where c.City == "London" blah blah blah

then that gets translated into

customers.Where(c=>c.City=="London") …

and overload resolution tries to find the best possible Where method by checking to see if customers implements such
a method, or, if not, by going to extension methods. The GetAwaiter / BeginAwait / EndAwait pattern will be the
same; we’ll just do overload resolution on the transformed expression and see what it comes up with. If we need to go
to extension methods, we will.

Finally: why "Task"?

The insight here is that asynchrony does not require parallelism, but parallelism does require asynchrony, and many of
the tools useful for parallelism can be used just as easily for non-parallel asynchrony. There is no inherent parallelism
in Task; that the Task Parallel Library uses a task-based pattern to represent units of pending work that can be
parallelized does not require multithreading.
As I've pointed out a few times, from the point of view of the code that is waiting for a result it really doesn't matter
whether that result is being computed in idle time on this thread, in a worker thread in this process, in another process
on this machine, on a storage device, or on a machine halfway around the world. What matters is that it's going to take
time to compute the result, and this CPU could be doing something else while it is waiting, if only we let it.

The Task class from the TPL already has a lot of investment in it; it's got a cancellation mechanism and other useful
features. Rather than invent some new thing, like some new "IFuture" type, we can just extend the existing task-based
code to meet our asynchrony needs.

Next time: How to further compose asynchronous tasks.


Asynchrony in C# 5, Part Three: Composition

I was walking to my bus the other morning at about 6:45 AM. Just as I was about to turn onto 45th street, a young
man, shirtless, covered in blood ran down 45th at considerable speed right in front of me. Behind him was another
fellow, wielding a baseball bat. My initial thought was "holy goodness, I have to call the police right now!"

Then I saw that the guy with the baseball bat was himself being chased by Count Dracula, a small horde of zombies, a
band of pirates, one medieval knight, and bringing up the rear, a giant bumblebee. Apparently some Wallingford
jogging club got into the Hallowe'en spirit this past weekend.

In unrelated news: we've had lots of great comments and feedback on the async feature so far; please keep it coming.
coming It
is being read; it will take weeks to digest it and volume of correspondence may preclude personal replies, for which I
apologize, but that's how it goes.

Today I want to talk a bit about composition of asynchronous code, why it is difficult in CPS, and how it is much easier
with "await" in C# 5.

I need a name for this thing. We named LINQ because it was Language Integrated Query.
Query For now, let's provisionally call
this new feature TAP,
TAP the Task Asynchrony Pattern.
Pattern I'm sure we'll come up with a better name later; remember, this is
still just a prototype. (*)

The example I've been using so far, of fetching and archiving documents was obviously deliberately contrived to be a
simple orchestration of two asynchronous tasks in a void returning method. As we saw, when using regular
Continuation Passing Style it can be tricky to orchestrate even two asynchronous methods. Today I want to talk a bit
about composition of asynchronous methods. Our ArchiveDocuments method was void returning, which simplifies
things greatly. Suppose that ArchiveDocuments was to return a value, say, the total number of bytes archived.
Synchronously, that's straightforward:

long ArchiveDocuments(List<Url> urls)


{
long count = 0;
for(int i = 0; i < urls.Count; ++i)
{
var document = Fetch(urls[i]);
count += document.Length;
Archive(document);
}
return count;
}

Now consider how we'd rewrite that asynchronously. If ArchiveDocuments is going to return immediately when the first
FetchAsync starts up, and is only going to be resumed when the first fetch completes, then when is the "return count;"
going to be executed? The naive asynchronous version of ArchiveDocuments cannot return a count; it has to be written
in CPS too:

void ArchiveDocumentsAsync(List<Url> urls, Action<long> continuation)


{
// somehow do the archiving asynchronously,
// then call the continuation
}

And now the taint has spread. Now the caller of ArchiveDocumentsAsync needs to be written in CPS, so that its
continuation can be passed in. What if it in turn returns a result? This is going to become a mess; soon the entire
program will be written upside down and inside out.
In the TAP model, instead we'd say that the type that represents asynchronous work that produces a result later is a
Task<T>. In C# 5 you can simply say:

async Task<long> ArchiveDocumentsAsync(List<Url> urls)


{
long count = 0;
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
count += document.Length;
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
return count;
}

and the compiler will take care of all the rewrites for you. It is instructive to understand exactly what happens here.
This will get expanded into something like:

Task<long> ArchiveDocuments(List<Url> urls)


{
var taskBuilder = AsyncMethodBuilder<long>.Create();
State state = State.Start;
TaskAwaiter<Document> fetchAwaiter = null;
TaskAwaiter archiveAwaiter = null;
int i;
long count = 0;
Task archive = null;
Document document;
Action archiveDocuments = () =>
{
switch(state)
{
case State.Start: goto Start;
case State.AfterFetch: goto AfterFetch;
case State.AfterArchive: goto AfterArchive;
}
Start:
for(i = 0; i < urls.Count; ++i)
{
fetchAwaiter = FetchAsync(urls[i]).GetAwaiter();
state = State.AfterFetch;
if (fetchAwaiter.BeginAwait(archiveDocuments))
return;
AfterFetch:
document = fetchAwaiter.EndAwait();
count += document.Length;
if (archive != null)
{
archiveAwaiter = archive.GetAwaiter();
state = State.AfterArchive;
if (archiveAwaiter.BeginAwait(archiveDocuments))
return;
AfterArchive:
archiveAwaiter.EndAwait();
}
archive = ArchiveAsync(document);
}
taskBuilder.SetResult(count);
return;
};
archiveDocuments();
return taskBuilder.Task;
}
(Note that we still have problems with the labels being out of scope. Remember, the compiler doesn't need to follow
the rules of C# source code when it is generating code on your behalf; pretend the labels are in scope at the point of
the goto. And note that we still have no exception handling in here. As I discussed last week in my post on building
exception handling in CPS, exceptions get a little bit weird because there are *two* continuations: the normal
continuation and the error continuation. How do we deal with that situation? I'll discuss how exception handling works
in TAP at a later date.)

Let me make sure the control flow is clear here. Let's first consider the trivial case: the list is empty. What happens? We
create a task builder. We create a void-returning delegate. We invoke the delegate synchronously. It initializes the outer
variable "count" to zero, branches to the "Start" label, skips the loop, tells the helper "you have a result", and returns.
The delegate is now done. The taskbuilder is asked for a task; it knows that the task's work is completed, so it returns
a completed task that simply represents the number zero.

If the caller attempts to await that task then its awaiter will return false when asked to begin async operations, because
the task has completed. If the caller does not await that task then... well, then they do whatever they do with a Task.
Eventually they can ask it for its result, or ignore it if they don't care.

Now let's consider the non-trivial case; there are multiple documents to archive. Again, we create a task builder and a
delegate which is invoked synchronously. First time through the loop, we begin an asynchronous fetch, sign up the
delegate as its continuation, and return from the delegate. At that point the task builder builds a task that represents
"I'm asynchronously working on the body of ArchiveDocumentsAsync" and returns that task. When the fetch task
completes asynchronously and invokes its continuation, the delegate starts up again "from the point where it left off"
thanks to the magic of the state machine. Everything proceeds exactly as before, in the case of the void returning
version; the only difference is that the returned Task<long> for ArchiveDocumentsAsync signals that it is complete (by
invoking its continuation) when the delegate tells the task builder to set the result.

Make sense?

Before I continue with some additional thoughts on composition of tasks, a quick note on the extensibility
extensibility of TAP. We
designed LINQ to be very extensible; any type that implements Select, Where, and so on, or has extension methods
implemented for them, can be used in query comprehensions. Similarly with TAP: any type that has a GetAwaiter that
returns a type that has BeginAwait, EndAwait, and so on, can be used in "await" expressions. However, methods marked
as being async can only return void, Task, or Task<T> for some T. We are all about enabling extensibility on
consumption of existing asynchronous things, but have no desire to get in the business of enabling production of
asynchronous methods with exotic types. (The alert reader will have noted that I have not discussed extensibility points
for the task builder.
builder At a later date I'll discuss where the task builder comes from.)

Continuing on: (ha ha ha)

In LINQ there are some situations in which the use of "language" features like "where" clauses is more natural and some
where using "fluent" syntax ("Where(c=>...)") is more natural. Similarly with TAP: our goal is to enable use of regular
C# syntax to compose and orchestrate asynchronous tasks, but sometimes you want to have a more "combinator"
based approach. To that end, we'll be making available methods with names like like "WhenAll" or "WhenAny" that
compose tasks like this:

List<List<Url>> groupsOfUrls = whatever;


Task<long[]> allResults = Task.WhenAll(from urls in groupsOfUrls select
ArchiveDocumentsAsync(urls));
long[] results = await allResults;
What does this do? Well, ArchiveDocumentsAsync returns a Task<long>, so the query returns an
IEnumerable<Task<long>>. WhenAll takes a sequence of tasks and produces a new task which asynchronously awaits
each of them, fills the result into an array, and then invokes its continuation with the results when available.

Similarly we'll have a WhenAny that takes a sequence of tasks and produces a new task that invokes its continuation
with the first result when any of those tasks complete. (An interesting question is what happens if the first one
completes successfully and the rest all throw an exception, but we'll talk about that later.)

There will be other task combinators and related helper methods; see the CTP samples for some examples. Note that in
the CTP release we were unable to modify the existing Task class; instead we've provisionally added the new
combinators to TaskEx.
TaskEx In the final release they will almost certainly be moved onto Task.

time No, seriously, asynchrony does not necessarily involve multithreading.


Next time:

(*) I emphasize that this is provisional and for my own rhetorical purposes, and not an official name of anything.
anything Please
don't publish a book called "Essential TAP" or "Learn TAP in 21 Time Units" or whatever. I have a copy of "Instant
DHTML Scriptlets" on my bookshelf; Dino Esposito writes so fast that he published an entire book between the time we
mentioned the code name of the product to him and we announced the real name. ("Windows Script Components" was
the final name.)
Asynchrony in C# 5.0 part Four: It's not magic

Today I want to talk about asynchrony that does not involve any multithreading whatsoever.

People keep on asking me "but how is it possible to have asynchrony without multithreading?" A strange question to
ask because you probably already know the answer. Let me turn the question around: how is it possible to have
multitasking without multiple CPUs? You can't do two things "at the same time" if there's only one thing doing the
work! But you already know the answer to that: multitasking on a single core simply means that the operating system
stops one task, saves its continuation somewhere, switches to another task, runs it for a while, saves its continuation,
and eventually switches back to continue the first task. Concurrency is an illusion in a single-core system; it is not the
case that two things are really happening at the same time. How is it possible for one waiter to serve two tables "at the
same time"? It isn't: the tables take turns being served. A skillful waiter makes each guest feel like their needs are met
immediately by scheduling the tasks so that no one has to wait.

Asynchrony without multithreading is the same idea. You do a task for a while, and when it yields control, you do
another task for a while on that thread. You hope that no one ever has to wait unacceptably long to be served.

Remember a while back I briefly sketched how early versions of Windows implemented multiple processes? Back in the
day there was only one thread of control; each process ran for a while and then yielded control back to the operating
system. The operating system would then loop around the various processes, giving each one a chance to run. If one of
them decided to hog the processor, then the others became non-responsive. It was an entirely cooperative venture.

So let's talk about multi-threading for a bit. Remember a while back, in 2003, I talked a bit about the apartment
threading model? The idea here is that writing thread-safe code is expensive and difficult; if you don't have to take on
that expense, then don't. If we can guarantee that only "the UI thread" will call a particular control then that control
does not have to be safe for use on multiple threads. Most UI components are apartment threaded, and therefore the UI
thread acts like Windows 3: everyone has to cooperate, otherwise the UI stops updating.

A surprising number of people have magical beliefs about how exactly applications respond to user inputs in Windows.
I assure you that it is not magic. The way that interactive user interfaces are built in Windows is quite straightforward.
When something happens, say, a mouse click on a button, the operating system makes a note of it. At some point, a
process asks the operating system "did anything interesting happen recently?" and the operating system says "why yes,
someone clicked this thing." The process then does whatever action is appropriate for that. What happens is up to the
process; it can choose to ignore the click, handle it in its own special way, or tell the operating system "go ahead and
do whatever the default is for that kind of event." All this is typically driven by some of the simplest code you'll ever
see:

while(GetMessage(&msg, NULL, 0, 0) > 0)


{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

That's it. Somewhere in the heart of every process that has a UI thread is a loop that looks remarkably like this one. One
call gets the next message. That message might be at too low a level for you; for example, it might say that a key with
a particular keyboard code number was pressed. You might want that translated into "the numlock key was pressed".
TranslateMessage does that. There might be some more specific procedure that deals with this message.
DispatchMessage passes the message along to the appropriate procedure.
I want to emphasize that this is not magic. It's a while loop. It runs like any other while loop in C that you've ever seen.
The loop repeatedly calls three methods, each of which reads or writes a buffer and takes some action before
returning. If one of those methods takes a long time to return (typically DispatchMessage is the long-running one of
course since it is the one actually doing the work associated with the message) then guess what? The UI doesn't fetch,
translate or dispatch notifications from the operating system until such a time as it does return. (Or, unless some other
method on the call chain is pumping the message queue, as Raymond points out in the linked article. We'll return to
this point below.)

Let's take an even simpler version of our document archiving code from last time:

void FrobAll()
{
for(int i = 0; i < 100; ++i)
Frob(i);
}

Suppose you're running this code as the result of a button click, and a "someone is trying to resize the window"
message arrives in the operating system during the first call to Frob. What happens? Nothing, that's what. The message
stays in the queue until everything returns control back to that message loop. The message loop isn't running; how
could it be? It's just a while loop, and the thread that contains that code is busy Frobbing. The window does not resize
until all 100 Frobs are done.

Now suppose you have

async void FrobAll()


{
for(int i = 0; i < 100; ++i)
{
await FrobAsync(i); // somehow get a started task for doing a Frob(i) operation on this
thread
}
}

What happens now?

Someone clicks a button. The message for the click is queued up. The message loop dispatches the message and
ultimately calls FrobAll.

FrobAll creates a new task with an action.

The task code sends a message to its own thread saying "hey, when you have a minute, call me". It then returns control
to FrobAll.

FrobAll creates an awaiter for the task and signs up a continuation for the task.

Control then returns back to the message loop. The message loop sees that there is a message waiting for it: please
call me back. So the message loop dispatches the message, and the task starts up the action. It does the first call to
Frob.

Now, suppose another message, say, a resize event, occurs at this point. What happens? Nothing. The message loop
isn't running. We're busy Frobbing. The message goes in the queue, unprocessed.

The first Frob completes and control returns to the task. It marks itself as completed and sends another message to the
message queue: "when you have a minute, please call my continuation". (*)
The task call is done. Control returns to the message loop. It sees that there is a pending window resize message. That
is then dispatched.

You see how async makes the UI more responsive without having any threads? Now you only have to wait for one Frob
to finish, not for all of them to finish, before the UI responds.

That might still not be good enough, of course. It might be the case that every Frob takes too long. To solve that
problem, you could make each call to Frob itself spawn short asynchronous tasks, so that there would be more
opportunities for the message loop to run. Or, you really could start the task up on a new thread. (The tricky bit then
becomes posting the message to run the continuation of the task to the right message loop on the right thread; that's
an advanced topic that I won't cover today.)

Anyway, the message loop dispatches the resize event and then checks its queue again, and sees that it has been asked
to call the continuation of the first task. It does so; control branches into the middle of FrobAll and we pick up going
around the loop again. The second time through, again we create a new task... and the cycle continues.

The thing I want to emphasize here is that we stayed on one thread the whole time. All we're doing here is breaking up
the work into little pieces and sticking the work onto a queue; each piece of work sticks the next piece of work onto the
queue. We rely on the fact that there's a message loop somewhere taking work off that queue and performing it.

UPDATE A number of people have asked me "so does this mean that the Task Asynchrony Pattern only works on UI
UPDATE:
threads that have message loops?" No. The Task Parallel Library was explicitly designed to solve problems involving
concurrency; task asynchrony extends that work. There are mechanisms that allow asynchrony to work in
multithreaded environments without message loops that drive user interfaces, like ASP.NET. The intention of this article
was to describe how asynchrony works on a UI thread without multithreading, not to say that asynchrony only works on
a UI thread without multithreading. I'll talk at a later date about server scenarios where other kinds of "orchestration"
code works out which tasks run when.

Extra bonus topic:


topic Old hands at VB know that in order to get UI responsiveness you can use this trick:

Sub FrobAll()
For i = 0 to 99
Call Frob(i)
DoEvents
Next
End Sub

Does that do the same thing as the C# 5 async program above? Did VB6 actually support continuation passing style?

No; this is a much simpler trick. DoEvents does not transfer control back to the original message loop with some sort
of "resume here" message like the task awaiting does. Rather, it starts up a second message loop (which, remember, is
just a perfectly normal while loop), clears out the backlog of pending messages, and then returns control back to
FrobAll. Do you see why this is potentially dangerous?

What if we are in FrobAll as a result of a button click? And what if while frobbing, the user pressed the button again?
DoEvents runs another message loop, which clears out the message queue, and now we are running FrobAll within
FrobAll; it has become reentrant. And of course, it can happen again, and now we're running a third instance of
FrobAll...

Of course, the same is true of task based asynchrony! If you start asynchronously frobbing due to a button click, and
there is a second button click while more frobbing work is pending, then you get a second set of tasks created. To
prevent this it is probably a good idea to make FrobAll return a Task, and then do something like:
async void Button_OnClick(whatever)
{
button.Disable();
await FrobAll();
button.Enable();
}

so that the button cannot be clicked again while the asynchronous work is still pending.

Next time:
time Asynchrony is awesome but not a panacea: a real life story about how things can go terribly wrong.

------------

(*) Or, it invokes the continuation right then and there. Whether the continuation is invoked aggressively or is itself
simply posted back as more work for the thread to do is user-configurable, but that is an advanced topic that I might
not get to
Asynchrony in C# 5 Part Five: Too many tasks

Suppose a city has a whole bunch of bank branches, each of which has a whole bunch of tellers and one gofer. There
are a whole bunch of customers in the city, each of whom wants to withdraw a whole bunch of money from the bank at
some varying time throughout the day.

The algorithm goes like this:

A customer finds the nearest bank branch and evaluates its line. If the line is out the door, then the customer goes to
another bank branch. This continues until they either find one with a short enough line, or they give up and go home.

Suppose they find one with a short enough line. The customer queues up. (Perhaps they queue up using the M model
or using the W model; it doesn't particularly matter for the purpose of this analogy. Let's suppose its the W model.)
When the customer reaches a teller, the transaction goes like this: the customer requests a certain number of bills of
various denominations, and the teller counts them out: one, two, three, four, five, there you go. The customer leaves
and the teller services the next customer.

This seems perfectly reasonable. But suppose the teller runs out of the right denomination of cash halfway through the
transaction. The teller tells the gofer to go get more fifties out of the vault, and then has a little nap while the gofer is
running to the vault and back. Now the customer, and every customer behind them,
them has to wait for the teller to wake
up, which doesn't happen until the gofer gets back from the vault.

In case it's not clear, in this analogy a city is a server farm, a bank branch is a machine, a teller is a worker thread, the
gofer is an I/O completion thread, and a customer is a client. The money is the computation that the server performs
on behalf of the client, and waiting for the gofer to go to the vault is a synchronous delay waiting on I/O completion.
Deciding whether the line is too long to choose a branch is load balancing the server farm.

Now, you might say that this could be more efficient. That time when the teller is asleep waiting for the gofer is
troubling. Ideally you want one thread per core going full out all the time. The teller could be servicing the next
customer in line, and then pick up with the first customer when the gofer gets back. This seems like an ideal use of
task-based asynchrony.

You have to be really careful to understand the implications on the whole system when you make a change like this. It
is really easy to accidentally implement the following system:

A customer finds the nearest bank branch. There is no line, so they go right in. There are no tellers. There's just a "take
a number" machine and an arrow pointing to a door. The customer takes a number and walks through the door into an
enormous warehouse containing tens of thousands of customers. There are a bunch of tellers running at top speed
from customer to customer, giving them each one bill at a time.
time If a teller runs out of a needed denomination then they
yell at the gofer, and keep right on going to the next customer who they can serve. Eventually the gofer brings them
the needed denomination. The tellers keep track of who all they are servicing "at the same time", and no one leaves
until they get all their money. If the gofer can't keep up then the warehouse just gets more and more crowded, and the
more crowded it gets, the longer it takes for a customer to get all their money, and the more customers who arrive
while they're waiting, and it just snowballs.

In this scheme the tellers are almost never asleep, so the CPU cores are being heavily utilized, and everyone gets their
first bill reasonably fast. "Time to first byte of results" is potentially excellent. But the load balancing just went out the
window; because there is never a line, there's no way for the load balancer to know that there are too many unserved
customers. And time to last byte served can be potentially bad.
This sounds like a silly story, but we accidentally did something like this to ourselves just the other day. We built a code
analyzer that uses task based asynchrony on the UI thread. The idea was that on every keystroke we would start an
asynchronous task that then itself started other asynchronous tasks. The tasks would be :

1) Check to see if there's been a subsequent keystroke recently; if so, then cancel any unfinished tasks associated with
this keystroke.
2) Recolour the user interface text based on the differential analysis of the change to the syntax tree induced by the
keystroke.
3) Set a timer to see if there has been half a second of time with no keystrokes. If so, the user may have paused typing
and we can kick off a background worker to perform deeper program analysis.

See the problem? The tasks are created so fast between keystrokes that if you're typing quickly soon you end up with a
warehouse full of tens of thousands of tasks, 99.99% of which have not yet run in order to cancel themselves. And half
the ones that do manage to run are creating timers, the vast majority of which are going to be deleted before they ever
tick. The garbage collection pressure from all those timers alone was enough to destroy performance. Asynchrony is
awesome, but you need to make sure that you get the granularity of the asynchrony at the appropriate level. The code
analyzer was rewritten to enqueue tasks that referred to a single, global timer, and to be less aggressive about
enqueueing tasks that were highly likely to need cancellation a tenth of a second later. Performance improved
drastically.

Task-based asynchrony is awesome but it is not a panacea; be careful and measure as you go. The fact that there is
hardly any waiting between the time you make the request and the time the asynchronous request enqueues itself onto
the work queue means that there is hardly any restriction on the number of tasks you can create quickly.

Next time:
time More thoughts on syntactic concerns
Asynchrony in C# 5 Part Six: Whither async?

A number of people have asked me what motivates the design decision to require any method that contains an "await"
expression to be prefixed with the contextual keyword "async".

Like any design decision there are pros and cons here that have to be evaluated in the context of many different
competing and incompossible principles. There's not going to be a slam-dunk solution here that meets every criterion
or delights everyone. We're always looking for an attainable compromise, not for unattainable perfection. This design
decision is a good example of that.

One of our key principles is "avoid breaking changes whenever reasonably possible". Ideally it would be nice if every
program that used to work in C# 1, 2, 3 and 4 worked in C# 5 as well. (*) As I mentioned a few episodes back, (**)
when adding a prefix operator there are many possible points of ambiguity and we want to eliminate all of them. We
considered many heuristics that could make good guesses about whether a given "await" was intended as an identifier
rather than a keyword, and did not like any of them.

The heuristics for "var" and "dynamic" were much easier because "var" is only special in a local variable declaration and
"dynamic" is only special in a context in which a type is legal. "await" as a keyword is legal almost everywhere inside a
method body that an expression or type is legal, which greatly increases the number of points at which a reasonable
heuristic has to be designed, implemented and tested. The heuristics discussed were subtle and complicated.
complicated For
example, var x = y + await; clearly should treat await as an identifer but should var x = await + y do the same,
or is that an await of the unary plus operator applied to y? var x = await t; should treat await as a keyword; should
var x = await(t); do the same, or is that a call to a method called await?

Requiring "async" means that we can eliminate all backwards compatibility problems at once; any method that contains
an await expression must be "new construction" code, not "old work" code, because "old work" code never had an async
modifier.

An alternative approach that still avoids breaking changes is to use a two-word keyword for the await expression.
That's what we did with "yield return". We considered many two-word patterns; my favourite was "wait for". We rejected
options of the form "yield with", "yield wait" and so on because we felt that it would be too easily confused with the
subtly different continuation behaviour of iterator blocks. We have effectively trained people that "yield" logically means
"proffer up a value", rather than "cede flow of control back to the caller", though of course it means both! We rejected
options containing "return" and "continue" because they are too easily confused with those forms of control flow.
Options containing "while" are also problematic; beginner programmers occasionally ask whether a "while" loop is
exited the moment that the condition becomes false, or if it keeps going until the bottom of the loop. You can see how
similar confusions could arise from use of "while" in asynchrony.

Of course "await" is problematic as well. Essentially the problem here is that there are two kinds of waiting. If you're in
a waiting room at the hospital then you might wait by falling asleep until the doctor is available. Or, you might wait by
reading a magazine, balancing a chequebook, calling your mother, doing a crossword puzzle, or whatever. The point of
task-based asynchrony is to embrace the latter model of waiting: you want to keep getting stuff done on this thread
while you're waiting for your task to complete, rather than sleeping, so you wait by remembering what you were doing,
and then go do something else while you're waiting. I am hoping that the user education problem of clarifying which
kind of waiting we're talking about is not insurmountable.

Ultimately, whether it is "await" or not, the designers really wanted it to be a single-word feature. We anticipate that
this feature will potentially be used numerous times in a single method. Many iterator blocks contain only one or two
yield returns, but there could be dozens of awaits in code which orchestrates a complex asynchronous operation.
Having a succinct operator is important.

Of course, you don't want it to be too succinct. F# uses "do!" and "let!" and so on for their asynchronous workflow
operations. That! makes! the! code! look! exciting! but it is also a "secret code" that you have to know about to
understand; it's not very discoverable. If you see "async" and "await" then at least you have some clue about what the
keywords mean.

Another principle is "be consistent with other language features". We're being pulled in two directions here. On the one
hand, you don't have to say "iterator" before a method which contains an iterator block. (If we had, then "yield
return x;" could have been just "yield x;".) This seems inconsistent with iterator blocks. On the other hand... let's
return to this point in a moment.

Another principle we consider is the "principle of least surprise". More specifically, that small changes should not have
surprising nonlocal results. Consider the following:

void Frob<X>(Func<X> f) { ... }


...
Frob(()=> {
if (whatever)
{
await something;
return 123;
}
return 345;
} );

It seems bizarre and confusing that commenting out the "await something;" changes the type inferred for X from
Task<int> to int. We do not want to add return type annotations to lambdas. Therefore, we'll probably go with
requiring "async" on lambdas that contain "await":

Frob(async ()=> {
if (whatever)
{
await something;
return 123;
}
return 345;
} );

Now the type inferred for X is Task<int> even if the await is commented out.

That is strong pressure towards requiring "async" on lambdas. Since we want language features to be consistent, and it
seems inconsistent to require "async" on anonymous functions but not on nominal methods, that is indirect pressure
on requiring it on methods as well.

Another example of a small change causing a big difference:

Task<object> Foo()
{
await blah;
return null;
}

if "async" is not required then this method with the "await" produces a non-null task whose result is set to null. If we
comment out the "await" for testing purposes, say, then it produces a null task -- completely different. If we require
"async" then the method returns the same thing both ways.
Another design principle is that the stuff that comes before the body of a declared entity such as a method is all stuff
that is represented in the metadata of the entity. The name, return type, type parameters, formal parameters,
attributes, accessibility, static/instance/virtual/override/abstract/sealed-ness, and so on, are all part of the metadata
of the method. "async" and "partial" are not, which seems inconsistent. Put another way: "async" is solely about
describing the implementation details of the method; it has no impact on how the method is used. The caller cares not
a bit whether a given method is marked as "async" or not, so why put it right there in the code where the person writing
the caller is likely to read it? This is points against "async".

On the other hand, another important design principle is that interesting code should call attention to itself. Code is
read a lot more than it is written. Async methods have a very different control flow than regular methods; it makes
sense to call that out at the top where the code maintainer reads it immediately. Iterator blocks tend to be short; I don't
think I've ever written an iterator block that does not fit on a page. It's pretty easy to glance at an iterator block and see
the yield. One imagines that async methods could be long and the 'await' could be buried somewhere not immediately
obvious. It's nice that you can see at a glance from the header that this method acts like a coroutine.

Another design principle that is important is "the language should be amenable to rich tools". Suppose we require
"async". What errors might a user make? A user might have an have a method with the async modifier which contains
no awaits, believing that it will run on another thread. Or the user might write a method that does have awaits but
forget to give the "async" modifier. In both cases we can write code analyzers that identify the problem and produce
rich diagnostics that can teach the developer how to use the feature. A diagnostic could, for instance, remind you that
an async method with no awaits does not run on another thread and give suggestions for how to achieve parallelism if
that's really what you want. Or a diagnostic could tell you that an int-returning method containing an await should be
refactored (automatically, perhaps!) into an async method that returns Task<int>. The diagnostic engine could also
search for all the callers of this method and give advice on whether they in turn should be made async. If "async" is not
required then we cannot easily detect or diagnose these sorts of problems.

That's a whole lot of pros and cons; after evaluating all of them, and lots of playing around with the prototype compiler
to see how it felt, the C# designers settled on requiring "async" on a method that contains an "await". I think that's a
reasonable choice.

Credits:
Credits Many thanks to my colleague Lucian for his insights and his excellent summary of the detailed design notes
which were the basis of this episode.

Next time:
time I want to talk a bit about exceptions and then take a break from async/await for a while. A dozen posts on
the same topic in just a few weeks is a lot.

(*) We have violated this principle on numerous occasions, both (1) by accident, and (2) deliberately, when the benefit
was truly compelling and the rate of breakage was likely to be low. The famous example of the latter is F(G<A,B>(7)).
In C# 1 that means that F has two arguments, both comparison operators. In C# 2 that means F has one argument and
G is a generic method of arity two.

(**) When I wrote that article I knew that we would be adding "await" as a prefix operator. It was an easy article to write
because we had recently gone through the process of noodling on the specification to find the possible points of
ambiguity. Of course I could not use "await" as the example back in September because we did not want to telegraph
the new C# 5 feature, so I picked "frob" as nicely meaningless.
Asynchrony in C# 5, Part Seven: Exceptions

Resuming where we left off (ha ha ha!) after that brief interruption: exception handling in "resumable" methods like our
coroutine-like asynchronous methods is more than a little bit weird. To get a sense of how weird it is, you might want
to first refresh your memory of my recent series on the design of iterator blocks, particularly the post about the
difference between a "push" model and a "pull" model. Briefly though:

In a regular code block, a try block surrounding a normal "synchronous" call site observes any exceptions that occur
within the call:

try { Q(); }
catch { ... }
finally { ... }

If Q() throws an exception then the catch block runs; when control leaves Q() by regular or exceptional means, the
finally block runs. Nothing unusual here.

Now consider an iterator block that has been rewritten into a MoveNext method of an enumerator. When that thing is
called it is called synchronously. If it throws an exception then the exception is handled by the nearest try-protected
region on the call stack. But suppose the iterator block itself has a try-
try-protected region that yields control back to the
caller:

try { yield return whatever; }


catch { ... }
finally { ... }

The yield statement returns control back to the caller, but the return does not activate the finally block. And if an
exception is thrown in the caller then the MoveNext() is no longer on the stack. Its exception handler has vanished. The
exception model of iterator blocks is pretty weird. The finally block only runs when control is in the MoveNext() method
and leaves the try-protected region by some mechanism other than yield return. Or when the enumerator is disposed
early, which can happen if the caller throws an exception that activates a finally block that disposes the enumerator. In
short: if the thing you've yielded control to has an exception that leaves the loop that is iterating the enumerator then
not Bizarre. That's why we made it illegal for
the finally block of the iterator probably runs, but the catch block does not!
you to yield in a try block that has a catch.

So what on earth are we going to do for methods with "awaits" in them? The situation is like the situation with iterator
blocks, but even more bizarre because of course the asynchronous task can itself throw an exception:
exception

async Task M()


{
try { await DoSomethingAsync(); }
catch { ... }
finally { ... }
}

We do want it to be legal to await something in a try block that has a catch. Suppose DoSomethingAsync throws before
it returns a task. No problem there; M is still on the stack, so the catch block runs. Suppose DoSomethingAsync returns
a task. M signs up the rest of itself as the continuation of the task, and immediately returns another task to its caller.
What happens when the job associated with the task returned by DoSomethingAsync is scheduled to run, and it throws
an exception? Logically we want M to still be "on the stack" so that its catch and finally run, just like it would if
DoSomething had been a synchronous call. (Unlike iterator blocks: we want the catch to run, not just the finally!) But M
is long gone; it has signed up a delegate that contains code that looks just like it as the continuation of a task, but M
and its try block are vanished. The task might not even be running on the thread that M ran on. Heck, it might not even
be running on the same continent if the task is actually farmed out to some service provider "in the cloud". What do we
do?

I said a few episodes back that exception handling in continuation passing style is easy; you just pass around two
continuations, one for the exceptional situation and one for the regular situation. That's not actually what we do here.
Instead what we do is: if the job throws an otherwise-uncaught exception then it is caught and the exception is stored
in the task. The task is then signaled as having completed unsuccessfully. When the continuation of the task resumes,
we do a "goto" into the middle of the try block (somehow) and check to see if the task blew up. If it did, then we can re-
throw the exception right there, and hey, this time there is a try-catch-finally that can handle the exception.

But suppose we do not handle the exception; maybe the catch block doesn't match. What do we do then? M's original
caller is again, long gone; the continuation is probably being called by some top-level message pump somewhere.
task We cache the exception again in that task, and then signal that task
What do we do? Well, remember, M returned a task.
as having completed unsuccessfully. Thus the buck is passed to the caller, which is of course what exception throwing
is all about: making your caller do the work of cleaning up your mess.

In short, M() is generated as something like this pseudo-C#:

Task M()
{
var builder = AsyncMethodBuilder.Create();
var state = State.Begin;
Action continuation = ()=>
{
try
{
if (state == State.AfterDoSomething) goto AfterDoSomething;
try
{
var awaiter = DoSomethingAsync().GetAwaiter;
state= State.AfterDoSomething;;
if (awaiter.BeginAwait(continuation))
return without running the finally;
AfterDoSomething:
awaiter.EndAwait(); // throws an exception if the task completed unsuccessfully
builder.SetResult();
return;
}
catch { ... }
finally { ... }
}
catch (Exception exception)
{
builder.SetException(exception); // signal this task as having completed unsuccessfully
return;
}
builder.SetResult();
};
continuation();
return builder.Task;
}

(Of course there are problems here; you cannot do a goto into the middle of a try block, the label is out of scope, and
so on. Ve have vays of making the compiler generate IL that works; it doesn't have to be legal C#. This is just a sketch.)

If the EndAwait throws an exception cached from the asynchronous operation then the catch and finally blocks run
normally. If the inner catch block doesn't handle it, or throws another exception, then the outer catch block gets it,
caches it in the task, and signals the task as having completed abnormally.
I have ignored several important cases in this brief sketch. For example, what if the method M is void returning? In that
situation there is no task constructed for M, and so there is nothing to be signalled as completed unsuccessfully, and
nowhere to cache the exception. What if DoSomethingAsync does a WhenAll on ten sub-tasks and two of them throw
an exception? What about the same scenario but with WhenAny?

Next time I'll talk a bit about these cases, muse about exception handling philosophy in general, and ask you whether
that philosophy gives good guidance or not. Then we'll take a short break for American Thanksgiving, and then pick up
with some topic other than asynchrony.
Asynchrony in C# 5, Part Eight: More Exceptions

(In this post I'll be talking about exogenous,


exogenous vexing,
vexing boneheaded and fatal exceptions. See this post for a definition of
those terms.)

If your process experiences an unhandled exception then clearly something bad and unanticipated has happened. If its
a fatal exception then you're already in no position to save the process; it is going down. You might as well leave it
unhandled, or just log it and rethrow it. If it had been anticipated because it's a vexing or exogenous exception then
there would be a handler in place for it. An unhandled vexing/exogenous exception is a bug, but probably one which
does not actually indicate a logic problem in the program's algorithms, it's just an oversight.

But if you have an unhandled boneheaded exception then that is evidence that your program has a very serious bug
indeed, a bug so bad that its operation cannot continue. The boneheaded exception should never have been thrown in
the first place; you never handle them, you make for darn sure they cannot possibly happen. If a boneheaded exception
is thrown then you have no idea whatsoever what locks were released early, what internal state is now corrupt or
inconsistent, and so on. You can't do anything with confidence, and often the best thing to do in that case is to
aggressively shut down the process before things get any worse.

We cannot easily tell the difference between bugs which are missing handlers for vexing/exogenous exceptions, and
which are bugs that have caused a program crash because something is broken in the implementation. The safest thing
to do is to assume that every unhandled
unhandled exception is either a fatal exception or an unhandled boneheaded exception.
In both cases, the right thing to do is to take down the process immediately.

This philosophy underlies the implementation of unhandled exceptions in the CLR. Way back in the CLR v1.0 days the
policy was that an unhandled exception on the "main" thread took down the process aggressively, but an unhandled
exception on a "worker" thread simply killed the thread and left the main thread running. (And an exception on the
finalizer thread was ignored and finalizers kept running.) This turned out to be a poor choice; the scenario it leads to is
that a server assigns a buggy subsystem to do some work on a bunch of worker threads; all the worker threads go
down silently, and the user is stuck with a server that is sitting there waiting patiently for results that will never come
because all the threads that produce results have disappeared. It is very difficult for the user to diagnose such a
problem; a server that is working furiously on a hard problem and a server that is doing nothing because all its workers
are dead look pretty much the same from the outside. The policy was therefore changed in CLR v2.0 such that an
unhandled exception on a worker thread also takes down the process by default. You want to be noisy about your
failures, not silent.

I am of the philosophical school that says that sudden, catastrophic failure of a software device is, of course,
unfortunate, but in many cases it is preferable that the software call attention to the problem so that it can be fixed,
fixed
rather than trying to muddle along in a bad state, possibly introducing a security hole or corrupting user data along the
way. Software that terminates itself upon encountering unexpected exceptions is software that is less vulnerable to
attackers taking advantage of its flaws. As Ripley said, when things go wrong you should take off and nuke the entire
site from orbit; it's the only way to be sure. But does this awesome philosophy serve the async scenario well?

Last time I mentioned two interesting scenarios: (1) what happens if a task-returning async method does a WhenAll or
WhenAny on multiple tasks, several of which throw exceptions? and (2) what if a void-returning async method awaits a
task which completes abnormally? What happens to that exception?

Let's consider the first case first.

WhenAll collects all the exceptions from its completed sub-tasks and stuffs them into an aggregating exception. When
all its sub-tasks complete, it completes its task abnormally with the aggregated exception. A slightly bizarre fact,
however, is that by default, the EndAwait only re-throws the first of those exceptions; it does not re-throw the entire
aggregating exception. The more common scenario is for any try-catch surrounding an "await" to be catching some set
of specific exceptions; making you always write code that goes and unpacks the aggregating exception seems onerous.
This may seem slightly odd; for more details on why this is a reasonable idea see Jon Skeet's recent posts on the topic.

The WhenAny case is similar. Suppose the first sub-task completes, either normally or abnormally. That completes the
WhenAny task, either normally or abnormally. Suppose one of the additional sub-tasks completes abnormally; what
happens to its exception? The WhenAny is done: it has already completed and called its continuation, which is now
scheduled to run on some work queue if it hasn't already.

In both the WhenAll and WhenAny cases we have a situation where there could be an exception that goes "unobserved"
by the creator of the WhenAll or WhenAny task. That is to say, in both these cases there could be an exception that is
thrown, automatically caught, cached and never thrown again which in the equivalent synchronous code would have
brought down the process.

This seems potentially bad. Should an unobserved exception from a task that was asynchronously awaited take down
the process, as the equivalent synchronous code would have?

Suppose we decide that yes, an unobserved exception should take down the process. When does that happen? That is,
re-
when do we definitively know that the exception actually was not re-thrown?
thrown We only know that if the task object is
finalized without its result ever being observed.
observed After all, a "living" task object that has completed abnormally could
have its continuation executed at any time in the future; it cannot know when that continuation is going to be
scheduled. There could be any number of queued-up tasks on this thread that get to run between the time this task
completed abnormally and its result is requested. As long as the task object is alive then its exception could be
observed.

OK, so, great, if a task is finalized, and it completed abnormally then we... what? Throw the exception on the finalizer
thread? Sure! That will take down the process, right? In CLR v2.0 and above, unhandled exceptions on any thread take
down the process. But let's take a step back. Remind me, why do we want an unobserved exception to take down the
process? The philosophical reason is: we cannot tell whether this was a boneheaded exception that indicates a
potentially horrible, security-impacting situation that needs to be dealt with by immediate termination, or simply the
result of a missing handler for an unanticipated exogenous exception. The safe thing to do is to say that it was a
boneheaded exception with a security impact and immediately take the process down. Which is precisely what we are
not doing! We are waiting for the task to be collected by the garbage collector and then trying to take the process down
in the finalizer thread. But in the gap between the exception being recorded in the task and the finalizer observing the
exception, we've potentially kept right on running dozens more tasks, any of which could be using the inconsistent
state caused by the boneheaded exception.

Furthermore, we anticipate that most async tasks that throw exceptions in realistic code will in fact be throwing
exogenous exceptions like "the password for this web service is wrong" or "you don't have permission to read this file",
or "this operation timed out", rather than boneheaded exceptions like "you dereferenced null" or "you tried to pop an
empty stack". In these realistic cases it seems much more plausible to say that if for some reason a task completes
abnormally and no one bothers to observe its result, it's because some asynchronous unit of work was abandoned; any
of its sub-tasks that ran into problems connecting to web servers (or whatever) can safely be ignored.

In short, an unobserved exception from a finalized task is one that no one cares about,
about is probably harmless,
harmless and if it
was harmful, then we've already delayed taking action too long to prevent more harm.
harm Either way, we might as well just
ignore it.
This does illustrate that asynchronous programming introduces a new flavour of security vulnerability. If there is a
security vulnerability caused by a bug that would normally take down the process, and if that code is rewritten to be
asynchronous, and if the buggy task is abandoned without observation of its exception, then the bug might not result
in an aggressive destruction of the now-vulnerable process. And even if the exception is eventually observed, there
might be a window in time between when the bug introduces the vulnerability and the exception is observed. That
window might be large enough for an attacker to succeed. That sounds like a tortuous chain of things that have to go
wrong - because it is - but attackers will take whatever they can get. They are crafty, they have all the time in the
world, and they only have to succeed once.

I never did say what happens to a void-returning method that awaits a task; you can think of this as a "fire and forget"
sort of method. Perhaps a void-returning button-click event handler awaits fetching some data asynchronously and
then updating the user interface; there's no "caller" of the event handler that cares to hold on to a task, and will never
observe its result. So what happens if the data-fetching task completes abnormally?

In that case, when the void-returning method (which registered itself as a continuation, remember) starts up again, it
checks to see if the task completed abnormally. If it did, then it immediately re-throws the exception to its caller, which
is, of course, probably some message loop. I believe the plan of action here is to be consistent with the behaviour
described above; in that scenario the message loop will discard the exception, assuming that the fire-and-forget
asynchronous method failed in some benign way.

Having been an advocate of the "nuke from orbit" philosophy of unhandled exceptions for many years, emotionally this
does not sit well with me, but I'm unable to marshal a convincing argument against this strategy for dealing with
exceptions in task-based asynchrony. Readers: What do you think? What is in your opinion the right thing to do in
scenarios where exceptions of tasks go unobserved?

And on that somewhat ominous note, I'm going to take a break from talking about the new Task Asynchrony Pattern for
now. Please download the CTP, keep sending us your feedback and questions, and start thinking about what sorts of
things that will work well or work poorly with this new feature. Next time: we'll pick up with more fabulous adventures
after American Thanksgiving; I'm cooking turkey for 19 this year, which should be quite the adventure in of itself.

You might also like