Is the copy constructor of `std::shared_ptr` atomic versus `reset()`?
<p>This is a verification question, to make sure I'm getting details right&mdash;language lawyers welcome.</p>

<p>I want to know that I can use <code>std::shared_ptr</code> in the following code and don't need to rewrite it with <code>atomic_shared_ptr</code>. The example has been simplified, but the essence is a possible race condition within a single instance of <code>example</code> between (*1) the copy constructor of <code>shared_ptr</code> and (*2) the call to <code>reset()</code>. </p>

<p>Note that a plain pointer for <code>p</code> won't work here. If <code>p</code> were to become null between the test and the call to <code>some_predicate</code>, you'd be indirecting a null pointer. That's the reason to use <code>shared_ptr</code> in the first place. I want to make sure I'm actually solving the race condition, not simply moving it elsewhere.</p>

<p>(It's not the point of the question, but this code might seem wrong at first glance. The behavior of <code>T</code> is idempotent. Once <code>p</code> has finished its job, it's not needed any more.)</p>

<pre><code>template&lt; class T &gt;
class example
{
    shared_ptr&lt; T &gt; p ;
public:
    example()
        : p( make_shared( T() ) )
    {}
    void f()
    {
        shared_ptr&lt; T &gt; p_transient(p) ; // *1
        if ( p_transient &amp;&amp; p_transient -&gt; some_predicate() )
        {
            p.reset() ; // *2
        }
    }
};
</code></pre>

<p>Suppose that (*1) and (*2) execute simultaneously. I can think of two possible results of the race. (The code is correct in each of these two cases.) My question is whether these are the <em>only</em> cases:</p>

<ul>
<li>The copy takes effect before the <code>reset</code>, so <code>p_transient</code> keeps the member instance of <code>T</code> alive. The deleter runs in thread *1 when <code>f</code> returns. (The idempotence of <code>T</code> comes into play here.)</li>
<li><code>reset</code> takes effect before the copy, so <code>p_transient</code> is initialized empty. The deleter runs in thread *2 before <code>reset</code> returns.</li>
</ul>

<p>I can't shake the feeling I'm getting something for nothing here, so I decided to write up the question. Anything I'm missing?</p>

<hr>

<p>P.S. Here's what I was missing. <code>shared_ptr</code> isn't special. Somehow I thought it would be, perhaps because I've implemented smart pointers (too many) times before. Shared pointers, particularly when there are also weak pointers, pretty much require mutex protection for their (hidden) shared state. I figured that protection must have encompassed the entire object, but it doesn't.</p>

<p>Thanks to the responders for references to the standard. The general rule that data races result in undefined behavior is 1.10/27 "Multi-threaded executions and data races [intro.multithread]". In particular, it means that postconditions may be violated in such a situation.</p>