{"id":104934,"date":"2021-03-05T07:00:00","date_gmt":"2021-03-05T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104934"},"modified":"2021-03-08T07:13:04","modified_gmt":"2021-03-08T15:13:04","slug":"20210305-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210305-00\/?p=104934","title":{"rendered":"Creating a co_await awaitable signal that can be awaited multiple times, part 5"},"content":{"rendered":"<p>So far, we&#8217;ve been <a title=\"Creating a co_await awaitable signal that can be awaited multiple times, part 4\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210304-00\/?p=104926\"> creating an awaitable signal that can be awaited multiple times<\/a>.<\/p>\n<p>In the most recent incarnation, we reversed the list in order to approximate FIFO resume. Doing this at resume time means that the list of nodes gets walked twice, once when reversing, and once when resuming. Let&#8217;s fix this by keeping a separate pointer to the last pointer, so we can append nodes to the end of the list in <var>O<\/var>(1). This adds an extra pointer to the size of the <code>awaitable_<wbr \/>event<\/code>, but we will earn that memory back by overloading the <code>last<\/code> pointer to indicate whether the event is signaled: Once the event is signaled, we will never append any nodes, so we will set <code>last<\/code> to <code>nullptr<\/code> to indicate that the event is signaled.<\/p>\n<p>The <code>last<\/code> member is a <code>std::atomic<\/code> because we access it outside the lock while another thread is potentially mutating it. The default accessors for <code>std::atomic<\/code> use <code>std::memory_<wbr \/>order_<wbr \/>seq_<wbr \/>cst<\/code>, but we want <code>std::memory_<wbr \/>order_<wbr \/>relaxed<\/code>, since we don&#8217;t really mind the data race, as long as the read isn&#8217;t torn. To avoid having to write out <code>std::memory_<wbr \/>order_<wbr \/>relaxed<\/code> all the time, I&#8217;ll introduce a <code>relaxed_<wbr \/>atomic<\/code> helper class.<\/p>\n<pre><span style=\"color: blue;\">\/\/ new\r\ntemplate&lt;typename T&gt;\r\nstruct relaxed_atomic : std::atomic&lt;T&gt;\r\n{\r\n  using atomic = std::atomic&lt;T&gt;;\r\n  using atomic::atomic;\r\n  using atomic::load;\r\n  using atomic::store;\r\n\r\n  T load() const noexcept\r\n  { return atomic::load(std::memory_order_relaxed); }\r\n  void store(T value) noexcept \r\n  { atomic::store(value, std::memory_order_relaxed); }\r\n\r\n  operator T() const noexcept { return load(); }\r\n  relaxed_atomic&amp; operator=(T value) noexcept\r\n  { store(value); return *this; }\r\n};<\/span>\r\n\r\nstruct awaitable_event\r\n{\r\n  void set() const\r\n  { shared-&gt;set(); }\r\n\r\n  auto operator co_await() const noexcept\r\n  { return awaiter{ *shared }; }\r\n\r\nprivate:\r\n  struct node\r\n  {\r\n    node* next;\r\n    std::experimental::coroutine_handle&lt;&gt; handle;\r\n  };\r\n\r\n  struct state\r\n  {\r\n    <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">std::atomic_bool signaled =false;<\/span><\/span>\r\n    winrt::slim_mutex mutex;\r\n    node* head = nullptr;\r\n    <span style=\"color: blue;\">relaxed_atomic&lt;node**&gt; last = &amp;head; \/\/ new<\/span>\r\n\r\n    void set()\r\n    {\r\n      node* rest = nullptr;\r\n      {\r\n        auto guard = winrt::slim_lock_guard(mutex);\r\n        <span style=\"color: blue;\">last.store(nullptr, std::memory_order_relaxed); \/\/ new<\/span>\r\n        rest = std::exchange(head, nullptr);\r\n      }\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">while (lifo) {<\/span><\/span>\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">    auto n = lifo;<\/span><\/span>\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">    lifo = std::exchange(n-&gt;next, fifo);<\/span><\/span>\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">    fifo = n;<\/span><\/span>\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">}<\/span><\/span>\r\n      while (rest) {\r\n        auto handle = rest-&gt;handle;\r\n        rest = rest-&gt;next;\r\n        handle();\r\n      }\r\n    }\r\n\r\n    bool await_ready() const noexcept\r\n    {\r\n      return <span style=\"color: blue;\">!last.load(); \/\/ new<\/span>\r\n    }\r\n\r\n    bool await_suspend(node&amp; n) noexcept\r\n    {\r\n      auto guard = winrt::slim_lock_guard(mutex);\r\n      <span style=\"color: blue;\">auto p = last.load();<\/span>\r\n      if (<span style=\"color: blue;\">!p<\/span>) return false;\r\n      <span style=\"color: blue;\">*p = &amp;n;\r\n      last = &amp;n.next;\r\n      n.next = nullptr;<\/span>\r\n      return true;\r\n    }\r\n\r\n    void await_resume() const noexcept { }\r\n  };\r\n\r\n  struct awaiter\r\n  {\r\n    state&amp; s;\r\n    node n;\r\n\r\n    bool await_ready() const noexcept { return s.await_ready(); }\r\n    bool await_suspend(\r\n      std::experimental::coroutine_handle&lt;&gt; handle) noexcept\r\n    {\r\n      n.handle = handle;\r\n      return s.await_suspend(n);\r\n    }\r\n\r\n    void await_resume() const noexcept { return s.await_resume(); }\r\n  };\r\n\r\n  std::shared_ptr&lt;state&gt; shared = std::make_shared&lt;state&gt;();\r\n};\r\n<\/pre>\n<p>There&#8217;s still a lot to say about this implementation (and the other implementations we&#8217;ve been looking at so far). We&#8217;ll take up some of the topics <a title=\"Creating a co_await awaitable signal that can be awaited multiple times, part 6\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210308-00\/?p=104938\"> next time<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Discussion of what we&#8217;ve done so far.<\/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-104934","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Discussion of what we&#8217;ve done so far.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104934","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=104934"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104934\/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=104934"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104934"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104934"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}