Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::atomic_flag as member variable

Tags:

c++

c++11

What is a safe way to initialize an std::atomic_flag in a class constructor?

This question seems to be asking the same question I'm asking - except here the asker is complaining about a compiler problem.

My question relates to the C++ standard itself. According to this site, initializing an std::atomic_flag using constructor initializer syntax is unspecified.

std::atomic_flag static_flag = ATOMIC_FLAG_INIT; // static initialization,
// guaranteed to be available during dynamic initialization of static objects.

int main()
{
    std::atomic_flag automatic_flag = ATOMIC_FLAG_INIT; // guaranteed to work
//    std::atomic_flag another_flag(ATOMIC_FLAG_INIT); // unspecified
}

Is this information correct? If so, I assume that:

struct Foo
{
  Foo() : flag(ATOMIC_FLAG_INIT)
  { }

  std::atomic_flag flag;
};

...is also unspecified. So, does this mean that we can't use an std::atomic_flag as a member variable of a class? Or is it safe if we simple call std::atomic_flag::clear() from within a class constructor?

like image 596
Siler Avatar asked Jun 26 '14 18:06

Siler


2 Answers

The wording concerning use of ATOMIC_FLAG_INIT has changed since N3337 to N3936 (the current C++14 draft). The former shows a possible usage, in a copy-initialization context, of the ATOMIC_FLAG_INIT macro in an example, which are non-normative, and doesn't mention anything about uses within other initialization contexts.

N3936 clarifies the usage, and no longer lists the copy-initialization usage as an example, but as part of the description itself.

§29.7/4 [atomics.flag]

The macro ATOMIC_FLAG_INIT shall be defined in such a way that it can be used to initialize an object of type atomic_flag to the clear state. The macro can be used in the form:

 atomic_flag guard = ATOMIC_FLAG_INIT;

It is unspecified whether the macro can be used in other initialization contexts. For a complete static-duration object, that initialization shall be static. Unless initialized with ATOMIC_FLAG_INIT, it is unspecified whether an atomic_flag object has an initial state of set or clear.

The rationale for these changes is discussed here.

So you're right that using the macro in the member initializer list cannot be relied upon. The solution is to use a non-static data member initializer, or brace-or-equal-initializer, to initialize the atomic_flag. Then it will be initialized in a copy initialization context.

struct Foo
{
  std::atomic_flag flag = ATOMIC_FLAG_INIT;
};
like image 78
Praetorian Avatar answered Nov 07 '22 02:11

Praetorian


The actual text of the C++11 standard (well, N1570)'s definition of ATOMIC_FLAG_INIT is

7.17.8 Atomic flag type and operations

...

The macro ATOMIC_FLAG_INIT may be used to initialize an atomic_flag to the clear state. An atomic_flag that is not explicitly initialized with ATOMIC_FLAG_INIT is initially in an indeterminate state.

EXAMPLE

   atomic_flag guard = ATOMIC_FLAG_INIT;

The C++ standard uses the word initialize to refer generally to all the various ways that a variable can be given an initial value. As there is no further wording to the contrary, I read the intent as being that atomic_flag guard(ATOMIC_FLAG_INIT), and use of ATOMIC_FLAG_INIT in constructor initializer lists, are also valid and not unspecified. I think the site you quote is reading too much into the single example. Examples in this standard are not normative, and in particular, that an example shows one way to do something does not mean that that is the only acceptable way to do it.

like image 2
zwol Avatar answered Nov 07 '22 00:11

zwol