{"id":98665,"date":"2018-05-03T07:00:00","date_gmt":"2018-05-03T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=98665"},"modified":"2019-03-13T00:43:28","modified_gmt":"2019-03-13T07:43:28","slug":"20180503-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180503-00\/?p=98665","title":{"rendered":"Avoiding deadlocks when cancelling a thread pool callback, part 1: External callback data"},"content":{"rendered":"<p>We saw last time how to use the thread pool cleanup functions to manage the lifetime of the context data. But if the callback function tries to call into the thread that is calling <code>Wait&shy;For&shy;Threadpool&shy;Timer&shy;Callbacks<\/code>, then you have a deadlock. The callback cannot proceed until <code>Wait&shy;For&shy;Threadpool&shy;Timer&shy;Callbacks<\/code> returns, but <code>Wait&shy;For&shy;Threadpool&shy;Timer&shy;Callbacks<\/code> won&#8217;t return until the callback completes. <\/p>\n<p>You can find yourself in this situation without realizing it. The callback function might send a message to a window that is owned by the waiting thread. Or it could invokes a method on an apartment-threaded object that belongs to the waiting thread. Or it could attempt to enter a critical section that is held by the waiting thread.&sup1; In many cases, the waiting thread is cleaning up an object in its destructor, so you don&#8217;t even control the locks that may be held at that point. <\/p>\n<p>This is where <a HREF=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms682581(v=vs.85).aspx\"><code>Disassociate&shy;Current&shy;Thread&shy;From&shy;Callback<\/code><\/a> enters the picture. The <code>Disassociate&shy;Current&shy;Thread&shy;From&shy;Callback<\/code> function tells the thread pool, &#8220;For the purpose of waiting until all callbacks are complete, consider this callback to be complete even though it&#8217;s still running.&#8221; You can think of this as the <code>Reply&shy;Message<\/code> of the thread pool. This means that functions like <code>Wait&shy;For&shy;Threadpool&shy;Xxx&shy;Callbacks<\/code> will return, but other functions like <code>Xxx&shy;When&shy;Callback&shy;Returns<\/code> won&#8217;t be fooled. They will wait for the callback to return for real before setting the event or leaving the critical section or whatever. <\/p>\n<p>Let&#8217;s figure out how to take advantage of this. <\/p>\n<p>We make the context data for the callback function a thread-safe reference-counted object. Typical examples are a COM pointer to an agile object, and a reference to a <code>std::shared_ptr<\/code>.&sup2; The first thing the callback function does is to increment the reference count on the object and save it in an RAII object. For a COM pointer, it would be creating a COM smart pointer around it (say, converting it to a <code>WRL::ComPtr<\/code>). For a <code>std::shared_ptr<\/code> it would be copying the <code>std::shared_ptr<\/code> to a local variable. <\/p>\n<p>Once the context data has been safely referenced, you call <code>Disassociate&shy;Current&shy;Thread&shy;From&shy;Callback<\/code> to release the waiting thread, if any. <\/p>\n<p>At this point, you can do your work with the captured strong reference (the <code>WRL::ComPtr<\/code> or the local copy of the <code>std::shared_ptr<\/code>) and stop using the inbound context parameter, because it is no longer valid; the waiting thread may have destroyed it. <\/p>\n<p>When your callback function completes, the RAII type will release the reference to the context data, and if that was the last reference, it will destroy the context data. <\/p>\n<p>We start with this helper class.<\/p>\n<pre>\nstruct TpTimerDeleter\n{\n void operator()(PTP_TIMER timer)\n {\n  SetThreadpoolTimer(timer, nullptr, 0, 0);\n  WaitForThreadpoolTimerCallbacks(timer, true);\n  CloseThreadpoolTimer(timer);\n }\n};\n<\/pre>\n<p>This deleter class performs the standard synchronous shutdown of a thread pool timer, as noted in <a HREF=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms682040(v=vs.85).aspx\">the documentation<\/a>. <\/p>\n<p>Here&#8217;s how we use it to build a thread pool callback that doesn&#8217;t deadlock at cancellation. We&#8217;ll start with one based on <code>WRL::ComPtr<\/code>: <\/p>\n<pre>\nclass ObjectWithTimer\n{\npublic:\n StartTimer();\n StopTimer();\n\nprivate:\n static void CALLBACK TimerCallback(\n  PTP_CALLBACK_INSTANCE instance,\n  void* context, PTP_TIMER timer);\n\n <font COLOR=\"blue\">WRL::ComPtr&lt;AgileContextData&gt; contextData;<\/font> \/\/ ComPtr-specific code\n std::unique_ptr&lt;TP_TIMER, TpTimerDeleter&gt; timer;\n};\n<\/pre>\n<p>Having a <code>std::unique_ptr<\/code> automatically makes the class non-copyable. We&#8217;ll see soon why it&#8217;s okay to let the object be movable. <\/p>\n<p><b>Exercise<\/b>: Why did I declare the <code>timer<\/code> member after the <code>contextData<\/code> member? (Normally, I don&#8217;t answer the exercises in the main body of the article, but this is an important detail, so I&#8217;ll answer it at the end.) <\/p>\n<pre>\nvoid ObjectWithTimer::StartTimer()\n{\n \/\/ Error checking elided for expository purposes.\n timer = CreateThreadpoolTimer(\n    TimerCallback,\n    <font COLOR=\"blue\">contextData.Get(),<\/font> \/\/ ComPtr-specific code\n    nullptr);\n\n SetThreadpoolTimer(timer, ...);\n}\n<\/pre>\n<p>The <code>StartTimer<\/code> method assumes that the <code>contextData<\/code> method has been initialized (presumably by methods not shown) and that the timer has not already been started. It creates the timer with the raw COM pointer as the reference data. This is a non-refcounted pointer, so we have to make sure it remains valid for as long as the callback is potentially-callable. Once we create the timer, we start it by calling <code>Set&shy;Threadpool&shy;Timer<\/code> and passing the timer parameters (not shown here). <\/p>\n<p>The context parameter passed to the callback is the thing we have to worry about if the <code>Object&shy;With&shy;Timer<\/code> gets moved. In this case, it&#8217;s okay to move the <code>Object&shy;With&shy;Timer<\/code> because the context parameter doesn&#8217;t point to the <code>Object&shy;With&shy;Timer<\/code>; it is a raw COM pointer to an object that won&#8217;t move. Therefore, the <code>Object&shy;With&shy;Timer<\/code> is movable. <\/p>\n<pre>\nvoid ObjectWithTimer::TimerCallback(PTP_CALLBACK_INSTANCE instance,\n  void* context, PTP_TIMER timer)\n{\n \/\/ ComPtr-specific code\n <font COLOR=\"blue\">WRL::ComPtr&lt;AgileContextData&gt; contextData{\n    reinterpret_cast&lt;AgileContextData*&gt;(context) };<\/font>\n\n context = nullptr;\n\n DisassociateCurrentThreadFromCallback(instance);\n\n ... do stuff with contextData ...\n}\n<\/pre>\n<p>The callback function starts by taking our weak pointer and converting it to a strong pointer. We designed our code so that the raw COM pointer is valid for as long as the callback is potentially-callable, so we know that it is valid here. We put it inside a <code>WRL::ComPtr<\/code> to make it a strong reference. <\/p>\n<p>And then here&#8217;s the interesting part: We call <code>Disassociate&shy;Current&shy;Thread&shy;From&shy;Callback<\/code><\/a> to tell the thread pool to release anybody who is waiting for the callback to complete. The callback <i>is still running<\/i>, but anybody who is waiting for it to complete is free to proceed anyway. We can do this because we have captured the information from the <code>context<\/code> parameter, and the main object can free it. (To ensure we don&#8217;t access the <code>context<\/code> parameter by mistake, we also set <code>context<\/code> to <code>nullptr<\/code>.) <\/p>\n<p>We then perform our callback operation with the strong reference in <code>contextData<\/code>. This can call back into the main thread because the main thread is no longer stuck in <code>Wait&shy;For&shy;Threadpool&shy;Timer&shy;Callbacks<\/code>, It will eventually return to its event dispatch loop, or release its locks, or whatever it is that needs to happen for the thread pool thread to be able to communicate with the main thread. <\/p>\n<p>The other half of the dance comes when we want to stop the timer. <\/p>\n<pre>\nvoid ObjectWithTimer::StopTimer()\n{\n timer.reset();\n <font COLOR=\"blue\">contextData.Reset();<\/font> \/\/ ComPtr-specific code\n}\n<\/pre>\n<p>To stop the timer, we let the <code>TpTimerDeleter<\/code> do the heavy lifting of checking if we have a timer and if so, shutting it down cleanly. <\/p>\n<p>Once that&#8217;s done, we can safely release our reference to the context data. If the callback is running, it will have its own reference. <\/p>\n<p>For simplicity, this code doesn&#8217;t try to save the thread pool timer object for future use. One of the features of the thread pool functions is that object creation preallocates all resources. Once you&#8217;ve created the thread pool timer successfully, all other operations will always succeed (assuming they are used correctly, of course). Therefore, what we could&#8217;ve done is allocate the <code>PTP_TIMER<\/code> at construction (throwing if not possible), and the have the <code>Start&shy;Timer<\/code> and <code>Stop&shy;Timer<\/code> methods merely reconfigure the timer and (in the case of <code>Stop&shy;Timer<\/code>) wait for the callbacks to drain. <\/p>\n<p>Note that in order to avoid the deadlock, we have to accept that the callback may run <i>after the timer has been stopped<\/i>. When you have a deadlock, something has to give, and we choose to break the deadlock by letting the callback complete asynchronously. If you need to know when the callback is definitely finished, you could add an event that gets signaled when the COM object is destructed, so that the caller knows when everything is finally finished. <\/p>\n<p>Adapting the above code to <code>std::shared_ptr<\/code> is not too difficult. The tricky part is that we cannot pass a raw pointer to the context data as our reference data, because raw C++ objects are not reference-counted. Instead, we pass a pointer to the <code>std::shared_ptr<\/code>, which is the thing with the reference count. <\/p>\n<pre>\nclass ObjectWithTimer\n{\npublic:\n <font COLOR=\"blue\">\/\/ Make object non-movable\n ObjectWithTimer(ObjectWithTimer&amp;&amp;) = delete;\n ObjectWithTimer operator=(ObjectWithTimer&amp;&amp;) = delete;<\/font>\n\n StartTimer();\n StopTimer();\n\nprivate:\n static void CALLBACK TimerCallback(\n  PTP_CALLBACK_INSTANCE instance,\n  void* context, PTP_TIMER timer);\n\n <font COLOR=\"blue\">std::shared_ptr&lt;AgileContextData&gt; contextData;<\/font> \/\/ shared_ptr-specific code\n std::unique_ptr&lt;TP_TIMER, TpTimerDeleter&gt; timer;\n};\n\nvoid ObjectWithTimer::StartTimer()\n{\n  \/\/ Error checking elided for expository purposes.\n  timer = CreateThreadpoolTimer(\n    TimerCallback,\n    <font COLOR=\"blue\">std::addressof(contextData),<\/font> \/\/ shared_ptr-specific code\n    nullptr);\n\n  \/\/ Start the timer\n  SetThreadpoolTimer(timer, ...);\n}\n\nvoid ObjectWithTimer::TimerCallback(PTP_CALLBACK_INSTANCE instance,\n  void* context, PTP_TIMER timer)\n{\n \/\/ Capture the context with a strong reference\n <font COLOR=\"blue\">\/\/ shared_ptr-specific code\n std::shared_ptr&lt;AgileContextData&gt; contextData{\n    *reinterpret_cast&lt;\n      std::shared_ptr&lt;AgileContextData&gt;*&gt;(context) };<\/font>\n\n context = nullptr;\n\n DisassociateCurrentThreadFromCallback(instance);\n\n ... do stuff with contextData ...\n}\n\nvoid ObjectWithTimer::StopTimer()\n{\n  timer.reset();\n  <font COLOR=\"blue\">contextData.reset();<\/font> \/\/ shared_ptr-specific code\n}\n<\/pre>\n<p>The principle is the same here as before, but the implementation is made more complicated by the fact that the <code>std::shared_ptr<\/code> is not necessarily (and in fact usually isn&#8217;t) the size of a pointer, so it doesn&#8217;t fit in the context pointer. We have to pass a pointer to the <code>std::shared_ptr<\/code>. <\/p>\n<p> <b>Exercise<\/b>: We could have written <code>&amp;contextData<\/code> instead of <code>std::addressof(contextData)<\/code>. Why did I use <code>std::addressof(contextData)<\/code>? <\/p>\n<p>The fact that a <code>std::shared_ptr<\/code> doesn&#8217;t fit in a pointer means that the <code>std::shared_ptr<\/code> cannot move, because it is still being referenced by the context pointer passed to the thread pool callback. We can make the <code>Object&shy;With&shy;Timer<\/code> object movable by putting the <code>std::shared_ptr<\/code> inside a <code>std::unique_ptr<\/code>, and passing the raw pointer to the <code>std::shared_ptr<\/code>. The <code>std::unique_ptr<\/code> will transfer the pointer to the moved-to object, and the <code>std::shared_ptr<\/code> itself doesn&#8217;t move. <\/p>\n<pre>\nclass ObjectWithTimer\n{\npublic:\n <font COLOR=\"blue\">\/\/ Make object movable again\n \/\/ <strike>ObjectWithTimer(ObjectWithTimer&amp;&amp;) = delete;<\/strike>\n \/\/ <strike>ObjectWithTimer operator=(ObjectWithTimer&amp;&amp;) = delete;<\/strike><\/font>\n\n StartTimer();\n StopTimer();\n\nprivate:\n static void CALLBACK TimerCallback(\n  PTP_CALLBACK_INSTANCE instance,\n  void* context, PTP_TIMER timer);\n\n <font COLOR=\"blue\">std::unique_ptr&lt;\n   shared_ptr&lt;AgileContextData&gt;&gt; contextData;<\/font> \/\/ unique_ptr-specific code\n std::unique_ptr&lt;TP_TIMER, TpTimerDeleter&gt; timer;\n};\n\nvoid ObjectWithTimer::StartTimer()\n{\n  \/\/ Error checking elided for expository purposes.\n  timer = CreateThreadpoolTimer(\n    TimerCallback,\n    <font COLOR=\"blue\">contextData.get(),<\/font> \/\/ unique_ptr-specific code\n    nullptr);\n\n  \/\/ Start the timer\n  SetThreadpoolTimer(timer, ...);\n}\n\nvoid ObjectWithTimer::TimerCallback(PTP_CALLBACK_INSTANCE instance,\n  void* context, PTP_TIMER timer)\n{\n \/\/ Capture the context with a strong reference\n <font COLOR=\"blue\">\/\/ no change here\n std::shared_ptr&lt;AgileContextData&gt; contextData{\n    *reinterpret_cast&lt;\n      std::shared_ptr&lt;AgileContextData&gt;*&gt;(context) };<\/font>\n\n context = nullptr;\n\n DisassociateCurrentThreadFromCallback(instance);\n\n ... do stuff with contextData ...\n}\n\nvoid ObjectWithTimer::StopTimer()\n{\n  timer.reset();\n  <font COLOR=\"blue\">contextData.reset();<\/font> \/\/ no change here\n}\n<\/pre>\n<p>The idea in all the cases is that we keep a reference-counted object in the <code>contextData<\/code> and provide the callback a way to convert its context parameter to a strong reference. Since we are careful always to keep the context parameter valid as long as the callback is potentially-callable, this conversion is straightforward: Just create your own strong reference from the raw pointer. <\/p>\n<p>Often, the context for the callback is the containing object itself, rather than some external data. We&#8217;ll explore that scenario next time, because this article is too long as it is. <\/p>\n<p>&sup1; The critical section case is easy to imagine: You might have a critical section that protects access to object state. The waiting thread owns the critical section because it&#8217;s trying to clean up the object&#8217;s thread pool timer. The callback is trying to acquire the critical section because it wants to access the object&#8217;s state as part of the callback operation. <\/p>\n<p>&sup2; <a HREF=\"https:\/\/www.youtube.com\/watch?v=lkgszkPnV8g#t=20m10s\">Be careful with the <code>std::shared_ptr<\/code><\/a>. Copying a <code>std::shared_ptr<\/code> is thread-safe, but mutating it is not, so you should initialize your <code>std::shared_ptr<\/code> with the context structure and not modify the <code>std::shared_ptr<\/code> until you are sure that no other threads are accessing it. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Breaking the deadlock by disassociating from the thread pool.<\/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-98665","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Breaking the deadlock by disassociating from the thread pool.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98665","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=98665"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98665\/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=98665"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=98665"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=98665"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}