{"id":104971,"date":"2021-03-16T07:00:00","date_gmt":"2021-03-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104971"},"modified":"2021-03-16T20:05:21","modified_gmt":"2021-03-17T03:05:21","slug":"20210316-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210316-00\/?p=104971","title":{"rendered":"Creating other types of synchronization objects that can be used with co_await, part 6: The semaphore"},"content":{"rendered":"<p>Our next stop in <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\"> showing off our library for building awaitable synchronization objects<\/a> is the semaphore. This will look very familiar because a semaphore with a maximum token count of 1 is the same thing as an auto-reset event, so we can just extend our auto-reset event implementation to support multiple tokens.<\/p>\n<pre>struct awaitable_semaphore_state :\r\n    async_helpers::awaitable_state&lt;awaitable_semaphore_state&gt;\r\n{\r\n    awaitable_semaphore_state(<span style=\"color: blue;\">unsigned int<\/span> initial)\r\n    : <span style=\"color: blue;\">tokens<\/span>(initial) {}\r\n\r\n    <span style=\"color: blue;\">std::atomic&lt;unsigned int&gt; tokens;\r\n\r\n    static bool transition(\r\n        unsigned int current, unsigned int&amp; future) noexcept\r\n    {\r\n        if (!current) return false;\r\n        future = current - 1;\r\n        return true;\r\n    }<\/span>\r\n\r\n    bool fast_claim(extra_await_data const&amp;) noexcept\r\n    {\r\n        <span style=\"color: blue;\">return calc_claim&lt;true&gt;(tokens);<\/span>\r\n    }\r\n\r\n    bool claim(extra_await_data const&amp;) noexcept\r\n    {\r\n        return calc_claim&lt;false&gt;(<span style=\"color: blue;\">tokens<\/span>);\r\n    }\r\n\r\n    void set(node_list&amp; list) noexcept\r\n    {\r\n        if (!resume_one(list)) {\r\n            <span style=\"color: blue;\">signaled.fetch_add(1, std::memory_order_relaxed);<\/span>\r\n        }\r\n    }\r\n\r\n    <span style=\"color: blue;\">void set_many(node_list&amp; list, unsigned int count) noexcept\r\n    {\r\n        for (; count &amp;&amp; resume_one(list); --count) { }\r\n\r\n        if (count) {\r\n            tokens.fetch_add(count, std::memory_order_relaxed);\r\n        }\r\n    }<\/span>\r\n};\r\n\r\nstruct awaitable_semaphore\r\n    : async_helpers::awaitable_sync_object&lt;awaitable_semaphore_state&gt;\r\n{\r\n    awaitable_semaphore(<span style=\"color: blue;\">unsigned int initial = 0<\/span>) :\r\n        awaitable_sync_object(initial) { }\r\n\r\n    void set() noexcept\r\n    {\r\n        action_impl(&amp;state::set);\r\n    }\r\n\r\n    <span style=\"color: blue;\">void set_many() noexcept\r\n    {\r\n        action_impl(&amp;state::set_many);\r\n    }<\/span>\r\n};\r\n<\/pre>\n<p>This is basically the same as an auto-reset event, except that the object state is a count of tokens instead of a single boolean value. Claiming a token involves decrementing the token count by one, rather than by &#8220;decrementing&#8221; a <code>true<\/code> to a <code>false<\/code>. Similarly, setting the semaphore increments the token count instead of &#8220;incrementing&#8221; the <code>false<\/code> to <code>true<\/code>.<\/p>\n<p>Since semaphores can hold more than one token, we don&#8217;t have a good &#8220;optimistic&#8221; value for the success case, so we just use the one-parameter version of <code>calc_claim<\/code> that uses the atomic variable&#8217;s current value as its starting point.<\/p>\n<p>Semaphores have the extra operation of &#8220;set many&#8221; which lets you set multiple tokens at once. We implement that by resuming as many waiters as we have tokens to resume, and then adding any leftover tokens to the token count for future consumption.<\/p>\n<p>Netx time, we&#8217;ll look at mutexes and recursive mutexes, which are quirky in the world of coroutines.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The asynchronous semaphore.<\/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-104971","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The asynchronous semaphore.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104971","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=104971"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104971\/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=104971"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104971"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}