{"id":108833,"date":"2023-09-28T07:00:53","date_gmt":"2023-09-28T14:00:53","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108833"},"modified":"2023-09-28T08:50:43","modified_gmt":"2023-09-28T15:50:43","slug":"c-winrt-gotcha-get_strong-will-produce-a-broken-strong-reference-if-destruction-has-already-begun","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230928-53\/?p=108833","title":{"rendered":"C++\/WinRT gotcha: <CODE>get_strong()<\/CODE> will produce a broken strong reference if destruction has already begun"},"content":{"rendered":"<p>One of the nice properties of C++ and C++\/WinRT weak references is that their expiration coincides with the destruction of the last strong reference. You don&#8217;t have race conditions where one thread manages to promote a weak reference just after the last strong reference is destroyed.<\/p>\n<p>C++ has <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> which lets you obtain a strong reference to the current object. If the current object has started destruction, then it throws <code>std::<wbr \/>bad_<wbr \/>weak_ptr<\/code>. If you don&#8217;t want an exception if the object has started destruction, you can do <code>weak_<wbr \/>from_<wbr \/>this().lock()<\/code>, which produces an empty <code>shared_ptr<\/code> if destruction has already begun.<\/p>\n<p>For C++\/WinRT, the corresponding <code>get_strong()<\/code> function does <i>not<\/i> check whether destruction has already begun. This means that if you call <code>get_strong()<\/code> after the object has begun destruction, you will get a <code>com_ptr<\/code> that holds a pointer to an object that is being destructed. You think you have extended its lifetime, but you didn&#8217;t. The object has already started destructing; you can&#8217;t &#8220;roll back&#8221; the destructor. Even worse, after the destruction has completed, the resulting strong reference is now a dangling pointer, and when it destructs, it&#8217;s going to try to decrement the reference count of already-freed memory. This is the sort of memory corruption bug that keeps you up at night.<\/p>\n<p>Suppose we have this C++\/WinRT implementation class:<\/p>\n<pre>using unique_power_setting_register =\r\n    unique_any&lt;HPOWERNOTIFY,\r\n                  decltype(&amp;::PowerSettingUnregisterNotification),\r\n                            ::PowerSettingUnregisterNotification&gt;;\r\n wil::\r\nstruct Widget : winrt::implements&lt;Widget, \u27e6 other interfaces \u27e7&gt;\r\n{\r\n    unique_power_setting_register m_notify = subscribe();\r\n\r\n    unique_power_setting_register subscribe()\r\n    {\r\n        unique_power_setting_register notify;\r\n        DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS params;\r\n        params.Callback = &amp;Widget::OnNotify;\r\n        params.Context = this;\r\n        THROW_IF_WIN32_ERROR(\r\n            PowerSettingRegisterNotification(\r\n                &amp;GUID_ACDC_POWER_SOURCE,\r\n                DEVICE_NOTIFY_CALLBACK,\r\n                &amp;params,\r\n                &amp;notify));\r\n        return notify;\r\n    }\r\n\r\n    static ULONG CALLBACK OnNotify(\r\n        void* context,\r\n        ULONG type,\r\n        void* setting)\r\n    {\r\n        Widget* self = static_cast&lt;Widget*&gt;(context);\r\n\r\n        \/\/ Code in italics is wrong\r\n        <i>auto strongThis = self-&gt;get_strong();<\/i>\r\n\r\n        QueueWorkItem([this, strongThis] {\r\n            \u27e6 process the notification \u27e7\r\n        });\r\n\r\n        return NOERROR;\r\n    }\r\n};\r\n<\/pre>\n<p>This class registers for suspend\/resume notifications upon construction, and the <code>unique_<wbr \/>power_<wbr \/>setting_<wbr \/>register<\/code> destructor unregisters at destruction. In the notification callback, we extend the lifetime of the <code>Widget<\/code> so that we can continue processing the notification asynchronously.<\/p>\n<p>Unfortunately, we run into the problem that <code>get_strong()<\/code> doesn&#8217;t extend the lifetime of an object that has started destruction. Instead, we can take inspiration from <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> and keep a <code>weak_ref<\/code> to ourselves.<\/p>\n<pre>struct Widget : winrt::implements&lt;Widget, \u27e6 other interfaces \u27e7&gt;\r\n{\r\n    <span style=\"border: solid 1px currentcolor;\">winrt::weak_ref&lt;Widget&gt; m_weakThis = get_weak();<\/span>\r\n    unique_power_setting_register m_notify = subscribe();\r\n\r\n    unique_power_setting_register subscribe()\r\n    {\r\n        unique_power_setting_register notify;\r\n        DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS params;\r\n        params.Callback = &amp;Widget::OnNotify;\r\n        params.Context = this;\r\n        THROW_IF_WIN32_ERROR(\r\n            PowerSettingRegisterNotification(\r\n                &amp;GUID_ACDC_POWER_SOURCE,\r\n                DEVICE_NOTIFY_CALLBACK,\r\n                &amp;params,\r\n                &amp;notify));\r\n        return notify;\r\n    }\r\n\r\n    static ULONG CALLBACK OnNotify(\r\n        void* context,\r\n        ULONG type,\r\n        void* setting)\r\n    {\r\n        Widget* self = static_cast&lt;Widget*&gt;(context);\r\n\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ WARNING! This code is still wrong!          <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\"><i>if (auto strongThis = self-&gt;m_weakThis.get()) {<\/i><\/span>\r\n            QueueWorkItem([this, strongThis] {\r\n                \u27e6 process the notification \u27e7\r\n            });\r\n        <span style=\"border: solid 1px currentcolor;\">}<\/span>\r\n\r\n        return NOERROR;\r\n    }\r\n};\r\n<\/pre>\n<p>When the callback happens, we try to promote the weak reference to a strong reference. Unlike <code>get_strong()<\/code> which always says &#8220;Sure!&#8221;, the <code>weak_ref::get()<\/code> method will fail if destruction has begun, in which case we just return immediately without doing any work.<\/p>\n<p>There are a number of subtleties in this code.<\/p>\n<p>First is that the <code>Power\u00adSetting\u00adUnregister\u00adNotification<\/code> function will wait for outstanding callbacks to complete before returning. This avoids a race condition where the callback is at the open-brace of <code>OnNotify<\/code> at the time the destruction occurs. If <code>Power\u00adSetting\u00adUnregister\u00adNotification<\/code> did not wait, then the callback could proceed with an already-destructed <code>Widget<\/code>, and that&#8217;s not going to end well.<\/p>\n<p>Second is that we declare the <code>m_weakThis<\/code> before we declare the <code>m_notify<\/code>. This ensures that the weak reference remains valid as long as the callback is registered, so that the callback can safely read it. If the members had been declared in the other order, then the weak reference would destruct before we unregister the callback, opening a race window where the callback runs and tries to use a destructed object.<\/p>\n<p>Basically, we reinvented <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> for C++\/WinRT.<\/p>\n<p>But wait, we still have a bug!<\/p>\n<p>We have the problem that we described last time: <a title=\"The dangers of releasing the last strong reference from within its own callback\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230927-00\/?p=108831\"> Creating a strong reference from inside a callback whose cleanup blocks on the callback<\/a>. The problem now is that some of the recommended solutions contradict our requirements here! For example, one recommendation is to avoid taking any strong references in the callback. But without a strong reference, how can we queue a work item to do processing later?<\/p>\n<p>One solution is to queue the work item with a weak reference, and resolve the weak reference to a strong one in the work item rather than doing so from the callback.<\/p>\n<pre>    static ULONG CALLBACK OnNotify(\r\n        void* context,\r\n        ULONG type,\r\n        void* setting)\r\n    {\r\n        Widget* self = static_cast&lt;Widget*&gt;(context);\r\n\r\n        QueueWorkItem([this, <span style=\"border: solid 1px currentcolor;\">weakThis = self-&gt;m_weakThis<\/span>] {\r\n            <span style=\"border: solid 1px currentcolor;\">if (auto strongThis = weakThis.get()) {<\/span>\r\n                \u27e6 process the notification \u27e7\r\n            <span style=\"border: solid 1px currentcolor;\">}<\/span>\r\n        });\r\n\r\n        return NOERROR;\r\n    }\r\n<\/pre>\n<p>As noted last time, enforcing the &#8220;no creation of strong references&#8221; rule can be difficult, so the best solution is probably to use <code>final_release<\/code> as described in that article. This lets you write callback code in the natural way with no special constraints.<\/p>\n<p>Next time, we&#8217;ll look at some complexity in C++\/WinRT that made it difficult to allow class to hold a weak reference to itself.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A strong reference to nothing.<\/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-108833","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A strong reference to nothing.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108833","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=108833"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108833\/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=108833"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108833"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108833"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}