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

Relax promises about condition variable. #76932

Merged
merged 1 commit into from
Sep 25, 2020

Conversation

m-ou-se
Copy link
Member

@m-ou-se m-ou-se commented Sep 19, 2020

For quite a while now, there have been plans to at some point use parking_lot or some other more efficient implementation of mutexes and condition variables. Right now, Mutex and CondVar both Box the 'real' mutex/condvar inside, to give it a stable address. This was done because implementations like pthread and Windows critical sections may not be moved. More efficient implementations based on futexes, WaitOnAddress, Windows SRW locks, parking_lot, etc. may be moved (while not borrowed), so wouldn't need boxing.

However, not boxing them (which would be great goal to achieve), breaks a promise std currently makes about CondVar. CondVar promises to panic when used with different mutexes, to ensure consistent behaviour on all platforms. To this check, a mutex is considered 'the same' if the address of the 'real mutex' in the Box is the same. This address doesn't change when moving a std::mutex::Mutex object, effectively giving it an identity that survives moves of the Mutex object. If we ever switch to a non-boxed version, they no longer carry such an identity, and this check can no longer be made.

Four options:

  1. Always box mutexes.
  2. Add a MutexId similar to ThreadId. Making mutexes bigger, and making it hard to ever have a const fn new for them.
  3. Making the requirement of CondVar stricter: panic if the Mutex object itself moved.
  4. Making the promise of CondVar weaker: don't promise to panic.

1, 2, and 3 seem like bad options. This PR updates the documentation for 4.

This allows for futex or thread parking based implementations in the
future.
@rust-highfive
Copy link
Collaborator

r? @sfackler

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 19, 2020
@sfackler sfackler added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Sep 19, 2020
@sfackler
Copy link
Member

I agree that this is the best option - I can't imagine that anyone is actually depending on this exact behavior. I'm going to run this through libs FCP though just in case!

@rfcbot fcp merge

@rfcbot
Copy link

rfcbot commented Sep 19, 2020

Team member @sfackler has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Sep 19, 2020
@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Sep 25, 2020
@rfcbot
Copy link

rfcbot commented Sep 25, 2020

🔔 This is now entering its final comment period, as per the review above. 🔔

@sfackler
Copy link
Member

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Sep 25, 2020

📌 Commit 26d6081 has been approved by sfackler

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 25, 2020
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 25, 2020
…as-schievink

Rollup of 15 pull requests

Successful merges:

 - rust-lang#76932 (Relax promises about condition variable.)
 - rust-lang#76973 (Unstably allow assume intrinsic in const contexts)
 - rust-lang#77005 (BtreeMap: refactoring around edges)
 - rust-lang#77066 (Fix dest prop miscompilation around references)
 - rust-lang#77073 (dead_code: look at trait impls even if they don't contain items)
 - rust-lang#77086 (Include libunwind in the rust-src component.)
 - rust-lang#77097 (Make [].as_[mut_]ptr_range() (unstably) const.)
 - rust-lang#77106 (clarify that `changelog-seen = 1` goes to the beginning of config.toml)
 - rust-lang#77120 (Add `--keep-stage-std` to `x.py` for keeping only standard library artifacts)
 - rust-lang#77126 (Invalidate local LLVM cache less often)
 - rust-lang#77146 (Install std for non-host targets)
 - rust-lang#77155 (remove enum name from ImplSource variants)
 - rust-lang#77176 (Removing erroneous semicolon in transmute documentation)
 - rust-lang#77183 (Allow multiple allow_internal_unstable attributes)
 - rust-lang#77189 (Remove extra space from vec drawing)

Failed merges:

r? `@ghost`
@bors bors merged commit a835af1 into rust-lang:master Sep 25, 2020
@rustbot rustbot added this to the 1.48.0 milestone Sep 25, 2020
JohnTitor added a commit to JohnTitor/rust that referenced this pull request Oct 1, 2020
…ex, r=dtolnay

Split sys_common::Mutex in StaticMutex and MovableMutex.

The (unsafe) `Mutex` from `sys_common` had a rather complicated interface. You were supposed to call `init()` manually, unless you could guarantee it was neither moved nor used reentrantly.

Calling `destroy()` was also optional, although it was unclear if 1) resources might be leaked or not, and 2) if `destroy()` should only be called when `init()` was called.

This allowed for a number of interesting (confusing?) different ways to use this `Mutex`, all captured in a single type.

In practice, this type was only ever used in two ways:

1. As a static variable. In this case, neither `init()` nor `destroy()` are called. The variable is never moved, and it is never used reentrantly. It is only ever locked using the `LockGuard`, never with `raw_lock`.

2. As a `Box`ed variable. In this case, both `init()` and `destroy()` are called, it will be moved and possibly used reentrantly.

No other combinations are used anywhere in `std`.

This change simplifies things by splitting this `Mutex` type into two types matching the two use cases: `StaticMutex` and `MovableMutex`.

The interface of both new types is now both safer and simpler. The first one does not call nor expose `init`/`destroy`, and the second one calls those automatically in its `new()` and `Drop` functions. Also, the locking functions of `MovableMutex` are no longer unsafe.

---

This will also make it easier to conditionally box mutexes later, by moving that decision into sys/sys_common. Some of the mutex implementations (at least those of Wasm and 'sys/unsupported') are safe to move, so wouldn't need a box. ~~(But that's blocked on  rust-lang#76932 for now.)~~ (See rust-lang#77380.)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Oct 1, 2020
…ex, r=dtolnay

Split sys_common::Mutex in StaticMutex and MovableMutex.

The (unsafe) `Mutex` from `sys_common` had a rather complicated interface. You were supposed to call `init()` manually, unless you could guarantee it was neither moved nor used reentrantly.

Calling `destroy()` was also optional, although it was unclear if 1) resources might be leaked or not, and 2) if `destroy()` should only be called when `init()` was called.

This allowed for a number of interesting (confusing?) different ways to use this `Mutex`, all captured in a single type.

In practice, this type was only ever used in two ways:

1. As a static variable. In this case, neither `init()` nor `destroy()` are called. The variable is never moved, and it is never used reentrantly. It is only ever locked using the `LockGuard`, never with `raw_lock`.

2. As a `Box`ed variable. In this case, both `init()` and `destroy()` are called, it will be moved and possibly used reentrantly.

No other combinations are used anywhere in `std`.

This change simplifies things by splitting this `Mutex` type into two types matching the two use cases: `StaticMutex` and `MovableMutex`.

The interface of both new types is now both safer and simpler. The first one does not call nor expose `init`/`destroy`, and the second one calls those automatically in its `new()` and `Drop` functions. Also, the locking functions of `MovableMutex` are no longer unsafe.

---

This will also make it easier to conditionally box mutexes later, by moving that decision into sys/sys_common. Some of the mutex implementations (at least those of Wasm and 'sys/unsupported') are safe to move, so wouldn't need a box. ~~(But that's blocked on  rust-lang#76932 for now.)~~ (See rust-lang#77380.)
@RalfJung
Copy link
Member

RalfJung commented Oct 3, 2020

So all these other condvar implementations still guarantee safety even when used with different mutexes over time?

Pthread Docs say

The effect of using more than one mutex for concurrent pthread_cond_timedwait() or pthread_cond_wait() operations on the same condition variable is undefined

This means at least for pthread condvars, we have to do this check.

@m-ou-se
Copy link
Member Author

m-ou-se commented Oct 3, 2020

So all these other condvar implementations still guarantee safety even when used with different mutexes over time?

Nope! But some do, and those can have their mutexes unboxed.

This means at least for pthread condvars, we have to do this check.

Indeed!

Pthread mutexes/condvars cannot be unboxed in Rust, and this check should stay for those. That's why it still says "may panic".

(The check could be slightly relaxed. Pthread and friends do allow using different mutexes with a single condvar, only not at the same time.)

@RalfJung
Copy link
Member

RalfJung commented Oct 3, 2020

Thanks for clarifying @m-ou-se :)

bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 4, 2020
…tex, r=dtolnay

Unbox mutexes and condvars on some platforms

Both mutexes and condition variables contained a Box containing the actual os-specific object. This was done because moving these objects may cause undefined behaviour on some platforms.

However, this is not needed on Windows[1], Wasm[2], cloudabi[2], and 'unsupported'[3], were the box was only needlessly making them less efficient.

This change gets rid of the box on those platforms.

On those platforms, `Condvar` can no longer verify it is only used with one `Mutex`, as mutexes no longer have a stable address. This was addressed and considered acceptable in rust-lang#76932.

[1]\: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
[2]\: These are just a single atomic integer together with futex wait/wake calls/instructions.
[3]\: The `unsupported` platform doesn't support multiple threads at all.
@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. to-announce Announce this issue on triage meeting and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Oct 5, 2020
@spastorino spastorino removed the to-announce Announce this issue on triage meeting label Oct 8, 2020
@m-ou-se m-ou-se deleted the condvar-promise branch November 19, 2020 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants