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

Atomic as_mut_ptr #66705

Merged
merged 4 commits into from
Dec 1, 2019
Merged

Atomic as_mut_ptr #66705

merged 4 commits into from
Dec 1, 2019

Conversation

pitdicker
Copy link
Contributor

I encountered the following pattern a few times: In Rust we use some atomic type like AtomicI32, and an FFI interface exposes this as *mut i32 (or some similar libc type).

It was not obvious to me if a just transmuting a pointer to the atomic was acceptable, or if this should use a cast that goes through an UnsafeCell. See #66136 (comment)

Transmuting the pointer directly:

let atomic = AtomicI32::new(1);
let ptr = &atomic as *const AtomicI32 as *mut i32;
unsafe {
    ffi(ptr);
}

A dance with UnsafeCell:

let atomic = AtomicI32::new(1);
unsafe {
    let ptr = (&*(&atomic as *const AtomicI32 as *const UnsafeCell<i32>)).get();
    ffi(ptr);
}

Maybe in the end both ways could be valid. But why not expose a direct method to get a pointer from the standard library?

An as_mut_ptr method on atomics can be safe, because only the use of the resulting pointer is where things can get unsafe. I documented its use for FFI, and "Doing non-atomic reads and writes on the resulting integer can be a data race."

The standard library could make use this method in a few places in the WASM module.

cc @RalfJung as you answered my original question.

@rust-highfive
Copy link
Collaborator

r? @KodrAus

(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 Nov 24, 2019
@RalfJung
Copy link
Member

👍 for the basic functionality; I don't have time to review the docs I am afraid.

@pitdicker
Copy link
Contributor Author

I don't have time to review the docs I am afraid.

Thank you and no problem. I find the docs hard to write with all the macro stuff, I had to give it multiple tries to get something ok-ish. Screenshot of AtomicU64::as_mut_ptr() if it helps:
Schermafdruk van 2019-11-24 17-35-32

@pitdicker pitdicker changed the title Atomic mut ptr Atomic as_mut_ptr Nov 24, 2019
@jfrimmel
Copy link
Contributor

Since you can produce data races using this method it may be good to mark it unsafe. This would most likely not even introduce more unsafe-blocks, since FFI functions (as on of the primary use cases) always require such an unsafe environment.

@pitdicker
Copy link
Contributor Author

pitdicker commented Nov 24, 2019

Since you can produce data races using this method it may be good to mark it unsafe. This would most likely not even introduce more unsafe-blocks, since FFI functions (as on of the primary use cases) always require such an unsafe environment.

That was my first intention, and in a way I agree with you. But there is no way to cause data races with this function in safe Rust, and there is precedent in not marking a function unsafe just because it can lead to unsafety later on. Even all the similarly named as_mut_ptr methods on types such as slices and str can lead to unsafety, but are not unsafe by itself.

@jfrimmel
Copy link
Contributor

That was my first intention, and in a way I agree with you. But there is no way to cause data races with this function in safe Rust, and there is precedent in not marking a function unsafe just because it can lead to unsafety later on. Even all the similarly named as_mut_ptr methods types such as slices and str can lead to unsafety, but are not unsafe by itself.

The important difference is, that they all take a &mut self, whereas those new functions only take the &self receiver. I'm not totally sure, whether this fact can be "exploited" though.

@pitdicker
Copy link
Contributor Author

pitdicker commented Nov 24, 2019

The important difference is, that they all take a &mut self, whereas those new functions only take the &self receiver. I'm not totally sure, whether this fact can be "exploited" though.

It was maybe good to mention that atomics are a #[repr(transparent)] wrapper around UnsafeCell<some_integer>, and are guaranteed to have the same memory representation as the integer type the wrap. Because of the UnsafeCell returning *mut from &self this is ok.

@pitdicker
Copy link
Contributor Author

pitdicker commented Nov 24, 2019

Another point against making this method unsafe: it is doing nothing that can't already be done in safe (but maybe questionable) code.

When should I make a tracking issue? After review?

@hellow554
Copy link
Contributor

hellow554 commented Nov 25, 2019

Quoting get_mut:

pub fn get_mut(&mut self) -> &mut i64

Returns a mutable reference to the underlying integer.

This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data.

Please note the difference, that get_mut takes &mut self instead. Shouldn't as_mut_ptr do the same?

@RalfJung
Copy link
Member

No. The signature here matches UnsafeCell::get.

@pitdicker
Copy link
Contributor Author

Apparently the documentation should explain why this method is safe. I am no wordsmith, @jfrimmel and @hellow554 can I use you as test subjects 😄? Does this explanation make sense and is it enough?

Returning an *mut pointer from a shared reference to this atomic is safe because the atomic types work with interior mutability. All modifications of an atomic change the value through a shared reference, and can do so safely as long as they use atomic operations. Any use of the returned raw pointer requires an unsafe block and still has to uphold the same restriction: operations done on it must be atomic.

@pitdicker
Copy link
Contributor Author

Please note the difference, that get_mut takes &mut self instead. Shouldn't as_mut_ptr do the same?

The &mut returned by get_mut has no restrictions on how you use the resulting integer because the signature guarantees there are no other threads in play.

The *mut returned by as_mut_ref can exist while the atomic is still in use by multiple threads, and can only used by unsafe code or an external library and must uphold the restriction to use atomic operations.

Copy link
Contributor

@KodrAus KodrAus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me! I’ll open up a tracking issue that we can reference in here then r=me

src/libcore/sync/atomic.rs Outdated Show resolved Hide resolved
src/libcore/sync/atomic.rs Outdated Show resolved Hide resolved
@KodrAus KodrAus mentioned this pull request Nov 30, 2019
6 tasks
@pitdicker
Copy link
Contributor Author

pitdicker commented Nov 30, 2019

Thank you!
r+=KodrAus

@pitdicker
Copy link
Contributor Author

Maybe this one: @bors r=KodrAus

@bors
Copy link
Contributor

bors commented Nov 30, 2019

@pitdicker: 🔑 Insufficient privileges: Not in reviewers

@RalfJung
Copy link
Member

@bors r=KodrAus

@bors
Copy link
Contributor

bors commented Nov 30, 2019

📌 Commit d34090a has been approved by KodrAus

@bors
Copy link
Contributor

bors commented Nov 30, 2019

🌲 The tree is currently closed for pull requests below priority 1000, this pull request will be tested once the tree is reopened

@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 Nov 30, 2019
Centril added a commit to Centril/rust that referenced this pull request Nov 30, 2019
Atomic as_mut_ptr

I encountered the following pattern a few times: In Rust we use some atomic type like `AtomicI32`, and an FFI interface exposes this as `*mut i32` (or some similar `libc` type).

It was not obvious to me if a just transmuting a pointer to the atomic was acceptable, or if this should use a cast that goes through an `UnsafeCell`. See rust-lang#66136 (comment)

Transmuting the pointer directly:
```rust
let atomic = AtomicI32::new(1);
let ptr = &atomic as *const AtomicI32 as *mut i32;
unsafe {
    ffi(ptr);
}
```

A dance with `UnsafeCell`:
```rust
let atomic = AtomicI32::new(1);
unsafe {
    let ptr = (&*(&atomic as *const AtomicI32 as *const UnsafeCell<i32>)).get();
    ffi(ptr);
}
```

Maybe in the end both ways could be valid. But why not expose a direct method to get a pointer from the standard library?

An `as_mut_ptr` method on atomics can be safe, because only the use of the resulting pointer is where things can get unsafe. I documented its use for FFI, and "Doing non-atomic reads and writes on the resulting integer can be a data race."

The standard library could make use this method in a few places in the WASM module.

cc @RalfJung as you answered my original question.
bors added a commit that referenced this pull request Dec 1, 2019
Rollup of 9 pull requests

Successful merges:

 - #66612 (Initial implementation of or-pattern usefulness checking)
 - #66705 (Atomic as_mut_ptr)
 - #66759 (impl TrustedLen for vec::Drain)
 - #66858 (Use LLVMAddAnalysisPasses instead of Rust's wrapper)
 - #66870 (SimplifyArmIdentity only for locals with the same type)
 - #66883 (rustc_typeck: gate AnonConst's generics on feature(const_generics).)
 - #66889 (Make python-generated source files compatible with rustfmt)
 - #66894 (Remove unneeded prelude imports in libcore tests)
 - #66895 (Feature gating *declarations* => new crate `rustc_feature`)

Failed merges:

 - #66905 (rustc_plugin: Remove some remaining plugin features)

r? @ghost
@bors bors merged commit d34090a into rust-lang:master Dec 1, 2019
@pitdicker pitdicker deleted the atomic_mut_ptr branch December 1, 2019 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants