{"id":102362,"date":"2019-03-26T07:00:00","date_gmt":"2019-03-26T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102362"},"modified":"2019-06-06T17:38:49","modified_gmt":"2019-06-07T00:38:49","slug":"20190326-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190326-00\/?p=102362","title":{"rendered":"When do <CODE>Core&shy;Dispatcher.<\/CODE><CODE>Run&shy;Async<\/CODE> and <CODE>Thread&shy;Pool.<\/CODE><CODE>Run&shy;Async<\/CODE> actions complete?"},"content":{"rendered":"<p>The <code>Core&shy;Dispatcher::<\/code><code>Run&shy;Async<\/code> and <code>Thread&shy;Pool::<\/code><code>Run&shy;Async<\/code> methods take a delegate and schedule it to be invoked on the dispatcher thread or on a thread pool thread. These methods return an <code>IAsync&shy;Action<\/code>, but when does that action complete? <\/p>\n<p>When dealing with asynchronous methods, there are two ways of talking about the result. <\/p>\n<p>First, there&#8217;s the return value of the asynchronous method, which at the ABI level is an <code>IAsync&shy;Action<\/code> or <code>IAsync&shy;Operation<\/code>. Depending on the language projection, this is exposed to the programmer as a language-specific object. <\/p>\n<table CLASS=\"cp3\" CELLPADDING=\"3\" BORDER=\"1\" STYLE=\"border-collapse: collapse\">\n<tr>\n<th>Projection<\/th>\n<th><code>IAsync&shy;Action<\/code><\/th>\n<th><code>IAsync&shy;Operation&lt;T&gt;<\/code><\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr>\n<td>C++\/WinRT<\/td>\n<td><code>IAsync&shy;Action<\/code><\/td>\n<td><code>IAsync&shy;Operation&lt;T&gt;<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>C++\/CX<\/td>\n<td><code>IAsync&shy;Action^<\/code><br>        <code>task&lt;void&gt;<\/code><\/td>\n<td><code>IAsync&shy;Operation&lt;T&gt;^<\/code><br>        <code>task&lt;T&gt;<\/code><\/td>\n<td ROWSPAN=\"2\">Projected as <code>IAsyncXxx<\/code><br>    usually wrapped into<br><code>task<\/code>\/<code>Task<\/code>.<\/td>\n<\/tr>\n<tr>\n<td>C#<\/td>\n<td><code>IAsync&shy;Action<\/code><br>        <code>Task<\/code><\/td>\n<td><code>IAsync&shy;Operation&lt;T&gt;<\/code><br>        <code>Task&lt;T&gt;<\/code><\/td>\n<\/tr>\n<tr>\n<td>JavaScript<\/td>\n<td><code>Promise<\/code><\/td>\n<td><code>Promise<\/code><\/td>\n<td><\/td>\n<\/tr>\n<\/table>\n<p>The second result is the thing that you receive when the asynchronous operation completes. <\/p>\n<table CLASS=\"cp3\" CELLPADDING=\"3\" BORDER=\"1\" STYLE=\"border-collapse: collapse\">\n<tr>\n<th>Projection<\/th>\n<th><code>IAsync&shy;Action<\/code><\/th>\n<th><code>IAsync&shy;Operation&lt;T&gt;<\/code><\/th>\n<\/tr>\n<tr>\n<td>C++\/WinRT<\/td>\n<td><code>void<\/code><\/td>\n<td><code>T<\/code><\/td>\n<\/tr>\n<tr>\n<td>C++\/CX<\/td>\n<td><code>void<\/code><\/td>\n<td><code>T<\/code><\/td>\n<\/tr>\n<tr>\n<td>C++\/CX + PPL<\/td>\n<td><code>void<\/code><\/td>\n<td><code>T<\/code><\/td>\n<\/tr>\n<tr>\n<td>C#<\/td>\n<td><code>void<\/code><\/td>\n<td><code>T<\/code><\/td>\n<\/tr>\n<tr>\n<td>JavaScript<\/td>\n<td><code>undefined<\/code><\/td>\n<td><code>T<\/code><\/td>\n<\/tr>\n<\/table>\n<p>And there&#8217;s also a third thing to worry about, which is <i>when<\/i> you receive that completion result. <\/p>\n<p>Let&#8217;s answer the three questions for the <code>Core&shy;Dispatcher::<\/code><code>Run&shy;Async<\/code> and <code>Thread&shy;Pool::<\/code><code>Run&shy;Async<\/code> methods. <\/p>\n<p>First, they <i>return<\/i> an <code>IAsync&shy;Action<\/code>. The methods schedule the delegate to be invoked later, and then return an <code>IAsync&shy;Action<\/code> representing the pending operation. <\/p>\n<p>Second, they <i>complete<\/i> with <code>void<\/code>. There is no additional information reported when the operation completes. <\/p>\n<p>Third (and most interesting) is that they complete <a HREF=\"https:\/\/docs.microsoft.com\/en-us\/uwp\/api\/windows.ui.core.coredispatcher.runasync#await-a-ui-task-sent-from-a-background-thread\">when the delegate <i>returns<\/i><\/a>. <\/p>\n<p>Not when the delegate <i>completes<\/i>. <\/p>\n<p>This means that when you pass a delegate that itself represents an asynchronous operation, the <code>IAsync&shy;Action<\/code> returned by <code>Run&shy;Async<\/code> completes once your delegate returns its own async operation. The dispatcher or thread pool doesn&#8217;t even see that async operation; it&#8217;s eaten by your language projection. All that the dispatcher or thread pool knows is that it invoked the delegate, and the delegate returned <code>void<\/code>, so we must be done. <\/p>\n<p>The C++\/WinRT, and JavaScript projections permit your delegate to return someting, even though the formal function signature returns <code>void<\/code>. The projection just throws your return value away, and the caller gets nothing. The C# language lets you make a function formally return <code>void<\/code>, even though it secretly continues running asynchronously. The syntax for this is <code>async void<\/code>, and I&#8217;ve discussed <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170721-00\/?p=96665\">the perils of <code>async void<\/code><\/a> in the past. <\/p>\n<p>This means that if you await the result of a <code>Run&shy;Async<\/code>, the await will complete when your delegate either returns or performs its own await operation, whichever comes first. <\/p>\n<pre>\n\/\/ C++\/WinRT\n\nco_await Dispatcher().RunAsync(CoreDispatcherPriority::Normal,\n    [lifetime = get_strong()]() -&gt; fire_and_forget\n    {\n        co_await SomethingAsync();\n        co_await SomethingElseAsync();\n        Finished();\n    });\n\n\/\/ C++\/CX\n\ncreate_task(Dispatcher-&gt;RunAsync(CoreDispatcherPriority::Normal,\n    ref new DispatchedHandler([this]()\n    {\n        create_task(SomethingAsync()).then([this]() {\n            return create_task(SomethingElseAsync());\n        }).then([this]() {\n            Finished();\n        });\n    }))).then([this]()\n    {\n        BackOnMainThread();\n    });\n\n\n\/\/ C++\/CX + co_await\n\nco_await Dispatcher-&gt;RunAsync(CoreDispatcherPriority::Normal,\n    ref new DispatchedHandler([this]()\n    {\n        []() -&gt; task&lt;void&gt;\n        {\n            co_await SomethingAsync();\n            co_await SomethingElseAsync();\n            Finished();\n        }();\n    }));\nBackOnMainThread();\n\n\/\/ C#\n\nawait Dispatcher.RunAsync(CoreDispatcherPriority::Normal, async () =&gt;\n    {\n        await SomethingAsync();\n        await SomethingElseAsync();\n        Finished();\n    });\nBackOnMainThread();\n\n\/\/ JavaScript (pretend)&sup1;\n\nawait dispatcher.runAsync(CoreDispatcherPriority.normal, async () =&gt;\n    {\n        await somethingAsync();\n        await somethingElseAsync();\n        finished();\n    });\nbackOnMainThread();\n<\/pre>\n<p>When does the <code>await<\/code>\/<code>co_await<\/code> complete and the <code>Back&shy;On&shy;Main&shy;Thread<\/code> run? <\/p>\n<p>Answer: When <code>Something&shy;Async<\/code> returns its <code>IAsync&shy;Action<\/code>, that action gets wrapped inside a coroutine, and execution suspends, returning control to the dispatcher or thread pool. At this point, the delegate has returned, and the <code>Run&shy;Async<\/code> declares its action to have completed. The object representing the coroutine (the <code>IAsyncAction<\/code>, <code>task<\/code>, <code>Task<\/code>, or <code>Promise<\/code>) is simply discarded. <\/p>\n<p>In C++\/WinRT and JavaScript, the discarding is done by the projection. In C++\/CX, the discarding is explicit in the code: Observe that we create a task but do not <code>return<\/code> it. In C#, the discarding is done by the language itself because an <code>async<\/code> lambda can be implicitly converted to a non-async <code>void<\/code> lambda (by treating it as if were <code>async void<\/code>). <\/p>\n<p>Another way of looking at this analysis is that the lambda returns when it encounters its first <code>await<\/code>\/<code>co_await<\/code> or <code>return<\/code>. This in turn causes the <code>Run&shy;Async<\/code> to complete its own <code>IAsync&shy;Action<\/code>. <\/p>\n<p>If we write things out explicitly, the sequence of operations might be more clear: <\/p>\n<pre>\n\/\/ C#\nasync () =&gt;\n{\n    await SomethingAsync();\n    await SomethingElseAsync();\n    Finished();\n}\n<\/pre>\n<p>This gets transformed by the compiler into <\/p>\n<pre>\nclass Lambda\n{\n    async void Invoke()\n    {\n        await SomethingAsync();\n        await SomethingElseAsync();\n        Finished();\n    }\n}\n<\/pre>\n<p>which gets further transformed into <\/p>\n<pre>\nclass Lambda\n{\n    void Invoke()\n    {\n        Task task1 = SomethingAsync();\n        task1.ContinueWith(_ =&gt; {\n            Task task2 = SomethingElseAsync();\n            task2.ContinueWith(_ =&gt; {\n                Finished();\n            });\n        });\n    }\n}\n<\/pre>\n<p>Once <code>Something&shy;Async<\/code> returns its <code>Task<\/code>, the lambda attaches a continuation to it, so that it can resume execution when the task completes. At that point, the outer lambda has finished its work, and the <code>Invoke<\/code> method returns. This returns control back to the delegate or thread pool, which declares that the <code>Run&shy;Async<\/code> has completed. And the completion of <code>Run&shy;Async<\/code> means that <code>Back&shy;On&shy;Main&shy;Thread<\/code> starts to run. <\/p>\n<p>This behavior is usually not what you want. You want to wait until the lambda has <i>completed<\/i>, not just returned. We&#8217;ll look at one possible solution next time. <\/p>\n<p>&sup1; JavaScript is a single-threaded language, so you can&#8217;t actually do this, but I included it for completeness to demonstrate what <i>would<\/i> happen if it were possible. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>When the delegate returns, not when it completes.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-102362","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>When the delegate returns, not when it completes.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102362","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=102362"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102362\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=102362"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102362"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102362"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}