{"id":101084,"date":"2019-03-06T07:00:00","date_gmt":"2019-03-06T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=101084"},"modified":"2019-06-06T17:30:15","modified_gmt":"2019-06-07T00:30:15","slug":"20190306-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190306-00\/?p=101084","title":{"rendered":"How do I destruct an object that keeps a reference to itself, because that reference prevents the object from being destructed"},"content":{"rendered":"<p>Consider the case of an ordered work queue. Components can queue work to the worker thread, with the expectation that the work will run sequentially. <\/p>\n<pre>\nclass OrderedWorkQueue\n{\npublic:\n OrderedWorkQueue()\n {\n  \/\/ Start up the worker thread.\n  m_thread = std::thread([this]() {\n   while (!m_exiting) {\n    m_signal.Wait();\n    m_queue.ProcessWork();\n   }\n  });\n }\n\n template&lt;typename... Args&gt;\n void QueueWork(Args&amp;&amp;... args)\n {\n  m_queue.Append(std::make_unique&lt;OrderedWorkItem&gt;\n                                  (std::forward&lt;Args&gt;(args)...));\n  m_signal.Signal();\n }\n\n ~OrderedWorkQueue()\n {\n  m_exiting = true;\n  m_signal.Signal();\n }\n\nprivate:\n \/\/ App-provided stuff. <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20161125-00\/?p=94795\">Assume they work<\/a>.\n class OrderedWorkItem\n {\n  template&lt;typename... Args&gt;\n  OrderedWorkItem(Args&amp;&amp;... args);\n\n  void Execute();\n };\n\n SomeSortOfQueue m_queue;\n SomeSortOfSignal m_signal;\n\n \/\/ Stuff related to worker thread management.\n std::atomic&lt;bool&gt; m_exiting;\n std::thread m_thread;\n};\n<\/pre>\n<p>The <code>Ordered&shy;Work&shy;Queue<\/code> object creates a worker thread whose job is to execute work items in the order they were queued. We have a problem here: The main thread signals the worker thread to exit, and then returns immediately, stranding the worker thread with a <code>this<\/code> pointer that points to a destructed object. <\/p>\n<p>One solution is to wait for the worker thread to finish its work. <\/p>\n<pre>\n ~OrderedWorkQueue()\n {\n  m_exiting = true;\n  m_signal.Signal();\n  <font COLOR=\"blue\">m_thread.join();<\/font>\n }\n<\/pre>\n<p>This ensures that the <code>this<\/code> pointer remains valid for the duration of the worker thread. However, this comes with its own problems. <\/p>\n<p>First, It changes the behavior of the class. destruction used to be a quick affair, but now destruction waits for the work items to drain, which can take an indefinite length of time. The intent of the <code>Ordered&shy;Work&shy;Queue<\/code> may have been to employ a fire-and-forget design: Create an ordered work queue, queue a bunch of work to it, and then destroy the work queue. The work that got queued up will still execute eventually, in order, but the main thread was expecting to be able to get other work done in the meantime. <\/p>\n<p>Furthermore, one of the work items may need to communicate with the thread that is doing the destructing, but it can&#8217;t do that because the destructing thread is waiting for the worker thread to exit. So you have a potential deadlock. <\/p>\n<p>Okay, so we solve this problem by having the worker thread maintain a strong reference to the object, to ensure that the object&#8217;s member variables remain valid for the duration of the thread. <\/p>\n<pre>\nclass OrderedWorkQueue\n{\n <font COLOR=\"blue\">static std::shared_ptr&lt;OrderedWorkQueue&gt; Create()\n {\n  auto self = std::make_shared&lt;OrderedWorkQueue&gt;();\n  self-&gt;<\/font>m_thread = std::thread([<font COLOR=\"blue\">lifetime = self<\/font>, this]() {\n   while (!m_exiting) {\n    m_signal.Wait();\n    m_queue.ProcessWork();\n   }\n  });\n  <font COLOR=\"blue\">return self;<\/font>\n }\n<\/pre>\n<p>The captured <code>lifetime<\/code> retains the shared reference so that the background thread can continue using the object&#8217;s member variables. <\/p>\n<p>But wait, we have a new problem: The destructor never runs because the worker thread retains a strong reference to it. <\/p>\n<p>Okay, so we try to fix the problem by passing a weak reference, and converting it to strong only as necessary. <\/p>\n<pre>\n static std::shared_ptr&lt;OrderedWorkQueue&gt; Create()\n {\n  auto self = std::make_shared&lt;OrderedWorkQueue&gt;();\n  self-&gt;m_thread = std::thread(\n   <font COLOR=\"blue\">[weak = std::weak_ptr&lt;OrderedWorkQueue&gt;(self)<\/font>, this<font COLOR=\"blue\">]() {\n   auto strong = weak.lock();\n   if (!strong) return;<\/font>\n   while (!m_exiting) {\n    m_signal.Wait();\n    auto workList = m_queue.DetachWork();\n    <font COLOR=\"blue\">\/\/ drop the strong reference while we process the work\n    strong.reset();<\/font>\n    ProcessWorkList(workList);\n    <font COLOR=\"blue\">\/\/ reacquire the strong reference after work is done\n    strong = weak.lock();\n    if (!strong) return;<\/font>\n   }\n  })<\/font>;\n  return self;\n }\n<\/pre>\n<p>This doesn&#8217;t really go anywhere because the <code>m_signal.Wait()<\/code> call runs while there is still a strong reference, so we are back where we started. <\/p>\n<p>One way out is to create a fa&ccedil;ade. The public-facing <code>Ordered&shy;Work&shy;Queue<\/code> is what other components use to queue work to a background thread. The public-facing part retains a shared reference to the private part, and it&#8217;s the private part that does the real work. <\/p>\n<pre>\nclass OrderedWorkQueue\n{\npublic:\n OrderedWorkQueue() = default;\n\n template&lt;typename... Args&gt;\n void QueueWork(Args&amp;&amp;... args)\n {\n  m_worker-&gt;QueueWork(std::forward&lt;Args&gt;(args)...);\n }\n\n ~OrderedWorkQueue()\n {\n  m_worker-&gt;Exit();\n }\n\nprivate:\n \/\/ This is our old OrderWorkQueue class\n class OrderedWorkQueueWorker\n {\n public:\n  static std::shared_ptr&lt;OrderedWorkQueueWorker&gt; Create()\n  {\n   auto self = std::make_shared&lt;OrderedWorkQueueWorker&gt;();\n   self-&gt;m_thread = std::thread([lifetime = self, this]() {\n    while (!m_exiting) {\n     m_signal.Wait();\n     m_queue.ProcessWork();\n    }\n   });\n   return self;\n  }\n\n  template&lt;typename... Args&gt;\n  void QueueWork(Args&amp;&amp;... args)\n  {\n   m_queue.Append(std::make_unique&lt;OrderedWorkItem&gt;\n                                  (std::forward&lt;Args&gt;(args)...));\n   m_signal.Signal();\n  }\n\n  void Exit()\n  {\n   m_exiting = true;\n   m_signal.Signal();\n  }\n\n private:\n  \/\/ App-provided stuff. Assume they work.\n  class OrderedWorkItem\n  {\n   template&lt;typename... Args&gt;\n   OrderedWorkItem(Args&amp;&amp;... args);\n\n   void Execute();\n  };\n\n  SomeSortOfQueue m_queue;\n  SomeSortOfSignal m_signal;\n\n  \/\/ Stuff related to worker thread management.\n  std::atomic&lt;bool&gt; m_exiting;\n  std::thread m_thread;\n };\n\n <font COLOR=\"blue\">std::shared_ptr&lt;OrderedWorkQueueWorker&gt; m_worker =\n                                OrderedWorkQueueWorker::Create();<\/font>\n};\n<\/pre>\n<p>An equivalent version which some people prefer is to put only the data members into the shared object. <\/p>\n<pre>\nclass OrderedWorkQueue\n{\npublic:\n OrderedWorkQueue()\n {\n  m_thread = std::thread([data = m_data]() {\n   while (!data-&gt;m_exiting) {\n    data-&gt;m_signal.Wait();\n    data-&gt;m_queue.ProcessWork();\n   }\n  });\n }\n\n template&lt;typename... Args&gt;\n void QueueWork(Args&amp;&amp;... args)\n {\n  m_data-&gt;m_queue.Append(std::make_unique&lt;OrderedWorkItem&gt;\n                                  (std::forward&lt;Args&gt;(args)...));\n  m_data-&gt;m_signal.Signal();\n }\n\n ~OrderedWorkQueue()\n {\n  m_data-&gt;m_exiting = true;\n  m_data-&gt;m_signal.Signal();\n }\n\nprivate:\n struct OrderWorkQueueData\n {\n  SomeSortOfQueue m_queue;\n  SomeSortOfSignal m_signal;\n  std::atomic&lt;bool&gt; m_exiting;\n };\n\n class OrderedWorkItem\n {\n  template&lt;typename... Args&gt;\n  OrderedWorkItem(Args&amp;&amp;... args);\n\n  void Execute();\n };\n\n std::shared_ptr&lt;OrderedWorkQueueData&gt; m_data =\n   std::make_shared&lt;OrderedWorkQueueData&gt;();\n\n std::thread m_thread;\n};\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Keep a reference to the right thing.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-101084","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Keep a reference to the right thing.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/101084","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=101084"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/101084\/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=101084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=101084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=101084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}