{"id":108252,"date":"2023-05-26T07:00:00","date_gmt":"2023-05-26T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108252"},"modified":"2023-05-26T06:52:07","modified_gmt":"2023-05-26T13:52:07","slug":"20230526-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230526-00\/?p=108252","title":{"rendered":"Getting a strong reference from the <CODE>this<\/CODE> pointer too late"},"content":{"rendered":"<p>It is a standard pattern for functions that are coroutines to promote the <code>this<\/code> pointer to a strong reference (either a COM strong reference or a <code>shared_ptr<\/code>), so that the object won&#8217;t be destructed while the coroutine is suspended. But it might be too late.<\/p>\n<p>Consider the following example:<\/p>\n<pre>struct MyObject : winrt::implements&lt;MyObject, winrt::IInspectable&gt;\r\n{\r\n  MyObject() = default;\r\n  ~MyObject() = default;\r\n\r\n  winrt::Widget::Closed_revoker m_revoker;\r\n\r\n  void RegisterForWidgetEvents(Widget const&amp; widget)\r\n  {\r\n      m_revoker = widget.Closed(winrt::auto_revoke,\r\n        { this, &amp;MyObject::OnWidgetClosed });\r\n  }\r\n\r\n  winrt::fire_and_forget OnWidgetClosed(Widget const&amp; sender, winrt::IInspectable const&amp;)\r\n  {\r\n    auto lifetime = get_strong();\r\n\r\n    co_await DoStuffAsync();\r\n    co_await DoMoreStuffAsync();\r\n  }\r\n};\r\n<\/pre>\n<p>The idea here is that we register for the Widget&#8217;s <code>Closed<\/code> event with a raw pointer. When the event is raised, the handle immediately promotes the raw pointer to a strong reference, so that the <code>MyObject<\/code> does not destruct during the two asynchronous calls that follow.<\/p>\n<p>But there&#8217;s still a race condition:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: 1px currentColor; border-style: none solid solid none;\">Thread 1<\/td>\n<td style=\"border: 1px currentColor; border-style: none none solid solid;\">Thread 2<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td>Widget closes<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">Last reference released<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td><code>Widget::OnWidgetClosed<\/code> begins<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">Destruction begins<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\"><code>m_revoker<\/code> unregisters handler<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td><code>get_strong()<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If the last reference is released before the <code>Widget::<wbr \/>On\u00adWidget\u00adClosed<\/code> method reaches the <code>get_strong()<\/code>, then the <code>get_strong()<\/code> method runs against an object that has already started destructing. It will nevertheless produce a strong reference and increment the reference count, but that reference count does not have the power of time travel. The destructor is already running; you incremented the reference count too late. The result is <a title=\"The case of the crash in a C++\/WinRT coroutine: Unpeeling the onion\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230505-00\/?p=108146\"> a mysterious crash<\/a>.<\/p>\n<p>A similar problem exists with <code>std::shared_ptr<\/code>:<\/p>\n<pre>struct MyObject : <span style=\"border: solid 1px currentcolor;\">std::enable_shared_from_this&lt;MyObject&gt;<\/span>\r\n{\r\n  MyObject() = default;\r\n  ~MyObject() = default;\r\n\r\n  winrt::Widget::Closed_revoker m_revoker;\r\n\r\n  void RegisterForWidgetEvents(Widget const&amp; widget)\r\n  {\r\n      m_revoker = widget.Closed(winrt::auto_revoke,\r\n        { this, &amp;MyObject::OnWidgetClosed });\r\n  }\r\n\r\n  winrt::fire_and_forget OnWidgetClosed(Widget const&amp; sender, winrt::IInspectable const&amp;)\r\n  {\r\n    auto lifetime = <span style=\"border: solid 1px currentcolor;\">shared_from_this()<\/span>;\r\n\r\n    co_await DoStuffAsync();\r\n    co_await DoMoreStuffAsync();\r\n  }\r\n};\r\n<\/pre>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: 1px currentColor; border-style: none solid solid none;\">Thread 1<\/td>\n<td style=\"border: 1px currentColor; border-style: none none solid solid;\">Thread 2<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td>Widget closes<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">Last reference released<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td><code>Widget::OnWidgetClosed<\/code> begins<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">Destruction begins<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\"><code>m_revoker<\/code> unregisters handler<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td><code>shared_from_this()<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The call to <code>shared_from_this()<\/code> throws <code>std::<wbr \/>bad_<wbr \/>weak_<wbr \/>ptr<\/code> because the weak pointer cannot be converted to a <code>shared_ptr<\/code>.<\/p>\n<p>In both cases, the problem is that the <code>On\u00adWidget\u00adClosed<\/code> callback is registered with a raw pointer. Instead, use a weak pointer and try to promote it to a strong pointer in the callback.<\/p>\n<pre>  \/\/ C++\/WinRT\r\n  void RegisterForWidgetEvents(Widget const&amp; widget)\r\n  {\r\n    m_revoker = widget.Closed(winrt::auto_revoke,\r\n      [weak = get_weak()](auto&amp;&amp; sender, auto&amp;&amp; args)\r\n      {\r\n        if (auto strong = weak.get()) {\r\n          strong-&gt;OnWidgetClosed(sender, args);\r\n        }\r\n      });\r\n  }\r\n\r\n  \/\/ C++\/WinRT alternate version\r\n  void RegisterForWidgetEvents(Widget const&amp; widget)\r\n  {\r\n    m_revoker = widget.Closed(winrt::auto_revoke,\r\n      { get_weak(), &amp;MyObject::OnWidgetClosed });\r\n  }\r\n\r\n  \/\/ C++ standard library\r\n  void RegisterForWidgetEvents(Widget const&amp; widget)\r\n  {\r\n    m_revoker = widget.Closed(winrt::auto_revoke,\r\n      [weak = weak_from_this()](auto&amp;&amp; sender, auto&amp;&amp; args)\r\n      {\r\n        if (auto strong = weak.lock()) {\r\n          strong-&gt;OnWidgetClosed(sender, args);\r\n        }\r\n      });\r\n  }\r\n<\/pre>\n<p>C++\/WinRT provides a helper constructor that does the <code>auto strong = weak.get()<\/code> thing automatically.<\/p>\n<p>Since weak pointers will not promote to strong\/shared pointers once the last strong\/shared reference is destructed, you don&#8217;t have the race condition where the callback tries to do something with an object that has begun destructing.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: 1px currentColor; border-style: none solid solid none;\">Thread 1<\/td>\n<td style=\"border: 1px currentColor; border-style: none none solid solid;\">Thread 2<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td>Widget closes<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">Last reference released<br \/>\n(weak pointers are now expired)<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td><code>Widget::OnWidgetClosed<\/code> begins<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">Destruction begins<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\"><code>m_revoker<\/code> unregisters handler<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentColor;\">\u00a0<\/td>\n<td><code>weak.get()<\/code> fails<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>Once destruction begins, strong references mean 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-108252","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Once destruction begins, strong references mean nothing.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108252","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=108252"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108252\/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=108252"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108252"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}