{"id":103840,"date":"2020-06-05T07:00:00","date_gmt":"2020-06-05T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103840"},"modified":"2020-06-11T06:18:45","modified_gmt":"2020-06-11T13:18:45","slug":"20200605-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200605-00\/?p=103840","title":{"rendered":"Using fibers to expand a thread&#8217;s stack at runtime, part 4"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200604-00\/?p=103833\"> Last time<\/a>, we transported some ephemeral error state from the temporary fiber to the originating thread. A common way of reporting an error in C++ is to use an exception, so let&#8217;s use that.<\/p>\n<pre><span style=\"color: blue;\">[[noreturn]] void ThrowWin32Error(DWORD error);<\/span>\r\n\r\ntemplate&lt;typename RetType&gt;\r\nRetType RunOnFiberWorker(\r\n    RetType (*callback)(void*),\r\n    void* parameter)\r\n    <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">RetType failureValue<\/span><\/span>\r\n{\r\n  <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">static_assert(std::is_trivially_copyable_v&lt;RetType&gt;);<\/span>\r\n  \/\/ <span style=\"text-decoration: line-through;\">static_assert(std::is_trivially_destructible_v&lt;RetType&gt;);<\/span><\/span>\r\n\r\n  struct State\r\n  {\r\n    RetType (*callback)(void*);\r\n    void* parameter;\r\n    <span style=\"color: blue;\">std::variant&lt;std::exception_ptr, RetType&gt;\r\n      capturedValue;<\/span>\r\n    HANDLE originalFiber;\r\n\r\n    void FiberProc()\r\n    {\r\n      <span style=\"color: blue;\">try {\r\n        capturedValue.template emplace&lt;1&gt;\r\n          (callback(parameter));\r\n      } catch (...) {\r\n        capturedValue.template emplace&lt;0&gt;\r\n          (std::current_exception());\r\n      }<\/span>\r\n      SwitchToFiber(originalFiber);\r\n    }\r\n\r\n  } state{ callback, parameter };\r\n  <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">std::move(failureValue), errno<\/span><\/span>\r\n\r\n  DWORD error = RunOnFiberTypeNeutral(\r\n    [](void* parameter)\r\n    {\r\n      reinterpret_cast&lt;State*&gt;(parameter)-&gt;FiberProc();\r\n    }, &amp;state, &amp;state.originalFiber);\r\n  if (error != ERROR_SUCCESS) {\r\n    <span style=\"color: blue;\">ThrowWin32Error(error);<\/span>\r\n  }\r\n\r\n  <span style=\"color: blue;\">auto e = std::get_if&lt;0&gt;(&amp;state.capturedValue);\r\n  if (e) std::rethrow_exception(*e);\r\n  return std::get&lt;1&gt;(std::move(state.capturedValue));<\/span>\r\n}\r\n\r\ntemplate&lt;typename Lambda&gt;\r\n<span style=\"color: blue;\">auto<\/span> RunOnFiber(Lambda&amp;&amp; lambda)\r\n{\r\n  using Type = std::remove_reference_t&lt;Lambda&gt;;\r\n  using RetType = decltype(lambda());\r\n  return RunOnFiberWorker&lt;RetType&gt;(\r\n    [](void* parameter)\r\n    {\r\n      return (*reinterpret_cast&lt;Type*&gt;(parameter))();\r\n    }, &amp;lambda);\r\n}\r\n<\/pre>\n<p>In this case, we capture the result of the callback with a <code>std::variant<\/code> of an <code>exception_ptr<\/code> or the formal return value. The <code>exception_ptr<\/code> is used if the callback threw an exception.<\/p>\n<p>The <code>exception_ptr<\/code> is the first type in the variant because the <code>RetType<\/code> may not be default-constructible, and even if it has a default constructor, that default constructor may be heavy with unwanted side-effects. Putting the <code>exception_ptr<\/code> as the first type in the variant means that the default constructor for the variant creates an <code>exception_ptr<\/code>, which is default-constructible.<\/p>\n<p>We then ask <code>Run\u00adOn\u00adFiber\u00adType\u00adNeutral<\/code> to do the fiber magic. If it failed, then we use some program-specific <code>Throw\u00adWin32\u00adError()<\/code> function to transform the Win32 error into some kind of an exception.<\/p>\n<p>Otherwise, we know that the fiber ran to completion. In the fiber procedure, we call the callback and we save the result in the <code>capturedValue<\/code> variable as the <code>RetType<\/code>. If the callback threw an exception, we catch the exception and stow it in the <code>capturedValue<\/code> as an <code>exception_ptr<\/code>.<\/p>\n<p>After returning to the original thread, we keep inside the variant to see whether it holds an exception or a value. If it holds an exception, we rethrow it. Otherwise, we return the value.<\/p>\n<p>We must use the explicit index versions of <code>variant::<\/code><code>emplace<\/code>, <code>get<\/code>, and <code>get_if<\/code> rather than the more readable type-based versions because the type-based version won&#8217;t work if the <code>RetType<\/code> is <code>exception_ptr<\/code>!<\/p>\n<p>Observe that the <code>RetType<\/code> is always moved. There is no requirement that it be default-constructible or copyable.<\/p>\n<p>Note that the above code does not work if the lambda returns a reference. I&#8217;ll leave that as an exercise.<\/p>\n<p>Even if you don&#8217;t plan on working with fibers, this series showed how to transport state between threads, which is still useful.<\/p>\n<p>But wait, our discussion of using fibers to expand a stack dynamically isn&#8217;t over. We&#8217;ll pick up additional topics next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Moving an exception from a fiber to the main thead.<\/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-103840","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Moving an exception from a fiber to the main thead.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103840","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=103840"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103840\/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=103840"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103840"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103840"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}