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

Use atomic RMW for {mutex, rwlock, cond, srwlock}_get_or_create_id functions #2114

Merged
merged 4 commits into from
May 13, 2022

Conversation

cbeuw
Copy link
Contributor

@cbeuw cbeuw commented May 11, 2022

This is required for #1963

{mutex, rwlock, cond, srwlock}_get_or_create_id() currently checks whether an ID field is 0 using an atomic read, allocate one and get a new ID if it is, then write it in a separate atomic write. This is fine without weak memory. For instance, in pthread_mutex_lock which may be called by two threads concurrently, only one thread can read 0, create and then write a new ID, the later-run thread will always see the newly created ID and never 0.

    fn pthread_mutex_lock(&mut self, mutex_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        let kind = mutex_get_kind(this, mutex_op)?.check_init()?;
        let id = mutex_get_or_create_id(this, mutex_op)?;
        let active_thread = this.get_active_thread();

However, with weak memory behaviour, both threads may read 0: the first thread has to see 0 because nothing else was written to it, and the second thread is not guaranteed to observe the latest value, causing a duplicate mutex to be created and both threads "successfully" acquiring the lock at the same time.

This is a pretty typical pattern requiring the use of atomic RMWs. RMW always reads the latest value in a location, so only one thread can create the new mutex and ID, all others scheduled later will see the new ID.

@cbeuw
Copy link
Contributor Author

cbeuw commented May 11, 2022

ecx.read_scalar_at_offset_atomic(mutex_op, 4, ecx.machine.layouts.u32, AtomicReadOp::Acquire)

Why these changes? These operations should not synchronize. That should be done by the lock itself.

@RalfJung I agree, changed them back to Relaxed

src/sync.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

This is a pretty typical pattern requiring the use of atomic RMWs. RMW always reads the latest value in a location, so only one thread can create the new mutex and ID, all others scheduled later will see the new ID.

I agree, except that this here is Abstract Machine code that runs "on the meta level" so I am somewhat surprised it needs to play by the same rules. Using interpreter memory to store the state of these things seemed like a clever idea, but maybe it wasn't...

src/shims/posix/sync.rs Outdated Show resolved Hide resolved
src/sync.rs Outdated Show resolved Hide resolved
src/data_race.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

Thanks. :-)
@bors r+

@bors
Copy link
Contributor

bors commented May 13, 2022

📌 Commit 9e38dc4 has been approved by RalfJung

@bors
Copy link
Contributor

bors commented May 13, 2022

⌛ Testing commit 9e38dc4 with merge 3f111c1...

@bors
Copy link
Contributor

bors commented May 13, 2022

☀️ Test successful - checks-actions
Approved by: RalfJung
Pushing 3f111c1 to master...

@bors bors merged commit 3f111c1 into rust-lang:master May 13, 2022
@cbeuw cbeuw deleted the shim-rmw branch June 27, 2023 12:32
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

Successfully merging this pull request may close these issues.

3 participants