{"id":107755,"date":"2023-01-27T07:00:00","date_gmt":"2023-01-27T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107755"},"modified":"2023-01-26T20:23:13","modified_gmt":"2023-01-27T04:23:13","slug":"20230127-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230127-00\/?p=107755","title":{"rendered":"Inside C++\/WinRT: Apartment switching: Unwinding the stack"},"content":{"rendered":"<p>Last time, <a title=\"Inside C++\/WinRT: Apartment switching: Bypassing the context callback\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230126-00\/?p=107753\"> we found a case where we could avoid calling <code>IContext\u00adCallback::<wbr \/>Context\u00adCallback()<\/code><\/a>, thereby reducing stack usage.<\/p>\n<p>Another case where we can avoid having to call <code>IContext\u00adCallback::<wbr \/>Context\u00adCallback()<\/code> is the case where we are resuming on the multi-threaded apartment (MTA). In that case, we can just resume on a new threadpool thread, since threadpool tasks run in the implicit multi-threaded apartment. This is an even better way of reducing stack usage, because it releases the original thread entirely.<\/p>\n<p>In order to know what kind of apartment we captured, we&#8217;ll have to remember the apartment type as well as the <code>IContext\u00adCallback<\/code>. We&#8217;ll keep both of them in a new structure:<\/p>\n<pre>struct resume_apartment_context\r\n{\r\n    resume_apartment_context() = default;\r\n    resume_apartment_context(std::nullptr_t) :\r\n        m_context(nullptr) {}\r\n\r\n    bool valid() const noexcept\r\n    {\r\n        return m_context != nullptr;\r\n    }\r\n\r\n    com_ptr&lt;IContextCallback&gt; m_context =\r\n        capture&lt;IContextCallback&gt;(WINRT_IMPL_CoGetObjectContext);\r\n    <span style=\"color: #08f;\">int32_t m_context_type = get_apartment_type().first;<\/span>\r\n};\r\n<\/pre>\n<p>When switching apartments, we add a check to see if we are switching to the MTA. If so, then we just queue the coroutine contination directly to a threadpool thread.<\/p>\n<pre>inline auto resume_apartment(\r\n    <span style=\"color: #08f;\">resume_apartment_context<\/span> const&amp; context,\r\n    coroutine_handle&lt;&gt; handle)\r\n{\r\n    WINRT_ASSERT(context.valid());\r\n    if (context<span style=\"color: #08f;\">.m_context<\/span> ==\r\n        capture&lt;IContextCallback&gt;(WINRT_IMPL_CoGetObjectContext))\r\n    {\r\n        handle();\r\n    }\r\n    <span style=\"color: #08f;\">else if (context.m_context_type == 1 \/* APTTYPE_MTA \/)\r\n    {\r\n        resume_background(handle);\r\n    }\r\n    else<\/span> if (is_sta_thread())\r\n    {\r\n        resume_apartment_on_threadpool(context<span style=\"color: #08f;\">.m_context<\/span>, handle);\r\n    }\r\n    else\r\n    {\r\n        resume_apartment_sync(context<span style=\"color: #08f;\">.m_context<\/span>, handle);\r\n    }\r\n}\r\n<\/pre>\n<p>The <code>resume_<wbr \/>background()<\/code> function resumes the coroutine on a threadpool thread:<\/p>\n<pre>inline void __stdcall resume_background_callback(\r\n    void*, void* context) noexcept\r\n{\r\n    coroutine_handle&lt;&gt;::from_address(context)();\r\n};\r\n\r\ninline auto resume_background(coroutine_handle&lt;&gt; handle)\r\n{\r\n    submit_threadpool_callback(\r\n        resume_background_callback, handle.address());\r\n}\r\n<\/pre>\n<p>The <code>apartment_<wbr \/>context<\/code> holds this structure instead of just the <code>IContext\u00adCallback<\/code>.<\/p>\n<pre>struct apartment_context\r\n{\r\n    apartment_context() = default;\r\n    apartment_context(std::nullptr_t) : context(nullptr) { }\r\n\r\n    operator bool() const noexcept { return <span style=\"color: #08f;\">context.valid()<\/span>; }\r\n    bool operator!() const noexcept { return <span style=\"color: #08f;\">!context.valid()<\/span>; }\r\n\r\n    <span style=\"color: #08f;\">resume_apartment_context context;<\/span>\r\n};\r\n<\/pre>\n<p>And the Windows Runtime awaiter captures it into the lambda:<\/p>\n<pre>template &lt;typename Async&gt;\r\nstruct await_adapter\r\n{\r\n    await_adapter(Async const&amp; async) : async(async) { }\r\n\r\n    Async const&amp; async;\r\n\r\n    bool await_ready() const noexcept\r\n    {\r\n        return false;\r\n    }\r\n\r\n    void await_suspend(std::experimental::coroutine_handle&lt;&gt; handle) const\r\n    {\r\n        auto extend_lifetime = async;\r\n        async.Completed([\r\n            handle,\r\n            context = <span style=\"color: #08f;\">resume_apartment_context()<\/span>\r\n        ](auto&amp;&amp; ...)\r\n        {\r\n            resume_apartment(context.context, handle);\r\n        });\r\n    }\r\n\r\n    auto await_resume() const\r\n    {\r\n        return async.GetResults();\r\n    }\r\n};\r\n<\/pre>\n<p>Next time, we&#8217;ll look at another feature of C++\/WinRT that needs to be added to our apartment switching framework.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reducing stack usage.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-107755","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Reducing stack usage.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107755","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=107755"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107755\/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=107755"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107755"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107755"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}