<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 3343: Ordering of calls to unlock() and notify_all() in Effects element of notify_all_at_thread_exit() should be reversed</title>
<meta property="og:title" content="Issue 3343: Ordering of calls to unlock() and notify_all() in Effects element of notify_all_at_thread_exit() should be reversed">
<meta property="og:description" content="C++ library issue. Status: Open">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue3343.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#Open">Open</a> status.</em></p>
<h3 id="3343"><a href="lwg-active.html#3343">3343</a>. Ordering of calls to <code>unlock()</code> and <code>notify_all()</code> in <i>Effects</i> element of <code>notify_all_at_thread_exit()</code> should be reversed</h3>
<p><b>Section:</b> 32.7.3 <a href="https://wg21.link/thread.condition.nonmember">[thread.condition.nonmember]</a> <b>Status:</b> <a href="lwg-active.html#Open">Open</a>
 <b>Submitter:</b> Lewis Baker <b>Opened:</b> 2019-11-21 <b>Last modified:</b> 2023-06-13</p>
<p><b>Priority: </b>3
</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Open">Open</a> status.</p>
<p><b>Discussion:</b></p>
<p>
32.7.3 <a href="https://wg21.link/thread.condition.nonmember">[thread.condition.nonmember]</a> p2 states:
</p>
<blockquote>
<p>
<i>Effects:</i> Transfers ownership of the lock associated with <code>lk</code> into internal storage 
and schedules <code>cond</code> to be notified when the current thread exits, after all objects of 
thread storage duration associated with the current thread have been destroyed. This notification 
shall be as if:
</p>
<blockquote><pre>
lk.unlock();
cond.notify_all();
</pre></blockquote>
</blockquote>
<p>
One common use-cases for the <code>notify_all_at_thread_exit()</code> is in conjunction with
<code>thread::detach()</code> to allow detached threads to signal when they complete and to allow 
another thread to wait for them to complete using the <code>condition_variable/mutex</code> pair.
<p/>
However, the current wording for <code>notify_all_at_thread_exit(condition_variable&amp; cond, 
unique_lock&lt;mutex&gt; lk)</code> makes it impossible to know when it is safe to destroy the 
<code>condition_variable</code> in the presence of spurious wake-ups and detached threads.
<p/>
For example: Consider the following code-snippet:
</p>
<blockquote><pre>
#include &lt;condition_variable&gt;
#include &lt;mutex&gt;
#include &lt;thread&gt;

int main() {
  std::condition_variable cv;
  std::mutex mut;
  bool complete = false;

  std::thread{[&amp;] {
    <i>// do work here</i>

    <i>// Signal thread completion</i>
    std::unique_lock lk{mut};
    complete = true;
    std::notify_all_at_thread_exit(cv, std::move(lk));
  }}.detach();

  <i>// Wait until thread completes</i>
  std::unique_lock lk{mut};
  cv.wait(lk, [&amp;] { return complete; });

  <i>// condition_variable destroyed on scope exit</i>
  return 0;
}
</pre></blockquote>
<p>
This seems to an intended usage of <code>thread::detach()</code> and <code>std::notify_all_at_thread_exit()</code> 
and yet this code contains a race involving the call to <code>cv.notify_all()</code> on the created thread, 
and the destructor of the <code>condition_variable</code>.
<p/>
To highlight the issue, consider the following case:
<p/>
Let <code>T0</code> be the thread that executes <code>main()</code> and <code>T1</code> be the thread created 
by the <code>std::thread</code> construction.
</p> 
<blockquote>
<p>
<code>T0</code>: creates thread <code>T1</code><br/>
<code>T0</code>: context-switched out by OS<br/>
<code>T1</code>: starts running<br/>
<p/>
<code>T1</code>: acquires mutex lock<br/>
<code>T1</code>: sets <code>complete = true</code><br/>
<p/>
<code>T1</code>: calls <code>notify_all_at_thread_exit()</code><br/>
<code>T1</code>: returns from thread-main function and runs all thread-local destructors<br/>
<code>T1</code>: calls <code>lk.unlock()</code><br/>
<code>T1</code>: context-switched out by OS<br/>
<code>T0</code>: resumes execution<br/>
<code>T0</code>: acquires mutex lock<br/>
<code>T0</code>: calls <code>cv.wait()</code> which returns immediately as <code>complete</code> is <code>true</code><br/>
<p/>
<code>T0</code>: returns from <code>main()</code>, destroying <code>condition_variable</code><br/>
<code>T1</code>: resumes execution<br/>
<p/>
<code>T1</code>: calls <code>cv.notify_all()</code> on a dangling <code>cv</code> reference (undefined behaviour)<br/>
</p>
</blockquote>
<p>
Other sequencings are possible involving spurious wake-ups of the <code>cv.wait()</code> call.
<p/>
A proof-of-concept showing this issue can be found <a href="https://wandbox.org/permlink/eUu3eiQbLl7JQKMm">here</a>.
<p/>
The current wording requires releasing the mutex lock before calling <code>cv.notify_all()</code>. In the 
presence of spurious wake-ups of a <code>condition_variable::wait()</code>, there is no way to know whether 
or not a detached thread that called <code>std::notify_all_at_thread_exit()</code> has finished calling 
<code>cv.notify_all()</code>. This means there is no portable way to know when it will be safe for the 
waiting thread to destroy that <code>condition_variable</code>.
<p/>
However, if we were to reverse the order of the calls to <code>lk.unlock()</code> and <code>cond.notify_all()</code> 
then the thread waiting for the detached thread to exit would not be able to observe the completion of the 
thread (in the above case, this would be observing the assignment of <code>true</code> to the <code>complete</code> 
variable) until the mutex lock was released by that thread and subsequently acquired by the waiting thread 
which would only happen after the completion of the call to <code>cv.notify_all()</code>. This would allow the 
above code example to eliminate the race between a subsequent destruction of the condition-variable and 
the call to <code>cv.notify_all()</code>.
</p>

<p><i>[2019-12-08 Issue Prioritization]</i></p>

<p>Priority to 3 after reflector discussion.</p>

<p><i>[2019-12-15; Daniel synchronizes wording with <a href="https://wg21.link/n4842">N4842</a>]</i></p>


<p><i>[2020-02, Prague]</i></p>

<p>Response from SG1: "We discussed it in Prague. We agree it’s an error and SG1 agreed with the PR."</p>
<p><strong>Previous resolution [SUPERSEDED]:</strong></p>
<blockquote class="note">

<p>This wording is relative to <a href="https://wg21.link/n4842">N4842</a>.</p>

<ol>
<li><p>Change 32.7.3 <a href="https://wg21.link/thread.condition.nonmember">[thread.condition.nonmember]</a> as indicated:</p>

<blockquote>
<pre>
void notify_all_at_thread_exit(condition_variable&amp; cond, unique_lock&lt;mutex&gt; lk);
</pre>
<blockquote>
<p>
[&hellip;]
<p/>
-2- <i>Effects:</i> Transfers ownership of the lock associated with <code>lk</code> into internal 
storage and schedules <code>cond</code> to be notified when the current thread exits, after all 
objects of thread storage duration associated with the current thread have been destroyed. 
This notification is equivalent to:
</p>
<blockquote><pre>
<del>lk.unlock();</del>
cond.notify_all();
<ins>lk.unlock();</ins>
</pre></blockquote>
</blockquote>
</blockquote>
</li>
</ol>
</blockquote>

<p><i>[2023-06-13, Varna; Tim provides improved wording]</i></p>

<p>Addressed mailing list comments. Ask SG1 to check.</p>



<p id="res-3343"><b>Proposed resolution:</b></p>

<p>This wording is relative to <a href="https://wg21.link/N4950" title=" Working Draft, Standard for Programming Language C++">N4950</a>.</p>

<ol>
<li><p>Change 32.7.3 <a href="https://wg21.link/thread.condition.nonmember">[thread.condition.nonmember]</a> as indicated:</p>

<blockquote>
<pre>
void notify_all_at_thread_exit(condition_variable&amp; cond, unique_lock&lt;mutex&gt; lk);
</pre>
<blockquote>
<p>
[&hellip;]
<p/>
-2- <i>Effects:</i> Transfers ownership of the lock associated with <code>lk</code> into internal
storage and schedules <code>cond</code> to be notified when the current thread
exits<del>,</del><ins>.  This notification is sequenced</ins> after all
objects of thread storage duration associated with the current thread have been
destroyed<del>.  This notification</del> <ins>and</ins> is equivalent to:
</p>
<blockquote><pre>
<del>lk.unlock();</del>
cond.notify_all();
<ins>lk.unlock();</ins>
</pre></blockquote>
</blockquote>
</blockquote>
</li>
</ol>





</body>
</html>
