Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Understanding the new memory model

67 views
Skip to first unread message

Vir Campestris

unread,
Aug 3, 2015, 4:39:46 PM8/3/15
to
I've just been having an internal discussion over some multi-threaded
code. I can let it go for now - the target system only has one processor
- but I'm not sure I understand properly.

Their code was taking a boost::mutex then either updating or reading a
variable. It's a produce/consumer model, one writes and the other reads.
As far as I can tell they are relying on the mutex doing a memory order
consume on acquisition, and an release on release. I'd prefer them to
say so explicitly.

What I'm not sure about is what is meant by statements such as

"no reads in the current thread dependent on the value currently loaded
can be reordered before this load" from
http://en.cppreference.com/w/cpp/atomic/memory_order

Does it mean any variable at all? Or is it just volatiles? Or is it just
other atomics?

Andy

Victor Bazarov

unread,
Aug 3, 2015, 4:46:47 PM8/3/15
to
On 8/3/2015 4:39 PM, Vir Campestris wrote:
> I've just been having an internal discussion over some multi-threaded
> code. I can let it go for now - the target system only has one processor
> - but I'm not sure I understand properly.
>
> Their code was taking a boost::mutex then either updating or reading a
> variable. It's a produce/consumer model, one writes and the other reads.
> As far as I can tell they are relying on the mutex doing a memory order
> consume on acquisition, and an release on release. I'd prefer them to
> say so explicitly.

Who are "they"?

> What I'm not sure about is what is meant by statements such as
>
> "no reads in the current thread dependent on the value currently loaded
> can be reordered before this load" from
> http://en.cppreference.com/w/cpp/atomic/memory_order

First off, there is a whole bunch of examples after those statements.
Have you taken the time to read those? Do they not shed any light on
what you need to know?

> Does it mean any variable at all? Or is it just volatiles? Or is it just
> other atomics?

It means any variable, AFAICT.

V
--
I do not respond to top-posted replies, please don't ask

Chris Vine

unread,
Aug 3, 2015, 7:48:43 PM8/3/15
to
On Mon, 3 Aug 2015 21:39:21 +0100
Vir Campestris <vir.cam...@invalid.invalid> wrote:
> I've just been having an internal discussion over some multi-threaded
> code. I can let it go for now - the target system only has one
> processor
> - but I'm not sure I understand properly.
>
> Their code was taking a boost::mutex then either updating or reading
> a variable. It's a produce/consumer model, one writes and the other
> reads. As far as I can tell they are relying on the mutex doing a
> memory order consume on acquisition, and an release on release. I'd
> prefer them to say so explicitly.

It's a matter of language definition. std::mutex::lock() performs an
acquire operation (not just a consume). std::mutex::unlock() performs
a release operation. No load after an acquire can migrate before
the corresponding prior release on the same mutex object. No store
prior to the release can migrate after the corresponding subsequent
acquire on that mutex. What's the point of documenting that in the
code, unless this is a custom mutex object with unusual properties (in
which case documentation would be essential)?

As I understand it boost::mutex does the same as standard mutexes, but
in any event the header to your post indicates you are interested in the
standard memory model rather than what boost's mutexes happen to do.
Unsurprisingly, POSIX mutexes happen to have the same semantics as
standard mutexes so it would be astonishing if boost::mutex was
different.

> What I'm not sure about is what is meant by statements such as
>
> "no reads in the current thread dependent on the value currently
> loaded can be reordered before this load" from
> http://en.cppreference.com/w/cpp/atomic/memory_order
>
> Does it mean any variable at all? Or is it just volatiles? Or is it
> just other atomics?

So far as concerns the standard memory model and standard mutexes, it
applies to anything. That is the point of a mutex. And because a mutex
unlock (unless it is a custom mutex) has release semantics, it doesn't
only apply to dependent loads.

Chris

Chris Vine

unread,
Aug 3, 2015, 8:01:45 PM8/3/15
to
I should qualify that by saying that you have quoted from documentation
on std::memory_order. In relation to atomic variables, the
documentation refers to dependent loads relating to a memory operation
on the same atomic variable. However, your post was concerned with
mutexes, about which I responded.

Chris

Paavo Helde

unread,
Aug 4, 2015, 12:22:36 AM8/4/15
to
Vir Campestris <vir.cam...@invalid.invalid> wrote in
news:I9WdnYLDz47lTCLI...@brightview.co.uk:

> What I'm not sure about is what is meant by statements such as
>
> "no reads in the current thread dependent on the value currently loaded
> can be reordered before this load" from
> http://en.cppreference.com/w/cpp/atomic/memory_order
>
> Does it mean any variable at all? Or is it just volatiles? Or is it just
> other atomics?

"Volatile" has next to nothing to do with multithreading. It's meant for
things like memory-mapped hardware registers and signal handling and brief
googling suggests that it might not be sufficient even for these purposes
nowadays.

It's true that volatiles can be used to mimick atomics to some extent on
some platforms, but as we now have std::atomic which is both portable and
actually guaranteed to work, one can totally forget about the volatile
keyword, at least in regard of multithreading.

Cheers
Paavo

Vir Campestris

unread,
Aug 4, 2015, 4:38:28 PM8/4/15
to
On 03/08/2015 21:46, Victor Bazarov wrote:
> On 8/3/2015 4:39 PM, Vir Campestris wrote:
>> I've just been having an internal discussion over some multi-threaded
>> code. I can let it go for now - the target system only has one processor
>> - but I'm not sure I understand properly.
>>
>> Their code was taking a boost::mutex then either updating or reading a
>> variable. It's a produce/consumer model, one writes and the other reads.
>> As far as I can tell they are relying on the mutex doing a memory order
>> consume on acquisition, and an release on release. I'd prefer them to
>> say so explicitly.
>
> Who are "they"?

Ah. My mistake. In this case I meant the documentation; I wasn't clear.

>
>> What I'm not sure about is what is meant by statements such as
>>
>> "no reads in the current thread dependent on the value currently loaded
>> can be reordered before this load" from
>> http://en.cppreference.com/w/cpp/atomic/memory_order
>
> First off, there is a whole bunch of examples after those statements.
> Have you taken the time to read those? Do they not shed any light on
> what you need to know?
>

I did. It seems I missed some; perhaps it's easier to read them at home
in a room by myself than in the middle of an open office.

>> Does it mean any variable at all? Or is it just volatiles? Or is it just
>> other atomics?
>
> It means any variable, AFAICT.
>
Which is backed up by this:

"If an atomic store in thread A is tagged memory_order_release and an
atomic load in thread B from the same variable is tagged
memory_order_acquire, all memory writes (non-atomic and relaxed atomic)
that happened-before the atomic store from the point of view of thread
A, become visible side-effects in thread B, that is, once the atomic
load is completed, thread B is guaranteed to see everything thread A
wrote to memory."

Thanks

Andy

Vir Campestris

unread,
Aug 4, 2015, 4:40:13 PM8/4/15
to
On 04/08/2015 00:43, Chris Vine wrote:
> It's a matter of language definition. std::mutex::lock() performs an
> acquire operation (not just a consume). std::mutex::unlock() performs
> a release operation. No load after an acquire can migrate before
> the corresponding prior release on the same mutex object. No store
> prior to the release can migrate after the corresponding subsequent
> acquire on that mutex. What's the point of documenting that in the
> code, unless this is a custom mutex object with unusual properties (in
> which case documentation would be essential)?
>
> As I understand it boost::mutex does the same as standard mutexes, but
> in any event the header to your post indicates you are interested in the
> standard memory model rather than what boost's mutexes happen to do.
> Unsurprisingly, POSIX mutexes happen to have the same semantics as
> standard mutexes so it would be astonishing if boost::mutex was
> different.

I had a good read on the Boost documentation, and couldn't see anything
that states that they perform acquires and releases. I'll assume that
they do what the standard ones are documented to do.

Thanks

Andy

Chris Vine

unread,
Aug 4, 2015, 8:24:44 PM8/4/15
to
Technically I don't think that applies to mutexes because although a
mutex represents a memory location it does not of itself offer atomic
stores and loads. Non-normatively, for mutexes the result you mention
is offered by §1.10/5 of C++11:

"Note: For example, a call that acquires a mutex will perform
an acquire operation on the locations comprising the mutex.
Correspondingly, a call that releases the same mutex will perform a
release operation on those same locations. Informally, performing a
release operation on A forces prior side effects on other memory
locations to become visible to other threads that later perform a
consume or an acquire operation on A."

The normative (and more hard-to-read) requirement for mutexes is in
§30.4.1.2/11 and §30.4.1.2/25 ("synchronizes with") read with §1.10/11
and §1.10/12 ("happens before") and §1.10/13 ("visible side effect").

So far as concerns acquire and release operations on atomic variables,
these also provide synchronization, in that informally an operation with
acquire semantics is one which does not permit subsequent memory
operations to be advanced before it, and an operation with release
semantics is one which does not permit preceding memory operations to
be delayed past it, as regards the two threads synchronizing.
This synchronization with respect to those two threads extends beyond
just the memory location represent by the particular atomic variable.
It applies generally to all operations on memory locations shared by the
two threads performing the acquire/release on the particular atomic
variable (but does not provide full sequential consistency with respect
to atomic operations performed by other threads).

A consume operation on the other hand only synchronizes on the
particular atomic variable and its dependencies. In practice, no one
bothers about consume operations except in the most obscure
circumstances. Likewise the need for full sequential consistency with
other threads for atomic variables is also relatively uncommon,
nothwithstanding that it is the default for atomics.

Chris

0 new messages