11

I am reading the C++ memory model defined in n3485 and it talks about release/acquire semantics, which from what I understand, and also from the definitions given in this blog:

Acquire semantics is a property which can only apply to operations which read from shared memory, whether they are read-modify-write operations or plain loads. The operation is then considered a read-acquire. Acquire semantics prevent memory reordering of the read-acquire with any read or write operation which follows it in program order.

Release semantics is a property which can only apply to operations which write to shared memory, whether they are read-modify-write operations or plain stores. The operation is then considered a write-release. Release semantics prevent memory reordering of the write-release with any read or write operation which precedes it in program order.

is going to prevent reordering of reads/writes before or after the current read/write being done. The first (acquire) will make sure that the read currently being done is not reordered with any read/write coming after it, the latter (release) will make sure that the current write is not being reordered with read/write operations that come before it.

Now can it be said that std::mutex::lock will have acquire semantics and that std::mutex::unlock essentially has release semantics?

In the Standard I can find this under section

30.4.1.2 Mutex types [thread.mutex.requirements.mutex]

11 Synchronization: Prior unlock() operations on the same object shall synchronize with (1.10) this operation.

From what I understand synchronize with is not explicitly defined in the standard, however it seems to be a type of happens before relation looking at two statements being evaluated between two different threads, however, from my understanding of acquire/release semantics, this has more to do with memory reordering. synchronize with could also be called release/acquire semantics?

So do release/acquire semantics apply not only to reordering of load/store operations and also intra-thread interleaving of operations?

In the standard section about the memory-model it mostly talks about ordered relations in terms of two threads interleaving. This leaves open to interpretation as to whether this applies also to memory ordering.

Can anybody clarify?

Community
  • 1
  • 1
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • 2
    "Synchronizes with" is defined in [intro.multithread], 1.10, paragraph 8. – Pete Becker Mar 09 '13 at 17:26
  • @PeteBecker: True, but what is written there does not sound much like a "definition": "*Certain library calls synchronize with other library calls performed by another thread. For example, an atomic store-release synchronizes with a load-acquire that takes its value from the store (29.3).*" Words like "certain" and "for example" are not very formal. – Andy Prowl Mar 09 '13 at 17:28
  • @AndyProwl - you're right, it is kind of fuzzy. Not because of "certain", which is a short way of saying "some but not all", but because it doesn't quite say what that means. When the library requirements say that an operation "synchronizes with" another operation, that introduces an ordering that becomes part of the "inter-thread happens before" relationship. So there is no one place that formally defines "synchronizes with". It's a distributed definition: wherever the library specification says it happens is where it happens. – Pete Becker Mar 09 '13 at 17:38
  • @PeteBecker: I think maybe I understand what you are saying, but I'm not sure this makes it a "definition": it just says when it happens, but not what it is. Now such an extensional definition may make sense in some cases, but only an intensional one carries understanding. I could say that a "Foo" is "this guy, this guy, that guy, that guy, and that guy", because those are all and the only instances of "Foo". However, making a meaning out of "Foo" is almost impossible this way. – Andy Prowl Mar 09 '13 at 17:43
  • Conceptually a lock operation reads the state of an unlocked mutex and an unlock operation assigns the unlock state to a mutex. – curiousguy Jun 07 '19 at 05:07

1 Answers1

7

Now can it be said that std::mutex::lock will have acquire semantics and that std::mutex::unlock essentially has release semantics?

Yes, this is correct.

From what I understand synchronize with is not explicitly defined in the standard

Well, in theory Paragraph 1.10/8 is probably meant to give the definition of synchronizes with:

Certain library calls synchronize with other library calls performed by another thread. For example, an atomic store-release synchronizes with a load-acquire that takes its value from the store (29.3). [Note: ...]

On the other hand, this does not sound like a very formal definition. However, a better, though implicit one is indirectly given in Paragraph 1.10/10:

An evaluation A is dependency-ordered before an evaluation B if

— A performs a release operation on an atomic object M, and, in another thread, B performs a consume operation on M and reads a value written by any side effect in the release sequence headed by A, or

— for some evaluation X, A is dependency-ordered before X and X carries a dependency to B.

[ Note: The relation “is dependency-ordered before” is analogous to “synchronizes with”, but uses release/- consume in place of release/acquire. —end note ]

Since the "is analogous to" relationship is most often symmetric, I would say that the above definition of "is-dependency-ordered before" indirectly provides a definition of "synchronizes with" as well - although you might correctly object that notes are non-normative; still, this seems to be the intended definition.

My intuition of the synchronizes with relationship is that it occurs between a write (atomic) operation performed by one thread that stores a certain value and the first (atomic) operation that reads that value. That operation might as well be in the same thread.

If the two operations are on different threads, then the synchronizes-with relation establishes a cross-thread ordering on operations.

In the Standard I can find this under section

30.4.1.2 Mutex types [thread.mutex.requirements.mutex]

11 Synchronization: Prior unlock() operations on the same object shall synchronize with (1.10) this operation.

To me, this seems compatible with the interpretation given above. An operation with release semantics (unlock, store) will synchronize with an operation of acquire semantics (lock, load).

however, from my understanding of acquire/release semantics, this has more to do with memory reordering. synchronize with could also be called release/acquire semantics?

Release and acquire semantics describe the nature of some operations; the synchronizes-with relationship is (indeed) a relationship which is established between operations that have acquire or release semantics, in a well-defined way.

So in a sense, synchronizes-with is a consequence of the semantics of those operations, and we use those semantics to achieve the correct ordering of instructions and constraint the possible reordering that the CPU or the compiler will perform.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 1
    So a mutex acts much like a fence would, but the only difference that with a fence, multiple threads could still execute the code simultaneously, whereas a mutex makes that code mutually exclusive, right? – Tony The Lion Mar 09 '13 at 17:55
  • 2
    @TonyTheLion: I am still learning this stuff (more or less like you I guess) and I wrote this answer without much formal knowledge, to try and complement your partial understanding with my partial understanding. However, I do not think at the moment I have an understanding good enough to answer this last question – Andy Prowl Mar 09 '13 at 17:59
  • @TonyTheLion A mutex ordered operations. There is no "fence" in C++ that does what a mutex does. Forget "fences". – curiousguy Jun 07 '19 at 05:06