{"id":108831,"date":"2023-09-27T07:00:00","date_gmt":"2023-09-27T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108831"},"modified":"2023-09-27T06:51:53","modified_gmt":"2023-09-27T13:51:53","slug":"20230927-00-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230927-00\/?p=108831","title":{"rendered":"The dangers of releasing the last strong reference from within its own callback"},"content":{"rendered":"<p>A common callback pattern is that unregistering a callback will wait until any outstanding callbacks have completed. This avoids the problem of freeing the data out from under a running callback.<\/p>\n<table style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border: 1px currentcolor; border-style: none solid solid none;\">Thread 1<\/th>\n<th style=\"border: 1px currentcolor; border-style: none none solid solid;\">Thread 2<\/th>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\"><code>RegisterCallback(callback, this);<\/code><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td><code>callback<\/code> begins<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Destructor begins<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\"><code>UnregisterCallback(callback)<\/code><\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Destructor completes<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td><code>callback<\/code> returns<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the above example, the <code>Unregister\u00adCallback<\/code> doesn&#8217;t wait for the outstanding callback to complete, and as a result, the object is destructed while there is code still using it.<\/p>\n<p>Forcing <code>Unregister\u00adCallback<\/code> to wait for outstanding callbacks to complete avoids this problem:<\/p>\n<table style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border: 1px currentcolor; border-style: none solid solid none;\">Thread 1<\/th>\n<th style=\"border: 1px currentcolor; border-style: none none solid solid;\">Thread 2<\/th>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\"><code>RegisterCallback(callback, this);<\/code><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td><code>callback<\/code> begins<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Destructor begins<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\"><code>UnregisterCallback(callback)<\/code><\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u22ee waiting for callback to finish<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u22ee waiting for callback to finish<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u22ee waiting for callback to finish<\/td>\n<td><code>callback<\/code> returns<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\"><code>UnregisterCallback(callback)<\/code> returns<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Destructor completes<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This pattern avoids a use-after-free problem, but it creates a new one: Deadlock.<\/p>\n<p>If your callback takes a strong reference to the containing object, then when that strong reference is destructed at the end of the callback, that may end up destructing the last strong pointer to the object, causing the object itself to destruct from inside the callback. That destructor will wait for the callback to complete, and we have deadlocked.<\/p>\n<table style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border: 1px currentcolor; border-style: none solid solid none;\">Thread 1<\/th>\n<th style=\"border: 1px currentcolor; border-style: none solid solid solid;\">Thread 2<\/th>\n<th style=\"border: 1px currentcolor; border-style: none none solid solid;\">Reference count<\/th>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Constructor<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u22ee<code>RegisterCallback(callback, this);<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">1<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Retain strong reference<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">1<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td><code>callback<\/code> begins<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td style=\"border-right: solid 1px currentcolor;\">\u22ee create strong reference to self<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">Release strong reference<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<td style=\"border-left: solid 1px currentcolor;\">1<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee do stuff with <code>this<\/code><\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee destruct local strong reference<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee\u22eeDestructor runs<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px currentcolor;\">\u00a0<\/td>\n<td>\u22ee\u22ee<code>UnregisterCallback(callback)<\/code> hangs<\/td>\n<td style=\"border-left: solid 1px currentcolor;\">\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The call to <code>UnregisterCallback<\/code> hangs because it is waiting for the callback to complete, but the callback is itself waiting for the destructor to finish, and the destructor is stuck in the <code>UnregisterCallback<\/code>.<\/p>\n<p>For thread pool callbacks, <a title=\"Avoiding deadlocks when cancelling a thread pool callback, part 1: External callback data\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180503-00\/?p=98665\"> we could use <code>Disassociate\u00adCurrent\u00adThread\u00adFrom\u00adCallback<\/code><\/a> to break the deadlock. But other types of callbacks may not have a way to say, &#8220;Act as if I returned (even though I haven&#8217;t yet).&#8221;<\/p>\n<p>One solution is to forbid taking a strong reference to the containing object from the callback.<\/p>\n<pre>void MyObject::callback(CallbackData* data, void* context)\r\n{\r\n    auto self = reinterpret_cast&lt;MyObject*&gt;(context);\r\n\r\n    \/\/ Calling self-&gt;get_strong() or self-&gt;shared_from_this()\r\n    \/\/ is forbidden! Or in fact anything else that might result in\r\n    \/\/ a strong reference to MyObject.\r\n    \/\/\r\n    \/\/ Also, cannot access any members that are initialized after\r\n    \/\/ the RegisterCallback call or cleaned up before the\r\n    \/\/ UnregisterCallback call.\r\n\r\n    \u27e6 do stuff with \"self\" \u27e7\r\n}\r\n<\/pre>\n<p>In addition to forbidding strong references, we must also make sure not to access any members which are initialized in the constructor after the call to <code>Register\u00adCallback<\/code> or cleaned up in the destructor prior to the <code>Unregister\u00adCallback<\/code> call. In the common case that the callback is managed by an RAII type, this means that you cannot access any members which are declared after the RAII type or manually initialized in the constructor body; nor can you access any members which are cleaned up explicitly in the destructor, or declared after the RAII type. (The &#8220;declared after the RAII type&#8221; restriction comes from the rule that C++ members are initialized in order of declaration in the class definition, and destructed in reverse order.)<\/p>\n<p>For example, suppose <code>callback_<wbr \/>holder<\/code> is an RAII type that calls <code>Unregister\u00adCallback<\/code> at destruction.<\/p>\n<pre>struct MyObject \r\n{\r\n    std::string m_value1;\r\n    Widget m_widget;\r\n    callback_holder m_callback(&amp;MyObject::callback, this);\r\n    std::string m_value2;\r\n\r\n    MyObject()\r\n    {\r\n        m_widget.prime();\r\n    }\r\n\r\n    ~MyObject()\r\n    {\r\n        m_widget.disable();\r\n    }\r\n\r\n    static void callback(CallbackData* data, void* context);\r\n};\r\n<\/pre>\n<p>Given the above class, the <code>callback<\/code> cannot access <code>m_value2<\/code>, since it is constructed after <code>m_callback<\/code> and destructed before it. It also has to be prepared for running before <code>m_widget<\/code> has been primed or running after <code>m_widget<\/code> has been disabled.<\/p>\n<p>The fact that the callback is still potentially active before the constructor finishes and after the destructor starts means that the usual rule of thumb that &#8220;there won&#8217;t be any conflicting threads during construction or destruction&#8221; does not apply, since there may be an active callback that is racing the constructor or destructor.<\/p>\n<p>You can simplify the rules by explicitly registering the callback at the end of the constructor and unregistering it at the start of the destructor:<\/p>\n<pre>    MyObject()\r\n    {\r\n        m_widget.prime();\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Do this last: Don't register for callbacks  <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ until all members are ready.                <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">m_callback.register(&amp;MyObject::callback, this);<\/span>\r\n    }\r\n\r\n    ~MyObject()\r\n    {\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Do this first: Ensure all outstanding<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ callbacks have completed.            <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">m_callback.reset();                     <\/span>\r\n        m_widget.disable();\r\n    }\r\n<\/pre>\n<p>Now, that&#8217;s a lot of rules to remember in the callback. Furthermore, enforcing these rules may be difficult if the &#8220;do stuff with <code>self<\/code>&#8221; is complex and calls other methods: Those other methods now need to know the rules about forbidden strong references and unsafe member variables, and these are the sorts of rules that are easy to break by accident and hard to detect via static analysis.<\/p>\n<p>Another option is to queue further work and return from the callback immediately. Give that future work a <i>weak<\/i> reference, with the promise not to resolve the weak reference to a strong reference until after you have arrived on the other thread.<\/p>\n<pre>void MyObject::Callback(CallbackData* data, void* context)\r\n{\r\n    auto self = reinterpret_cast&lt;MyObject*&gt;(context);\r\n\r\n    [](auto weak, auto important) -&gt; fire_and_forget {\r\n        co_await resume_background();\r\n        if (auto strong = weak.get()) {\r\n            \u27e6 do stuff with \"strong\" \u27e7\r\n        }\r\n    }(get_weak(), data-&gt;important);\r\n}\r\n<\/pre>\n<p>The data we carry to the background thread are a <i>weak<\/i> reference to ourselves, as well as any event payload that we need. (We probably can&#8217;t capture the raw pointer <code>data<\/code> since that would result in using the <code>data<\/code> pointer after the callback returns.) Only after safely arriving on the background thread do we try to promote the weak reference to a strong reference, and we bail out if the object has already begun destruction.<\/p>\n<p>Another solution is to break the deadlock by forcing the destructor to run outside of the callback: In C++\/WinRT, you can <a title=\"Extension points for your implementation types: Deferred destruction\" href=\"https:\/\/learn.microsoft.com\/windows\/uwp\/cpp-and-winrt-apis\/details-about-destructors#deferred-destruction\"> declare your class with a <code>final_<wbr \/>release<\/code><\/a> and move the destruction to another thread.<\/p>\n<pre>struct MyObject : implements&lt;MyObject, IInspectable&gt;\r\n{\r\n    static fire_and_forget final_release(std::unique_ptr&lt;MyObject&gt; ptr)\r\n    {\r\n        \/\/ Queue continuation on a background thread\r\n        co_await resume_background();\r\n\r\n        \/\/ allow ptr to destruct on a background thread\r\n    }\r\n};\r\n<\/pre>\n<p>This is probably the simplest solution, assuming you have it available as an option, since it allows the rest of the class to write code &#8220;naturally&#8221; and not have to worry about all the weird rules.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Deadlocking with yourself.<\/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-108831","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Deadlocking with yourself.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108831","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=108831"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108831\/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=108831"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108831"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108831"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}