Why does ThreadSanitizer report a race with this lock-free example?
<p>I've boiled this down to a simple self-contained example. The main thread enqueues 1000 items, and a worker thread tries to dequeue concurrently. ThreadSanitizer complains that there's a race between the read and the write of one of the elements, even though there is an acquire-release memory barrier sequence protecting them.</p>

<pre><code>#include &lt;atomic&gt;
#include &lt;thread&gt;
#include &lt;cassert&gt;

struct FakeQueue
{
    int items[1000];
    std::atomic&lt;int&gt; m_enqueueIndex;
    int m_dequeueIndex;

    FakeQueue() : m_enqueueIndex(0), m_dequeueIndex(0) { }

    void enqueue(int x)
    {
        auto tail = m_enqueueIndex.load(std::memory_order_relaxed);
        items[tail] = x;              // &lt;- element written
        m_enqueueIndex.store(tail + 1, std::memory_order_release);
    }

    bool try_dequeue(int&amp; x)
    {
        auto tail = m_enqueueIndex.load(std::memory_order_acquire);
        assert(tail &gt;= m_dequeueIndex);
        if (tail == m_dequeueIndex)
            return false;
        x = items[m_dequeueIndex];    // &lt;- element read -- tsan says race!
        ++m_dequeueIndex;
        return true;
    }
};


FakeQueue q;

int main()
{
    std::thread th([&amp;]() {
        int x;
        for (int i = 0; i != 1000; ++i)
            q.try_dequeue(x);
    });

    for (int i = 0; i != 1000; ++i)
        q.enqueue(i);

    th.join();
}
</code></pre>

<p>ThreadSanitizer output:</p>

<pre><code>==================
WARNING: ThreadSanitizer: data race (pid=17220)
  Read of size 4 at 0x0000006051c0 by thread T1:
    #0 FakeQueue::try_dequeue(int&amp;) /home/cameron/projects/concurrentqueue/tests/tsan/issue49.cpp:26 (issue49+0x000000402bcd)
    #1 main::{lambda()#1}::operator()() const &lt;null&gt; (issue49+0x000000401132)
    #2 _M_invoke&lt;&gt; /usr/include/c++/5.3.1/functional:1531 (issue49+0x0000004025e3)
    #3 operator() /usr/include/c++/5.3.1/functional:1520 (issue49+0x0000004024ed)
    #4 _M_run /usr/include/c++/5.3.1/thread:115 (issue49+0x00000040244d)
    #5 &lt;null&gt; &lt;null&gt; (libstdc++.so.6+0x0000000b8f2f)

  Previous write of size 4 at 0x0000006051c0 by main thread:
    #0 FakeQueue::enqueue(int) /home/cameron/projects/concurrentqueue/tests/tsan/issue49.cpp:16 (issue49+0x000000402a90)
    #1 main /home/cameron/projects/concurrentqueue/tests/tsan/issue49.cpp:44 (issue49+0x000000401187)

  Location is global 'q' of size 4008 at 0x0000006051c0 (issue49+0x0000006051c0)

  Thread T1 (tid=17222, running) created by main thread at:
    #0 pthread_create &lt;null&gt; (libtsan.so.0+0x000000027a67)
    #1 std::thread::_M_start_thread(std::shared_ptr&lt;std::thread::_Impl_base&gt;, void (*)()) &lt;null&gt; (libstdc++.so.6+0x0000000b9072)
    #2 main /home/cameron/projects/concurrentqueue/tests/tsan/issue49.cpp:41 (issue49+0x000000401168)

SUMMARY: ThreadSanitizer: data race /home/cameron/projects/concurrentqueue/tests/tsan/issue49.cpp:26 FakeQueue::try_dequeue(int&amp;)
==================
ThreadSanitizer: reported 1 warnings
</code></pre>

<p>Command line:</p>

<pre><code>g++ -std=c++11 -O0 -g -fsanitize=thread issue49.cpp -o issue49 -pthread
</code></pre>

<p>g++ version: 5.3.1</p>

<p>Can anybody shed some light onto why tsan thinks this is a data race?</p>

<hr>

<p><strong>UPDATE</strong></p>

<p>It seems like this is a false positive. To appease ThreadSanitizer, I've added annotations (see <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_interface_ann.cc?revision=221194&amp;view=markup" rel="noreferrer">here</a> for the supported ones and <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/annotate_happens_before.cc?view=markup" rel="noreferrer">here</a> for an example). Note that detecting whether tsan is enabled in GCC via a macro has <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64354" rel="noreferrer">only recently been added</a>, so I had to manually pass <code>-D__SANITIZE_THREAD__</code> to g++ for now.</p>

<pre><code>#if defined(__SANITIZE_THREAD__)
#define TSAN_ENABLED
#elif defined(__has_feature)
#if __has_feature(thread_sanitizer)
#define TSAN_ENABLED
#endif
#endif

#ifdef TSAN_ENABLED
#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr) \
    AnnotateHappensBefore(__FILE__, __LINE__, (void*)(addr))
#define TSAN_ANNOTATE_HAPPENS_AFTER(addr) \
    AnnotateHappensAfter(__FILE__, __LINE__, (void*)(addr))
extern "C" void AnnotateHappensBefore(const char* f, int l, void* addr);
extern "C" void AnnotateHappensAfter(const char* f, int l, void* addr);
#else
#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr)
#define TSAN_ANNOTATE_HAPPENS_AFTER(addr)
#endif

struct FakeQueue
{
    int items[1000];
    std::atomic&lt;int&gt; m_enqueueIndex;
    int m_dequeueIndex;

    FakeQueue() : m_enqueueIndex(0), m_dequeueIndex(0) { }

    void enqueue(int x)
    {
        auto tail = m_enqueueIndex.load(std::memory_order_relaxed);
        items[tail] = x;
        TSAN_ANNOTATE_HAPPENS_BEFORE(&amp;items[tail]);
        m_enqueueIndex.store(tail + 1, std::memory_order_release);
    }

    bool try_dequeue(int&amp; x)
    {
        auto tail = m_enqueueIndex.load(std::memory_order_acquire);
        assert(tail &gt;= m_dequeueIndex);
        if (tail == m_dequeueIndex)
            return false;
        TSAN_ANNOTATE_HAPPENS_AFTER(&amp;items[m_dequeueIndex]);
        x = items[m_dequeueIndex];
        ++m_dequeueIndex;
        return true;
    }
};

// main() is as before
</code></pre>

<p>Now ThreadSanitizer is happy at runtime.</p>