{"id":111040,"date":"2025-04-02T07:00:00","date_gmt":"2025-04-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111040"},"modified":"2025-04-02T09:14:26","modified_gmt":"2025-04-02T16:14:26","slug":"20250402-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250402-00\/?p=111040","title":{"rendered":"Adding delays to our task sequencer, part 1"},"content":{"rendered":"<p>Suppose you want to use the <a title=\"Serializing asynchronous operations in C++\/WinRT\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220915-00\/?p=107182\"> <code>task_<wbr \/>sequencer<\/code> class<\/a> we created a while back (and <a title=\"Fixing exception safety in our task_sequencer\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250328-00\/?p=111016\"> which we fixed not too long ago<\/a>), but you also want to implement a rudimentary form of throttling, so that tasks run at a specified maximum rate.<\/p>\n<p>Suppose for concreteness that you want to have a 1-second cooling off period before the next task runs. How would we add this to our <code>task_<wbr \/>sequencer<\/code>?<\/p>\n<p>Well, the thing that kicks off the next task is the <code>completer<\/code>, which calls <code>complete()<\/code> on the chained task to trigger the start of the next task. All we have to do is delay that completion. For that, we can use <code>fire_and_forget<\/code>.<\/p>\n<pre>struct task_sequencer\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    struct completer\r\n    {\r\n        ~completer()\r\n        {\r\n            <span style=\"border: solid 1px currentcolor;\">complete_later(std::move(chain));<\/span>\r\n        }\r\n        std::shared_ptr&lt;chained_task&gt; chain;\r\n\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">static fire_and_forget complete_later(  <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    std::shared_ptr&lt;chained_task&gt; chain)<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                       <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    co_await winrt::resume_after(1s);   <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    chain-&gt;complete();                  <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                       <\/span>\r\n    };\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>Instead of calling <code>chain-&gt;<wbr \/>complete()<\/code> immediately from the destructor, we kick off a coroutine that calls it after waiting one second.<\/p>\n<p>This coroutine is simple enough you might find it easier to inline it, so that all the logic is in one place.<\/p>\n<pre>struct task_sequencer\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    struct completer\r\n    {\r\n        ~completer()\r\n        {\r\n            <span style=\"border: solid 1px currentcolor; border-bottom: none;\">[](auto chain) -&gt; winrt::fire_and_forget {<\/span>\r\n            <span style=\"border: 1px currentcolor; border-style: none solid;\">    co_await winrt::resume_after(1s);     <\/span>\r\n            <span style=\"border: 1px currentcolor; border-style: none solid;\">    chain-&gt;complete();                    <\/span>\r\n            <span style=\"border: solid 1px currentcolor; border-top: none;\">}(std::move(chain));                      <\/span>\r\n        }\r\n        std::shared_ptr&lt;chained_task&gt; chain;\r\n    };\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>Maybe instead of waiting one second between the completion of one operation and the start of the next, you want to wait one second between the <i>start<\/i> of one operation and the start of the next. We&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Not so fast there.<\/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-111040","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Not so fast there.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111040","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=111040"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111040\/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=111040"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111040"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111040"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}