{"id":104981,"date":"2021-03-22T07:00:00","date_gmt":"2021-03-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104981"},"modified":"2021-04-08T07:04:04","modified_gmt":"2021-04-08T14:04:04","slug":"20210322-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210322-00\/?p=104981","title":{"rendered":"Creating other types of synchronization objects that can be used with co_await, part 10: Wait for an event to clear"},"content":{"rendered":"<p>We&#8217;ve been looking at <a title=\"Creating other types of synchronization objects that can be used with co_await, part 3: Parallel resumption\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210311-00\/?p=104949\"> creating different types of awaitable synchronization objects<\/a>. This time, we&#8217;re going to create something that doesn&#8217;t exist in the normal Win32 repertoire: An event where you can wait for the event to be in a desired state, either set or reset. Normal Win32 events allow you to wait for them to be set, but <a title=\"Can I wait for a kernel event to become unsignaled?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200305-00\/?p=103535\"> you cannot wait for Win32 event to be reset<\/a>. The usual workaround is to have two events.<\/p>\n<p>Let&#8217;s go.<\/p>\n<pre>struct awaitable_signal_state;\r\n\r\ntemplate&lt;&gt;\r\nstruct async_helpers::\r\n    extra_await_data&lt;awaitable_signal_state&gt;\r\n{\r\n    extra_await_data(bool value = true) : desired(value) {}\r\n    bool desired;\r\n};\r\n<\/pre>\n<p>If you don&#8217;t say whether you are awaiting the signal to be set or reset, we will assume set.<\/p>\n<pre>struct awaitable_signal_state :\r\n    async_helpers::awaitable_state&lt;awaitable_signal_state&gt;\r\n{\r\n    awaitable_signal_state(bool initial)\r\n    : signaled(initial) {}\r\n\r\n    std::atomic&lt;bool&gt; signaled;\r\n\r\n    bool fast_claim(extra_await_data const&amp; e) noexcept\r\n    {\r\n        return signaled.load(std::memory_order_acquire)\r\n            == e.desired;\r\n    }\r\n\r\n    bool claim(extra_await_data const&amp; e) noexcept\r\n    {\r\n        return signaled.load(std::memory_order_relaxed)\r\n            == e.desired;\r\n    }\r\n\r\n    void set_state(node_list&amp; list, bool value) noexcept\r\n    {\r\n        if (signaled.load(std::memory_order_relaxed)\r\n            != value) {\r\n            signaled.store(value, std::memory_order_relaxed);\r\n            resume_all(list);\r\n        }\r\n    }\r\n};\r\n<\/pre>\n<p>If the signal is in the desired state, then we allow the await to complete immediately. Otherwise, we suspend the coroutine.<\/p>\n<p>If the state changes, we resume everybody who was waiting for the state to change.<\/p>\n<pre>struct awaitable_signal\r\n    : async_helpers::awaitable_sync_object&lt;awaitable_signal_state&gt;\r\n{\r\n    awaitable_signal(bool initial = false) :\r\n        awaitable_sync_object(initial) { }\r\n\r\n    auto when_state(bool desired)\r\n    { return make_awaiter(desired); }\r\n\r\n    auto when_set() { return when_state(true); }\r\n    auto when_reset() { return when_state(false); }\r\n\r\n    void set_state(bool desired) noexcept\r\n    {\r\n        action_impl(&amp;state::set_state, desired);\r\n    }\r\n\r\n    void set() noexcept\r\n    {\r\n        action_impl(&amp;state::set_state, true);\r\n    }\r\n\r\n    void reset() noexcept\r\n    {\r\n        action_impl(&amp;state::set_state, false);\r\n    }\r\n};\r\n<\/pre>\n<p>The main class supports a plain <code>co_await<\/code> which defaults to waiting for the signal to be set. You can <code>co_await signal.when_set()<\/code> to wait for it to be set, or <code>co_await signal.when_reset()<\/code> to wait for it to be reset, or <code>co_await signal.when_state()<\/code> to wait for a specific state.<\/p>\n<p>All of this playing around with awaitable synchronization objects is really just warm-up for the case of an awaitable object that produces a result. We&#8217;ll take that up next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Something new.<\/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-104981","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Something new.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104981","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=104981"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104981\/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=104981"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104981"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104981"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}