{"id":2185,"date":"2021-07-16T13:32:14","date_gmt":"2021-07-16T11:32:14","guid":{"rendered":"https:\/\/alax.info\/blog\/?p=2185"},"modified":"2021-07-16T13:36:14","modified_gmt":"2021-07-16T11:36:14","slug":"wait-for-iasyncaction-on-sta-thread","status":"publish","type":"post","link":"https:\/\/alax.info\/blog\/2185","title":{"rendered":"Wait for IAsyncAction on STA thread"},"content":{"rendered":"\n<p>Figured out how to elegantly do a blocking wait for an asynchronous coroutine-enabled function on a STA thread. <\/p>\n\n\n\n<p>You can&#8217;t do this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \/std:c++latest \/await\n\n#include &lt;unknwn.h&gt;\n#include &lt;winrt\\base.h&gt;\n#include &lt;winrt\\Windows.Foundation.h&gt;\n\n#pragma comment(lib, \"windowsapp.lib\")\n\nwinrt::Windows::Foundation::IAsyncAction Foo()\n{\n    co_return;\n}\n\nint main()\n{\n    winrt::init_apartment(winrt::apartment_type::single_threaded);\n    Foo().get(); \/\/ &lt;&lt;--- Debug Assertion Failed!\n    return 0;\n}\n<\/code><\/pre>\n\n\n\n<p>There is an assertion failure because <code>.get()<\/code> assumes ability to block. On STA this hits a failure in <code>winrt::impl::blocking_suspend<\/code> call.<\/p>\n\n\n\n<p>So you have to avoid doing <code>.get()<\/code> to synchronize and there should be a message pump (you might need it for another reason anyway or why would you want non-default <code>single_threaded<\/code> in first place?).<\/p>\n\n\n\n<p>So you would get something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \/std:c++latest \/await\n\n#include &lt;unknwn.h&gt;\n#include &lt;winrt\\base.h&gt;\n#include &lt;winrt\\Windows.Foundation.h&gt;\n\n#pragma comment(lib, \"windowsapp.lib\")\n\nwinrt::Windows::Foundation::IAsyncAction Foo()\n{\n    co_return;\n}\n\nint main()\n{\n    winrt::init_apartment(winrt::apartment_type::single_threaded);\n    winrt::handle CompletionEvent { CreateEvent(nullptr, TRUE, FALSE, nullptr) };\n    auto const Action { Foo() };\n    Action.Completed(&#91;&amp;](winrt::Windows::Foundation::IAsyncAction const&amp;, winrt::Windows::Foundation::AsyncStatus Status) \n    {\n        WINRT_ASSERT(Status == winrt::Windows::Foundation::AsyncStatus::Completed);\n        WINRT_VERIFY(SetEvent(CompletionEvent.get()));\n    });\n    HANDLE const Objects&#91;] { CompletionEvent.get() };\n    for(; ; )\n    {\n        auto const WaitResult = MsgWaitForMultipleObjects(static_cast&lt;DWORD&gt;(std::size(Objects)), Objects, FALSE, INFINITE, QS_ALLEVENTS);\n        if(WaitResult == WAIT_OBJECT_0 + 0) \/\/ CompletionEvent\n            break;\n        WINRT_ASSERT(WaitResult == WAIT_OBJECT_0 + std::size(Objects));\n        MSG Message;\n        while(PeekMessageW(&amp;Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))\n            DispatchMessageW(&amp;Message);\n    }\n    return 0;\n}\n<\/code><\/pre>\n\n\n\n<p>Now the question is what if the <code>Foo<\/code> function needs to switch context while being on a STA thread, would it need to repeat the same pattern and dispatch messages while waiting?<\/p>\n\n\n\n<p>NO!<\/p>\n\n\n\n<p>Use of <code>apartment_context<\/code> enables to switch context and return back to STA in the coroutine execution sequnce, while being on outer message pump between the coroutines.<\/p>\n\n\n\n<p>Below is full sample code that does strange threading things in the <code>Foo<\/code> function with threads and COM apartment checks, then return to calling STA in the end of the day. Additionally, it posts a message from the worker thread and makes sure that outer message pump catches it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \/std:c++latest \/await\n\n#include &lt;unknwn.h&gt;\n#include &lt;winrt\\base.h&gt;\n#include &lt;winrt\\Windows.Foundation.h&gt;\n\n#pragma comment(lib, \"windowsapp.lib\")\n\nusing namespace winrt::Windows::Foundation;\n\n#include &lt;chrono&gt;\n#include &lt;thread&gt;\n\nusing namespace std::chrono_literals;\n\nvoid ApartmentCheck(APTTYPE ExpectType, APTTYPEQUALIFIER ExpectQualifier)\n{\n    APTTYPE Type;\n    APTTYPEQUALIFIER Qualifier;\n    WINRT_VERIFY(SUCCEEDED(CoGetApartmentType(&amp;Type, &amp;Qualifier)));\n    WINRT_ASSERT(Type == ExpectType &amp;&amp; Qualifier == ExpectQualifier);\n}\n\nIAsyncAction Foo()\n{\n    ApartmentCheck(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE);\n    winrt::apartment_context Context;\n    winrt::handle ExternalEvent { CreateEvent(nullptr, TRUE, FALSE, nullptr) };\n    {\n        auto const ThreadIdentifier = GetCurrentThreadId();\n        std::thread SimulationThread(&#91;&amp;] \n        {\n            WINRT_VERIFY(PostThreadMessageW(ThreadIdentifier, WM_APP, 0, 0));\n            std::this_thread::sleep_for(5s);\n            WINRT_VERIFY(SetEvent(ExternalEvent.get())); \n        });\n        \/\/co_await winrt::resume_background();\n        co_await winrt::resume_on_signal(ExternalEvent.get());\n        SimulationThread.join();\n    }\n    ApartmentCheck(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA); \/\/ MtaThread enables this, see below\n    co_await Context;\n    ApartmentCheck(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE);\n    co_return;\n}\n\nint main()\n{\n    winrt::init_apartment(winrt::apartment_type::single_threaded);\n    winrt::handle MtaThreadTerminationEvent { CreateEvent(nullptr, TRUE, FALSE, nullptr) };\n    std::thread MtaThread(&#91;&amp;] \n    { \n        winrt::init_apartment();\n        WINRT_VERIFY(WaitForSingleObject(MtaThreadTerminationEvent.get(), INFINITE) == WAIT_OBJECT_0);\n    });\n    std::this_thread::sleep_for(1s);\n    winrt::handle CompletionEvent { CreateEvent(nullptr, TRUE, FALSE, nullptr) };\n    auto const Action { Foo() };\n    Action.Completed(&#91;&amp;](IAsyncAction const&amp;, AsyncStatus Status) \n    {\n        WINRT_ASSERT(Status == AsyncStatus::Completed);\n        WINRT_VERIFY(SetEvent(CompletionEvent.get()));\n    });\n    unsigned int MessageCount = 0;\n    HANDLE const Objects&#91;] { CompletionEvent.get() };\n    for(; ; )\n    {\n        auto const WaitResult = MsgWaitForMultipleObjects(static_cast&lt;DWORD&gt;(std::size(Objects)), Objects, FALSE, INFINITE, QS_ALLEVENTS);\n        if(WaitResult == WAIT_OBJECT_0 + 0) \/\/ CompletionEvent\n            break;\n        WINRT_ASSERT(WaitResult == WAIT_OBJECT_0 + std::size(Objects));\n        MSG Message;\n        while(PeekMessageW(&amp;Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))\n        {\n            WINRT_ASSERT(Message.message == WM_USER || Message.message == WM_APP);\n            if(Message.message == WM_APP)\n                MessageCount++;\n            DispatchMessageW(&amp;Message);\n        }\n    }\n    WINRT_ASSERT(MessageCount == 1);\n    WINRT_VERIFY(SetEvent(MtaThreadTerminationEvent.get()));\n    MtaThread.join();\n    return 0;\n}\n\n<\/code><\/pre>\n\n\n\n<p>Some additional comments to the code:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>MtaThread<\/code> is necessary for thread pool threads to belong to <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130419-00\/?p=4613\">implicit MTA<\/a> or otherwise COM backed STA return would not work<\/li><li>Initial sleep is to make sure that MTA is up<\/li><li><code>DispatchMessageW<\/code> would dispatch two messages, one that we <code>PostThreadMessageW<\/code> ourselves and the other <code>WM_USER<\/code> one which is a part of <code>co_await Context;<\/code> call)<\/li><li><code>SimulationThread<\/code> is featuring externally set asynchronous event<\/li><li>Commented out <code>co_await winrt::resume_background();<\/code> indicates that there is no need in explicit switch to a worker thread: coroutine tech itself would suspend execution and continue on a thread pool thread (or maybe it&#8217;s implementation specific?)<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Figured out how to elegantly do a blocking wait for an asynchronous coroutine-enabled function on a STA thread. You can&#8217;t do this: There is an assertion failure because .get() assumes ability to block. On STA this hits a failure in winrt::impl::blocking_suspend call. So you have to avoid doing .get() to synchronize and there should be&hellip; <\/p>\n<p><a class=\"moretag\" href=\"https:\/\/alax.info\/blog\/2185\">Read the full article<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[588],"class_list":["post-2185","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-c-winrt"],"_links":{"self":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/2185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/comments?post=2185"}],"version-history":[{"count":0,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/2185\/revisions"}],"wp:attachment":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/media?parent=2185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/categories?post=2185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/tags?post=2185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}