Yes, it is allowed. Output is not possible, but std::mutex
not necessarily sequentially sequential. Get / release enough to rule out this behavior.
std::mutex
not defined in the standard for consistent consistency, only
30.4.1.2 Mutex Types [thread.mutex.requirements.mutex]
11 Synchronization: previous unlock () operations on the same object must be synchronized with (1.10) this operation [lock ()].
Synchronize-with seems to be defined the same way as std::memory_order::release/acquire
(see this question ).
As far as I can see, the receive / release spinlock will conform to the standards for std :: mutex.
Great edit:
However, I do not think it means what you think (or what I thought). The conclusion is still impossible, since the semantics of receiving / issuing is enough to exclude it. This is a kind of subtle point that is best explained here . At first this seems impossible, but I think itβs right to be careful with such things.
From the standard unlock () is synchronized with lock (). This means that everything that happens before unlock () is visible after lock (). Happens earlier (hereinafter ->), this is a slightly strange attitude, which is better explained in the link above, but since in this example everything is mutually exclusive, everything works as you expect, i.e. const auto _1 = fA;
occurs before const auto _2 = fB;
and any changes visible to the thread when it unlock()
mutex are visible to the next thread, which is the lock()
mutex. It also has some expected properties, for example, if X occurs before Y, and Y - before Z, then X β Z, also if X occurs before Y, Y does not happen before X.
From here it is not difficult to see a contradiction that seems intuitively correct.
In short, there is a well-defined order of operations for each mutex β for example, for mutex A, threads A, C, D hold locks in a certain sequence. For stream D to print fA = 0, it must block mA before stream A, and vice versa for stream C. Thus, the blocking sequence for mA is D (mA) β A (mA) β C (mA).
For mutex B, the sequence should be C (mB) β B (mB) β D (mB).
But from the program we know C (mA) β C (mB), so that we can connect them together to get D (mA) β A (mA) β C (mA) β C (mB) β B (mB) β D (mb), which means D (mA) β D (mb). But the code also gives us D (mb) β D (mA), which is a contradiction, meaning that your observable result is impossible.
This result is not different for capture / release spin locks, I think everyone confused regular memory access for capture / release for a variable with access to a variable protected by spin lock. The difference is that with the help of spin-lock, read streams also perform comparison / exchange and write of a release, which is a completely different scenario for writing a single issue and reading a read.
If you use a sequential sequential spin lock, this will not affect the result. The only difference is that you could always categorically answer questions like βmutex A was locked to mutex Bβ from a separate thread that did not receive any of the locks. But for this example and most others, such a statement is useless, therefore, acquisition / release is the standard.