Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why the code in listing_5.1.cpp works? #8

Open
liwei-cpp opened this issue Oct 31, 2020 · 2 comments
Open

Why the code in listing_5.1.cpp works? #8

liwei-cpp opened this issue Oct 31, 2020 · 2 comments

Comments

@liwei-cpp
Copy link

For the spinlock code, the lock logic is as follows:

void lock()
{
    while(flag.test_and_set(std::memory_order_acquire));
}

However, I don't understand why it works. The memory_order_acquire just make sure that all memory operations can not be moved before it. But flag.test_and_set contains a write operation after the read. And I think the write operation has relax memory order with the above code. That means if there are 2 threads that call the lock() simultaneously:

  • Firstly, thread1's flag.test_and_set returns false and writes true into flag, but with relax memory order.
  • Then, thread2's flag.test_and_set use acquire to read the data in flag, but it may not read the true set by thread 1, since thread 1's write is using relax memory order. Then thread2 may get false too, and it can also pass the lock and enter the critical section.

So I think it should use std::memory_order_acq_rel to make the logic correct.

Can you tell me what's wrong with my understanding?

Thanks

@v0d0m3r
Copy link

v0d0m3r commented Oct 31, 2020

I also have understanding similar to yours.
This book also has:

If you use read-modify-write operations, it’s important to pick which semantics you desire. In this case, you want both acquire and release semantics, so memory_order_acq_rel is appropriate, but you can use other orderings too. A fetch_sub operation with memory_order_acquire semantics doesn’t synchronize with anything, even though it stores a value, because it isn’t a release operation. Likewise, a store can’t synchronize with a fetch_or with memory_order_release semantics, because the read part of the fetch_or isn’t an acquire operation. Read-modify-write operations with memory_order_acq_rel semantics behave as both an acquire and a release, so a prior store can synchronize with such an operation, and it can synchronize with a subsequent load, as is the case in this example.

@anthonywilliams
Copy link
Owner

Read-modify-write operations such as test_and_set are still atomic, and the read and write portions are inseparable. Thus if you have two calls to flag.test_and_set then one will be first and one will be second. If the flag was initially not set, then the "first" call will return false and the "second" call will return true. This is independent of the memory ordering passed: it still works with memory_order_relaxed. The memory ordering specified provides the synchronization on other operations: those on the data protected by the mutex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants