{"id":105047,"date":"2021-04-02T07:00:00","date_gmt":"2021-04-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105047"},"modified":"2021-04-02T08:13:29","modified_gmt":"2021-04-02T15:13:29","slug":"20210402-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210402-00\/?p=105047","title":{"rendered":"C++ coroutines: Making the promise itself be the shared state, the inspiration"},"content":{"rendered":"<p>Earlier, we <a title=\"C++ coroutines: The initial and final suspend, and improving our return_value method\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210331-00\/?p=105028\"> improved our simple coroutine promise by delaying the resumption of awaiting coroutines until local variables have destructed<\/a>. This time, we&#8217;ll look at another improvement.<\/p>\n<p>Recall that our coroutine is structured like this:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<th>Coroutine state<\/th>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<th>Caller<\/th>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">bookkeeping<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>promise<\/td>\n<td style=\"margin-right: 1ex;\">\n<div style=\"border: solid 1px black; border-right: none;\">\u00a0<\/div>\n<\/td>\n<td style=\"border: solid 1px black; border-top: none;\"><code>holder<\/code><\/td>\n<td>\u2192<\/td>\n<td style=\"border: solid 1px black;\"><code>result_<wbr \/>holder<\/code> state<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black;\"><code>holder<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">stack frame<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>There are two allocations, one for the coroutine state, and one for the shared state internal to the <code>result_<wbr \/>holder<\/code>. But what if we put the <code>result_<wbr \/>holder<\/code> shared state inside the promise? In other words, what if we made the promise <i>be<\/i> the <code>result_<wbr \/>holder<\/code> shared state?\u00b9<\/p>\n<p>This trick takes advantage of the fact that you are permitted to suspend in the <code>final_<wbr \/>suspend<\/code>. This lets you pause the coroutine execution before it gets to the point where it destroys the coroutine state.<\/p>\n<p>The idea is that we move into the promise object all of the <code>result_<wbr \/>holder<\/code> shared state, including the reference count hiding inside the <code>shared_ptr<\/code>.<\/p>\n<p>Let&#8217;s make the original diagram a bit more honest about the shared pointer control block. Recall that a <code>shared_ptr<\/code> is a pair of pointers, one to a control block and one to the shared data, and the control block consists of two reference counts, one for strong references and one for weak references.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<th>Coroutine state<\/th>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<th>Caller<\/th>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">bookkeeping<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>promise<\/td>\n<td style=\"margin-right: 1ex;\">\n<div style=\"border: solid 1px black; border-right: none;\">\u00a0<br \/>\n\u00a0<\/div>\n<\/td>\n<td style=\"border: solid 1px black; border-top: none;\"><code>holder<\/code><\/td>\n<td>\u2192<br \/>\n\u2192<\/td>\n<td style=\"border: solid 1px black;\">refcounts<br \/>\n<code>result_<wbr \/>holder<\/code> state<\/td>\n<td>\u2190<br \/>\n\u2190<\/td>\n<td style=\"border: solid 1px black;\"><code>holder<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">stack frame<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>What we&#8217;re doing is moving the shared pointer control block and the shared state into the promise.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<th>Coroutine state<\/th>\n<td>&nbsp;<\/td>\n<th>Caller<\/th>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">bookkeeping<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td rowspan=\"2\">promise<\/td>\n<td style=\"margin-right: 1ex;\" rowspan=\"2\">\n<div style=\"border: solid 1px black; border-right: none;\">\u00a0<br \/>\n\u00a0<\/div>\n<\/td>\n<td style=\"border: 1px black; border-style: none solid;\">refcount<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black;\"><code>holder<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-top: none;\"><code>result_<wbr \/>holder<\/code> state<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">stack frame<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We don&#8217;t need to support weak references at all, so we are down to just one reference count.<\/p>\n<p>A running coroutine has a reference to its own state, and any outstanding <code>holder<\/code> objects also have a reference to the coroutine state. Only when all references go away do we destroy the coroutine state.<\/p>\n<p>We&#8217;re going to have to rewrite a bunch of stuff basically from scratch, seeing as we&#8217;re abandoning the entire <code>shared_ptr<\/code> model that we had been using up until now. Let&#8217;s hope it&#8217;s worth it.<\/p>\n<p><b>Bonus chatter<\/b>: I figured I&#8217;d do the whole <code>shared_ptr<\/code> thing first, since it makes the several-week-long path to this point easier to follow. If I had started directly with the &#8220;result holder state embedded in the coroutine state&#8221;, it would probably have been too confusing.<\/p>\n<p>\u00b9 Thanks to Gor Nishanov for providing this inspiration.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Just be it.<\/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-105047","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Just be it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105047","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=105047"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105047\/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=105047"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105047"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105047"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}