Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.

atomic boolean initializes to true?

kfriddile
Beginner
3,519 Views

According to my debugger, atomic variables are initialized to true. This was counter-intuitive to me since atomic numeric types are initialized to zero. What is the rationale for initializing to true? I'm using the0080605 release of TBB with VC9.

0 Kudos
58 Replies
kfriddile
Beginner
2,462 Views
Hmm, I appear to be having other problems I haven't had before, so perhaps my original post is irrelevent. I just created a simple atomic and it didn't initialize to zero either, instead containing a bogus uninitialized value. Something weird is going on.
0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,462 Views
Quoting - kfriddile
Hmm, I appear to be having other problems I haven't had before, so perhaps my original post is irrelevent. I just created a simple atomic and it didn't initialize to zero either, instead containing a bogus uninitialized value. Something weird is going on.

I don't know what was the intent, but it's declared as:
template // Primary template
struct atomic_base {
I my_value;
};

So atomic will not be initialized to zero automatically, it will be initialized to garbage, unless you use 'value-initialization' (i.e. 'atomic()').

0 Kudos
kfriddile
Beginner
2,462 Views
Quoting - Dmitriy V'jukov
I don't know what was the intent, but it's declared as:
template // Primary template
struct atomic_base {
I my_value;
};

So atomic will not be initialized to zero automatically, it will be initialized to garbage, unless you use 'value-initialization' (i.e. 'atomic()').

This seems contradictory to what James Reinders' TBB books says:

"You can rely on zero initialization to initialize an atomic to 0. To create an atomic with a specific value, default-construct it first, and afterword assign a value to it."

Anyways, I tried doing something like:

[cpp]tbb::atomic( foo );[/cpp]

which still gave me garbage.

What I finally found that works is:

[cpp]tbb::atomic foo( boost::initialized_value );[/cpp]

using the boost utility library (http://www.boost.org/doc/libs/1_36_0/libs/utility/value_init.htm).

0 Kudos
Alexey-Kukanov
Employee
2,462 Views
Quoting - kfriddile

This seems contradictory to what James Reinders' TBB books says:

"You can rely on zero initialization to initialize an atomic to 0. To create an atomic with a specific value, default-construct it first, and afterword assign a value to it."

Without more context, it's hard to say what the quotation really says (at the moment, I do not have the book next to me). I guess the context might be explanation of why there is no constructors for tbb::atomic. What I think is really meant there is that if you have memory that contains zeroes, and interpret it as tbb:atomic, it will work. How you get zero-initialized memory is another question; it could be a file-scope variable for example. Sorry if the book confused you.

0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,462 Views
Quoting - kfriddile

This seems contradictory to what James Reinders' TBB books says:

"You can rely on zero initialization to initialize an atomic to 0. To create an atomic with a specific value, default-construct it first, and afterword assign a value to it."

Anyways, I tried doing something like:

[cpp]tbb::atomic( foo );[/cpp]

which still gave me garbage.

What I finally found that works is:

[cpp]tbb::atomic foo( boost::initialized_value );[/cpp]

using the boost utility library (http://www.boost.org/doc/libs/1_36_0/libs/utility/value_init.htm).

Why it seems contradictory? Have you tried zero-initialization?

0 Kudos
kfriddile
Beginner
2,462 Views
Quoting - Dmitriy V'jukov

Why it seems contradictory? Have you tried zero-initialization?

Well, I'm using the atomic as a member variable of a class, so I need to initialize it in the initializer list. I tried initializing it like this:

[cpp]class Foo
{
public:
    Foo();

private:
    tbb::atomic m_atomic;
}

Foo::Foo()
    : m_atomic()
{
}[/cpp]

and the value was still uninitialized.

Replacing m_atomic() with m_atomic( tbb::atomic() ) gives the same result. The only thing I've found that works in this case is m_atomic( boost::initialized_value )

0 Kudos
RafSchietekat
Valued Contributor III
2,462 Views
Based on this evidence, my best guess is a compiler deficiency.

0 Kudos
RafSchietekat
Valued Contributor III
2,462 Views
Could you post a small self-contained program, though? There are still various other explanations, like erroneously overwriting memory, publishing access to Foo to another thread before Foo is fully constructed, ...

0 Kudos
Anton_Pegushin
New Contributor II
2,462 Views
The phrase in the Reinders book you're refering to has a different meaning. In the section "Why atomics have no constructors" James is explaning that file-scope atomics need to be initialized (zero-initialized) before any constructors can be called because these atomics can potentially be referenced before the calls to the constructors. And thats a very important property of the atomics. But this does not mean that class-scope atomic will get zero-initialized. In this same section James states: To create an atomic with specific value, defaul-construct it first and then assign a value to it (ex: atomic x; x = 2048;)

Quoting - kfriddile

Replacing m_atomic() with m_atomic( tbb::atomic() ) gives the same result. The only thing I've found that works in this case is m_atomic( boost::initialized_value )

Please note, that copy-construction for atomics is not atomic (I'm talking about m_atomic( tbb::atomic() )). The reason is again to be consistent with the important property of atomics explained above. For this, atomics don't have any constructors and copy constructor gets to be compiler generated. Please follow the rule of "default-construct and then assign".

0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,462 Views
Quoting - Raf Schietekat

Based on this evidence, my best guess is a compiler deficiency.

Yeah, it seems that it's a bug in MSVC (I've checked MSVC8 and 9). MSVC correctly zero-initializes POD types, but fails to do so for non-POD types w/o user-defined constructor (i.e. tbb::atomic<>).

g++ 3.4.6 correctly zero-initializes types like tbb::atomic<>.

Yikes!

0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,462 Views

But this does not mean that class-scope atomic will get zero-initialized

I think you are confusing term 'zero-initialized'. 'Zero-initialized' is a defined term in ISO C++, which refers to some forms of initialization like initialization of objects with static storage duration AND ALSO to initialization of primitive object members provided that object is declared with empty parenthesis, i.e. "()".

This means that tbb::atomic<>'s value MUST BE '0', if atomic is declared like:

tbb::atomic x = tbb::atomic();

Thats how I interpret Reinders' statement "You can rely on zero initialization to initialize an atomic to 0". I.e. if one express 'zero-initialization' (i.e. write "()"), then one can rely on atomic's value to be '0'.

0 Kudos
RafSchietekat
Valued Contributor III
2,462 Views

There's a bug in the copy assignment operator for atomic pointers (argument passed by value instead of by reference).

And what is the meaning of "such constructors could lead to accidental introduction of compiler temporaries" in the Reference Manual (I don't see how not defining a constructorwould preventthat). It might be nice to have a statement about zero-initialisation as well.

0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,462 Views
Quoting - Raf Schietekat

There's a bug in the copy assignment operator for atomic pointers (argument passed by value instead of by reference).

Why is it a bug?

It's better to pass simple objects 'by value' instead of 'by reference', because 'by reference' physically means 'by pointer', and 'by pointer' creates additional level of indirection (i.e. there will be additional dereference operation, unless function is inlined).

Quoting - Raf Schietekat

And what is the meaning of "such constructors could lead to accidental introduction of compiler temporaries" in the Reference Manual (I don't see how not defining a constructorwould preventthat). It might be nice to have a statement about zero-initialisation as well.

I think that *modern* compilers in release mode are able to eliminate all additional copies in both situations (with and without user-defined copy ctor). Although I think that *old* compilers feel better w/o user-defined copy ctor (i.e. are able to eliminate more temporary objects).

0 Kudos
RafSchietekat
Valued Contributor III
2,462 Views

"Why is it a bug?" Because it involves copy construction.

Perhaps we have been here before, but do you or does anyone know when additional copies can be generated in a way that might interfere with the user's intentions?

0 Kudos
Alexey-Kukanov
Employee
2,462 Views
Quoting - Raf Schietekat
There's a bug in the copy assignment operator for atomic pointers (argument passed by value instead of by reference).

And what is the meaning of "such constructors could lead to accidental introduction of compiler temporaries" in the Reference Manual (I don't see how not defining a constructorwould preventthat). It might be nice to have a statement about zero-initialisation as well.

I agree about the bug. Every other copy assignment including void*takes the right-hand argument by reference. It will not really lead to any issue on architectures where machine word store and load is atomic; nevertheless we should make it right.

The reference was fixed, but might be not yet updated at the TBB site. Now it says:

The copy constructor is not atomic because it is compiler generated. Introducing any non-trivial constructors might remove an important property of atomic:namespace scope instances are zero-initialized before namespace scope dynamic initializers run. This property can be essential for code executing early during program startup.

0 Kudos
Alexey-Kukanov
Employee
2,462 Views
Quoting - Raf Schietekat

Perhaps we have been here before, but do you or does anyone know when additional copies can be generated in a way that might interfere with the user's intentions?

In case of tbb::atomic copy construction, it will break atomicity for e.g. 64-bit types on 32-bit architecture. Does it answer your question, or you asked for something different?

0 Kudos
kfriddile
Beginner
2,462 Views
Quoting - Dmitriy V'jukov

I think you are confusing term 'zero-initialized'. 'Zero-initialized' is a defined term in ISO C++, which refers to some forms of initialization like initialization of objects with static storage duration AND ALSO to initialization of primitive object members provided that object is declared with empty parenthesis, i.e. "()".

This means that tbb::atomic<>'s value MUST BE '0', if atomic is declared like:

tbb::atomic x = tbb::atomic();

Thats how I interpret Reinders' statement "You can rely on zero initialization to initialize an atomic to 0". I.e. if one express 'zero-initialization' (i.e. write "()"), then one can rely on atomic's value to be '0'.

Yes, this is what was confusing me. When the book used the term "zero-initialized", I assumed it meant the standard's definition of the term. If I'm reading things right, it sounds like my assumption was correct and I was simply bitten by a compiler deficiency?

0 Kudos
RafSchietekat
Valued Contributor III
2,462 Views
In case of tbb::atomic copy construction, it will break atomicity for e.g. 64-bit types on 32-bit architecture. Does it answer your question, or you asked for something different?

My question is not about why such copies subvert atomicity, but about a clarification of #13 (my question and Dmitriy's answer), i.e., when may they unintentially occur? (Perhaps it's clearer to disregard "in a way that might interfere with the user's intentions".)

0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,462 Views
Quoting - Raf Schietekat

"Why is it a bug?" Because it involves copy construction.

Perhaps we have been here before, but do you or does anyone know when additional copies can be generated in a way that might interfere with the user's intentions?

I think I start getting your point. You mean that load from source object will NOT be atomic nor acquire. Right? I was confused by your statement that it's not Ok to declare assignment operator as receiving operand by value.

If assignment operator assumed to make atomic load acquire from source operand, then yes, receiving operand be value will break that guarantee.

0 Kudos
Dmitry_Vyukov
Valued Contributor I
2,144 Views

In case of tbb::atomic copy construction, it will break atomicity for e.g. 64-bit types on 32-bit architecture. Does it answer your question, or you asked for something different?

Assignment operator have to make load *acquire* from source operand, so it will also break on Itanium platform.

0 Kudos
Reply