New order of expression evaluation (pointer assignment)

Consider the following code:

Class* p = nullptr; //global var

      

This code executed by thread 1:

p = new Class;

      

This code runs on thread 2:

if (p != nullptr) ...; // does the standard gurantee that the pointer will be assigned only after object is constructed ?

      

My question is, is it standard when p

assigned to point to allocated memory? Example 1:

  • new expression call operator new
  • p

    assigned to point to newly allocated memory
  • Class

    c`tor is called and the allocated memory is passed to it

Example 2:

  • new expression call operator new
  • Class

    c`tor is called and the allocated memory is passed to it
  • p

    assigned to point to newly allocated memory
+3


source to share


2 answers


According to the standard, if these operations on the two threads are not synchronized, the behavior is undefined.

C ++ 11 draft N3337,

[intro.multithread] / 4:

Two expression evaluations contradict each other if one of them changes the memory location (1.7) and the other accesses or changes the same memory location.

[intro.multithread] / 21:

The execution of a program contains a data race if it contains two conflicting activities in different threads, at least one of which is not atomic and the other is not happening. Any such data race results in undefined.



The relevant quote from C ++ 14 is essentially the same.


As far as the order of execution is concerned p = new Class;

, it is as in your example 2, because the first is evaluated new Class

and then the assignment is performed (if the constructor Class

or operator new

does not throw an exception).

[expr.ass] / 1:

In all cases, the assignment is ordered after the value of the evaluation of the right and left operands and before the evaluation of the value of the assignment expression.

+6


source


No, the standard does not guarantee anything like that.

To fix this, you need to have a memory barrier between constructing your object and assigning a pointer so that thread-to-thread communication takes place between them - before the relationship between them:

Class* tmp = new Class();
// you need a memory barrier here
p = tmp;

      

In C ++ 11, you use std::atomic

to introduce memory barriers:



std::atomic<Class*> p;

      

And in this case it is better to use store()

instead of assignment:

p.store(tmp, std::memory_order_release);

      

0


source







All Articles