<html>
    <head>
      <base href="https://llvm.org/bugs/" />
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW " title="NEW --- - TSAN incorrectly model condition variable semantics" href="https://urldefense.proofpoint.com/v2/url?u=https-3A__llvm.org_bugs_show-5Fbug.cgi-3Fid-3D23616&d=AwMBaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=pF93YEPyB-J_PERP4DUZOJDzFVX5ZQ57vQk33wu0vio&m=9afZ1te3ExIWdWQah38Mm66-pMpj2tAAvCCWMKSTmI8&s=jBsGVmUT081_hslAbeWfnCbBy7OTp3lKv9Mg4ACo_g8&e=">23616</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>TSAN incorrectly model condition variable semantics
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>compiler-rt
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Linux
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>compiler-rt
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>eric@efcs.ca
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvmbugs@cs.uiuc.edu
          </td>
        </tr>

        <tr>
          <th>Classification</th>
          <td>Unclassified
          </td>
        </tr></table>
      <p>
        <div>
        <pre>The example execution should be well formed but it currently causes TSAN to
incorrectly report a data race.

Step   Thread A                    Thread B
1.     ...                        lk.lock()
2.     ...                        cv.wait(lk)
3.    lk.lock()                      ...
4.    cv.notify_one()                ...
5.    cv.~condition_variable()       ...
6.    lk.unlock()                    ...
7.      ...                       finally exits cv.wait  // ok, not a data race

For this execution TSAN will report something akin to:

1. In step 5 thread A writes to cv while holding lk.
2. Previous read of cv by thread B in step 2.

Note that when TSAN intercepts cv.wait(lk) it unlocks lk before annotating the
read. This is what leads to the incorrect behaviour.


To restate C++14 [thread.condition]p3 the call to `wait(lock)` has three atomic
steps (denoted Wait.X)
Wait.1: Enter the waiting queue and release the lock.
Wait.2: Become unblocked in wait().
Wait.3: Reacquire the mutex.

In order to show that there is no data race I will demonstrate that Thread A
cannot reach step 5 until thread B completes Wait.2. This condition is
sufficient because once thread B completes Wait.2 the condition variable can be
safely destroyed according to the wording in 30.5.1 p5.

Obviously thread A cannot proceed past step 3 until thread B completes Wait.1
and releases the mutex.

Thread B cannot complete Wait.2 until it is notified by thread A. The effects
of notify_all() say that the unblocking of thread B "happens before" thread A
returns from the notify_all().

Therefore thread B must reach Wait.3 before thread A can reach step 5

Below are the relevant clauses from the C++14 standard regarding condition
variable.

30.5 [thread.condition]p3:
<span class="quote">>p3 The execution of notify_one and notify_all shall be atomic. The execution 
>    of wait, wait_for, and
>    wait_until shall be performed in three atomic parts:
>    1. the release of the mutex and entry into the waiting state;
>    2. the unblocking of the wait; and
>    3. the reacquisition of the lock.
>p4 The implementation shall behave as if all executions of notify_one,
>   notify_all, and each part of the wait,
>   wait_for, and wait_until executions are executed in a single unspecified 
>   total order consistent with the "happens before" order.</span >

30.5.1 [thread.condition.condvar]p5
<span class="quote">> ~ condition_variable();
> Requires: There shall be no thread blocked on *this. [ Note: That is, all 
>     threads shall have been notified; they may subsequently block on the
>     lock specified in the wait. This relaxes the usual rules, which would 
>     have required all wait calls to happen before destruction. Only the 
>     notification to unblock the wait must happen before destruction. The user 
>     must take care to ensure that no threads wait on
>     *this once the destructor has been started, especially when the waiting 
>     threads are calling the wait</span >

30.5.1 [thread.condition.condvar]p8
<span class="quote">> void notify_all() noexcept;
> Effects: Unblocks all threads that are blocked waiting for *this.</span >


The following libc++ test serves as a minimal reproducer.
<a href="https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_llvm-2Dmirror_libcxx_blob_master_test_std_thread_thread.condition_thread.condition.condvar_destructor.pass.cpp&d=AwMBaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=pF93YEPyB-J_PERP4DUZOJDzFVX5ZQ57vQk33wu0vio&m=9afZ1te3ExIWdWQah38Mm66-pMpj2tAAvCCWMKSTmI8&s=0liP3kJwTTeFkp8JK01Lsasuy4RVHYaKU3ubtfTm_5E&e=">https://github.com/llvm-mirror/libcxx/blob/master/test/std/thread/thread.condition/thread.condition.condvar/destructor.pass.cpp</a></pre>
        </div>
      </p>
      <hr>
      <span>You are receiving this mail because:</span>
      
      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>