atomic boolean initializes to true?

atomic boolean initializes to true?

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.

59 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

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.

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()').

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

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:

tbb::atomic( foo );

which still gave me garbage.

What I finally found that works is:

tbb::atomic foo( boost::initialized_value );

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

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.

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:

tbb::atomic( foo );

which still gave me garbage.

What I finally found that works is:

tbb::atomic foo( boost::initialized_value );

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?

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

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:

class Foo
{
public:
    Foo();

private:
    tbb::atomic m_atomic;
}

Foo::Foo()
    : m_atomic()
{
}

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 )

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

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, ...

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".

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!

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Anton Pegushin (Intel)

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'.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

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.

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).

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

"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?

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.

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?

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?

Quoting - Alexey Kukanov (Intel)

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".)

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.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Alexey Kukanov (Intel)

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.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Dmitriy V'jukov

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

I agree. So in case of passing rhs by value, compiler-generated copy constructor will not issue load-acquire for rhs but a simple load; then the tbb::atomic implementation will enforce load-acquire on the temporary copy, and store-release to the destination. Thanks Dmitry.

By the way, the fix is ready and will be published in the next development update.

"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." Indeed, the load may not be atomic (in the original sense of the word). I'm not concerned about acquire, which logically occurs after the load, so an intermediate copy wouldn't hurt, whatever its memory semantics. But my question about those unintended copies has not been addressed yet: can anything still go wrong?

Quoting - Raf Schietekat

I'm not concerned about acquire, which logically occurs after the load, so an intermediate copy wouldn't hurt, whatever its memory semantics.

I am not sure here.

On platforms where memory fences are separate and "global" (for example, Sparc, where acquire is membar #LoadLoad | #LoadStore) your reasoning is correct. But on platforms where fences are integrated part of memory operations and "local" (for example, Itanium, where acquire is ld.acq) your reasoning can be not correct.

Quoting - Raf Schietekat

But my question about those unintended copies has not been addressed yet: can anything still go wrong?

Can you elaborate a bit more here? What copies? Can you provide some examples of what are you talking about?

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

"But on platforms where fences are integrated part of memory operations and "local" (for example, Itanium, where acquire is ld.acq) your reasoning can be not correct." It makes no difference whether the fence is part of the instruction.

"Can you elaborate a bit more here? What copies? Can you provide some examples of what are you talking about?" See #13 and #18.

Quoting - Raf Schietekat

"Can you elaborate a bit more here? What copies? Can you provide some examples of what are you talking about?" See #13 and #18.

The interface of tbb::atomic<> is quite simple and minimalistic, I don't see any other places where similar problem can occur.

MSVC is able to call 2 user defined conversion operators implicitly, i.e. user defined cast operator followed by implicit constructor. But atomic<> doesn't have any (non copy) constructors...

Btw, I think that it's possible to just remove assignment operator which gets 'atomic const& rhs', because there is assignment operator which gets 'T rhs', and atomic has cast operator to T which makes load-acquire. So assignment of one atomic to another atomic will still work.... Hmm...

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Raf Schietekat

"But on platforms where fences are integrated part of memory operations and "local" (for example, Itanium, where acquire is ld.acq) your reasoning can be not correct." It makes no difference whether the fence is part of the instruction.

Do you mean the fact that TBB's load-acquire is stronger than C++0x's load-acquire and incurs unnecessary overheads in some cases?

I really don't know what are semantics of TBB's load-acquire. Where are formal semantics? :)

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

"Btw, I think that it's possible to just remove assignment operator which gets 'atomic const& rhs', because there is assignment operator which gets 'T rhs', and atomic has cast operator to T which makes load-acquire. So assignment of one atomic to another atomic will still work.... Hmm..." It would be nice to restore the symmetry between copy constructor and copy assignment operator, which should normally be defined together, but the former cannot be defined because it would also require a user-defined default constructor which would break the zero-initialisation expectations, and the latter is required because otherwise an incorrect implicit copy assignment operator would be defined.

"Do you mean the fact that TBB's load-acquire is stronger than C++0x's load-acquire and incurs unnecessary overheads in some cases?" Why stronger or more expensive? That's just how it works: release-store and load-acquire, each time logically in that order, whether integrated in an instruction or not. That means that copy-load-acquire, with the load from the otherwise invisible copied value, has the same semantics as the original load-acquire. Not so?

Quoting - Raf Schietekat

"Btw, I think that it's possible to just remove assignment operator which gets 'atomic const& rhs', because there is assignment operator which gets 'T rhs', and atomic has cast operator to T which makes load-acquire. So assignment of one atomic to another atomic will still work.... Hmm..." It would be nice to restore the symmetry between copy constructor and copy assignment operator, which should normally be defined together, but the former cannot be defined because it would also require a user-defined default constructor which would break the zero-initialisation expectations, and the latter is required because otherwise an incorrect implicit copy assignment operator would be defined.

Arghhh!

You are totally right!

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Raf Schietekat

"Do you mean the fact that TBB's load-acquire is stronger than C++0x's load-acquire and incurs unnecessary overheads in some cases?" Why stronger or more expensive? That's just how it works: release-store and load-acquire, each time logically in that order, whether integrated in an instruction or not. That means that copy-load-acquire, with the load from the otherwise invisible copied value, has the same semantics as the original load-acquire. Not so?

Yes, they are logically in that order. But this doesn't mean that they have global effect. Load-acquire is acquire only on that location that is loaded. At least this is how it works in C++0x. And they have reasons for such semantics. I think that "global" acquire can not be implemented efficiently on some architectures.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Dmitriy V'jukov

Quoting - Raf Schietekat

"Do you mean the fact that TBB's load-acquire is stronger than C++0x's load-acquire and incurs unnecessary overheads in some cases?" Why stronger or more expensive? That's just how it works: release-store and load-acquire, each time logically in that order, whether integrated in an instruction or not. That means that copy-load-acquire, with the load from the otherwise invisible copied value, has the same semantics as the original load-acquire. Not so?

Yes, they are logically in that order. But this doesn't mean that they have global effect. Load-acquire is acquire only on that location that is loaded. At least this is how it works in C++0x. And they have reasons for such semantics. I think that "global" acquire can not be implemented efficiently on some architectures.

So there's no causality chain of the message value for built-in fences? That's weird, are you sure about that?

Quoting - Dmitriy V'jukov

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!

I thought the standard said that zero initialization of non-POD types was not required by compilers. So I wouldn't call this a bug as much as a lacking feature in MSVC, that GCC gives us as a bonus. Although I'm not sure if g++ 4.x will do the zero initialization if you use -O3 flags for optimization. GCC has really done away with all the fluff, and is finally catching up on speed.

It does seem true that default-initialisation does not translate to zero-initialisation for non-POD objects, so for non-static atomics it would be necessary to explicitly assign a value (it is not enough to put my_atomic() in the initialiser list, etc.). So is this matter about incorrect zero-initialisation or about a misunderstanding of when zero-initialisation is meant to occur?

Quoting - Raf Schietekat

So there's no causality chain of the message value for built-in fences? That's weird, are you sure about that?

For sure!

N2798 1.10/7:

an atomic operation A that performs a release operation on an object M synchronizes with an
atomic operation B that performs an acquire operation on M and reads a value written by any
side effect in the release sequence headed by A.

I.e. acquire operation must be on the same variable on which there was release operation.

Although, for stand-alone fences there are different rules.

N2798 29.6/4:

An atomic operation A that is a release operation on an atomic object M synchronizes with an acquire fence
B if there exists some atomic operation X on M such that X is sequenced before B and reads the value
written by A or a value written by any side effect in the release sequence headed by A.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - robert.jay.gould

I thought the standard said that zero initialization of non-POD types was not required by compilers. So I wouldn't call this a bug as much as a lacking feature in MSVC, that GCC gives us as a bonus. Although I'm not sure if g++ 4.x will do the zero initialization if you use -O3 flags for optimization. GCC has really done away with all the fluff, and is finally catching up on speed.

ISO/IEC 14882:2003

8.5/7:

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

8.5/5:

To value-initialize an object of type T means:
- if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
- if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;

- if T is an array type, then each element is value-initialized;
- otherwise, the object is zero-initialized

To zero-initialize an object of type T means:
- if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
- if T is a non-union class type, each nonstatic data member and each base-class subobject is zeroinitialized;
- if T is a union type, the objects first named data member89) is zero-initialized;
- if T is an array type, each element is zero-initialized;
- if T is a reference type, no initialization is performed.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Raf Schietekat

It does seem true that default-initialisation does not translate to zero-initialisation for non-POD objects, so for non-static atomics it would be necessary to explicitly assign a value (it is not enough to put my_atomic() in the initialiser list, etc.). So is this matter about incorrect zero-initialisation or about a misunderstanding of when zero-initialisation is meant to occur?

ISO/IEC 14882:2003

8.5/7:

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

8.5/5:

To value-initialize an object of type T means:
- if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
- if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;

- if T is an array type, then each element is value-initialized;
- otherwise, the object is zero-initialized

To zero-initialize an object of type T means:
- if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
- if T is a non-union class type, each nonstatic data member and each base-class subobject is zeroinitialized;
- if T is a union type, the objects first named data member89) is zero-initialized;
- if T is an array type, each element is zero-initialized;
- if T is a reference type, no initialization is performed.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

"For sure!" I don't know, it seems very formal, but without examples I'm sceptical that the authors didn't trip over their own feet (they're only human, and who says enough iterations were made). For example, what is "an atomic operation B that performs an acquire operation on M *and* reads a value written by any side effect in the release sequence headed by A" (my *emphasis*): what kind of operation does all that at once? But perhaps more to the point: whatdo the hardware people say about this? Perhaps somebody from Intel about Itanium?

Sorry, I was looking at an earlier version of the C++ standard regarding initialisation; I'll check tonight whether I overlooked value-initialisation or whether it was added later.

Quoting - Dmitriy V'jukov

ISO/IEC 14882:2003

8.5/7:

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

Aha! Looking at atomic.h

bool specialization has no constructor.

void* specialization has no constructor.

Neither inherits from atomic_impl, this means:

- if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

Mystery solved?

"Mystery solved?" The referenced paragraph is about a class type *with* a user-declared constructor, so it is not applicable here.

Quoting - Raf Schietekat

"For sure!" I don't know, it seems very formal, but without examples I'm sceptical that the authors didn't trip over their own feet (they're only human, and who says enough iterations were made). For example, what is "an atomic operation B that performs an acquire operation on M *and* reads a value written by any side effect in the release sequence headed by A" (my *emphasis*): what kind of operation does all that at once? But perhaps more to the point: whatdo the hardware people say about this? Perhaps somebody from Intel about Itanium?

Load-acquire does all that at once:

X.load(memory_order_acquire);

It's acquire operation on X AND it reads some value from X.

I don't know which platform causes such rules, but I beleive there are reasons for it. It must be some platform with fences combined with load/store instructions (Itanium? ld.acq, st.rel).

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

"It's acquire operation on X AND it reads some value from X." That's not what the proposal says. See? What good are "formal semantics" if even their proponents don't understand them... :-)

Quoting - Raf Schietekat

"It's acquire operation on X AND it reads some value from X." That's not what the proposal says. See? What good are "formal semantics" if even their proponents don't understand them... :-)

They are not for general understanding, they are for subtle problem resolution :)

Why it's not what proposal says: "atomic operation B that performs an acquire operation on M and reads [some] value" ?

(1) "load(memory_order_acquire)" is atomic operation.

(2) it's acquire operation on memory location M

(3) it definitely reads some value

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

"Why it's not what proposal says" My criticism was that the proposal refers to an operation that acquires M itself and reads a value not necessarily on the same location (a "side effect"): what is that all about?

I think I'll have a look at this latest version before watching tonight's episode of Dexter (about a serial killer-killing serial killer), just to get into the mood... :-)

Quoting - Raf Schietekat

"Why it's not what proposal says" My criticism was that the proposal refers to an operation that acquires M itself and reads a value not necessarily on the same location (a "side effect"): what is that all about?

No, you mix up everything. Reading of value is not side-effect, it's evaluation. It's store operation which has modification of variable as side-effect. Term side-effect refers to modification of the value of the variable. It has nothing to do with "some other variable".

Quoting - Raf Schietekat

I think I'll have a look at this latest version before watching tonight's episode of Dexter (about a serial killer-killing serial killer), just to get into the mood... :-)

You better do it in reverse order :)

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Whether "reads" constitute side effects depends on the author. I think most programmers do not consider reads to be side effects. However, some of the theorists do consider reads to be an effect if the location is mutable.

See http://www.psrg.lcs.mit.edu/publications/Papers/fx91-report.pdffor a language with a fascinating "effect system" that checks side effects in a manner analogous to type checking. It considers reads to be side effects (page 3, top of right column).

My impression is that effect systems so far have been toosimple to do much good for real programming. I suspect that a truly useful needs to deal with effects on parts of objects and subscript patterns.

Quoting - Arch Robison (Intel)

Whether "reads" constitute side effects depends on the author.

We was talking about ISO C++, and it clearly defines term 'side-effect':

N2723 1.9/13:

Accessing an object designated by a volatile lvalue (3.10), modifying an object, calling a library I/O
function, or calling a function that does any of those operations are all side effects

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Sorry for the confusion, I should have repeated in full that apparently one operation "performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A", i.e., not necessarily on the same location (which would only be a special case), and I don't see what could be meant by that.

So, what about Itanium (forgetting about C++ for the moment): will an intermediate copy interfere with the release-acquire chain?

And I think I misunderstood release sequence (oops!), but let's keep this until later.

Quoting - Raf Schietekat

Sorry for the confusion, I should have repeated in full that apparently one operation "performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A", i.e., not necessarily on the same location (which would only be a special case), and I don't see what could be meant by that.

Things are crystal-clear here. There can be ONLY stores (or probably RMW operations, doesn't matter) to variable M in the release sequence headed by A. So, if B read value stored by any operation in the release sequence headed by A, then it reads value of variable M. Q. E. D.

N2723 1.10/6

A release sequence on an atomic object M is a maximal contiguous sub-sequence of side effects in the
modification order of M
, where the first operation is a release, and every subsequent operation
- is performed by the same thread that performed the release, or
- is a non-relaxed atomic read-modify-write operation.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Quoting - Raf Schietekat

So, what about Itanium (forgetting about C++ for the moment): will an intermediate copy interfere with the release-acquire chain?

From what I see in the documentation, intermediate store BREAKS release-acquire chain:

Acquire semantics imply that the instruction is made visible prior to all subsequent orderable instructions.

...

Data dependencies define the relationship between operations from the same processor that
have register or memory dependencies on the same address1. This relationship need only be
honored by the local processor
(i.e. the processor that executes the operations).

...

Data Dependency Does Not Establish MP Ordering

Although I don't get 100% confidence.

All about lock-free algorithms, multicore, scalability, parallel computing and related topics: http://www.1024cores.net

Pages

Leave a Comment

Please sign in to add a comment. Not a member? Join today