This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 110c. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2023-02-07


710. Data races during construction

Section: 11.9.5  [class.cdtor]     Status: CD2     Submitter: Jeffrey Yasskin     Date: 3 May, 2008

[Voted into WP at March, 2010 meeting.]

Consider the following example:

    struct A {
      A() {
        std::thread(&A::Func, this).detach();
      }
      virtual void Func() {
        printf("In A");
      }
    };

    struct B : public A {
      virtual void Func() {
        printf("In B");
      }
    };

    struct C : public B {
      virtual void Func() {
        printf("In C");
      }
    };

    C c;

What is the program allowed to print? Should it be undefined behavior or merely unspecified which of the Func()s is called?

There is a related question about which variables C::Func() can depend on having been constructed. Unless we want to require the equivalent of at least memory_order_consume on the presumed virtual function table pointer, I think the answer is just the members of A.

If I instead just have

    A a;

I think the only reasonable behavior is to print In A.

Finally, given

    struct F {
      F() {
        std::thread(&F::Func, this).detach();
      }
      virtual void Func() {
        print("In F");
      }
    };

    struct G : public F {
    };

    G g;

I can see the behavior being undefined, but I think a lot of people would be confused if it did anything other than print In F.

Suggested resolution:

I think the intent here is that an object should not be used in another thread until any non-trivial constructor has been called. One possible way of saying that would be to add a new paragraph at the end of 11.9.5 [class.cdtor]:

A constructor for a class with virtual functions or virtual base classes modifies a memory location in the object that is accessed by any access to a virtual function or virtual base class or by a dynamic_cast. [Note: This implies that access to an object by another thread while it is being constructed often introduces a data race (see 6.9.2 [intro.multithread]). —end note]

Proposed resolution (October, 2009):

Add the following as a new paragraph at the end of 6.7.3 [basic.life]:

In this section, “before” and “after” refer to the “happens before” relation (6.9.2 [intro.multithread]). [Note: Therefore, undefined behavior results if an object that is being constructed in one thread is referenced from a different thread without adequate synchronization. —end note]