-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Rust does not guarantee thread-safety #26215
Comments
Could you please explain where you believe the thread-unsafety lies? If you change the last line to
You can see the real reason for the panic: thread ' ' panicked at 'calledOption::unwrap() on a None value', /home/rustbuild/src/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libcore/option.rs:362playpen: application terminated with error code 101 Playground URL: https://play.rust-lang.org/?gist=13ac9c8daa26803dd005&version=stable |
That panic seems to result from the playground environment. |
here's a version without
how did you come to that conclusion? you released the lock to the mutex by leaving the scope of the first mutex guard. Therefore another thread was able to lock the mutex and modify the inner content. That has nothing to do with thread safety, this is perfectly fine. |
Clearly the problem is that the term "thread safety" is not a formal term with a precise definition. What Rust guarantees is an absence of data-races, nothing more, nothing less. (That said, the std lib does make some effort to make other kinds of errors somewhat harder to come across, such as access to "inconsistent" data that can result from panics, but there are no guarantees there). |
Actually, after some discussion with @aturon, we agreed that Rust gives you more than data-race freedom, but it's hard to succinctly state the property that it gives you. In particular, we guarantee you that something like:
always prints 3, which Java does not, even if both guarantee low-level data-races (in Java's case, through the VM, in Rust's case, through the type system). |
Another way to phrase it is that we allow you to define the "consistency boundaries" -- unless you opt-in using |
FWIW, I agree that the current wording on the main page ("guarantees thread safety") is probably a bit too strong. It used to just talk about data races, but we felt that was too weak -- it didn't really capture the full benefits of We can always make the home page bullets link to various of the blog posts we've been publishing to explain Rust's vision in more detail, and that can help clarify the extent of the benefits of each. |
NB. Rust does actually provide tools that allow this sort of code to be guaranteed-correct. The problem here is there's high-level semantics imposed on a plain integer, which is far too general for the compiler to know if the code is correct (for all the compiler knows it may be fine for the value to change between the two critical sections). Using a custom type allows imposing higher-order constraints. In this case, moving (instead of copying) is enough to guarantee the semantics you want: use std::sync::{Arc, Mutex};
use std::thread;
#[derive(PartialEq, Eq)]
pub struct Value {
x: i32
}
impl Value {
pub fn new(x: i32) -> Value { Value { x: x } }
pub fn increment(&mut self) { self.x += 1; }
}
fn main() {
let p = Arc::new(Mutex::new(Value::new(0)));
for _ in 1..100 {
let x = p.clone();
thread::spawn(move || {
let n;
{
let mut x_ = x.lock().unwrap();
x_.increment();
n = *x_;
}
thread::sleep_ms(50);
{
// because Rust guarantees thread-safety, we can safely assume x hasn't been changed
let x_ = x.lock().unwrap();
if *x_ != n {
panic!();
}
}
});
}
thread::sleep_ms(2000);
}
https://play.rust-lang.org/?gist=782b015afec0d2ea63bc&version=stable&run=1 Don't get me wrong: the original code is still doesn't do what you intended (i.e. Rust isn't automatically guarantee the "thread-safety" that that code wants), but still it is nice that it is possible to do so. |
@nikomatsakis I think Rust's thread safety guarantees shine the strongest in generic code. Specifically: in safe code, any function generic over As far as I know, the only time Java guarantees anything anywhere near that strong is during Of course, there is nothing stopping people from writing an I think this is the key to why it feels like Rust is enforcing something stronger than data race safety. But I have no idea how to express it succinctly. It would help if the Rust standard library did define the hypothetical trait I mention above somewhere ( |
@pythonesque More generally, in Rust (unlike Java/C#) you can't have races (i.e. nondeterministic results due to parallelism) on ordinary fields. You can have (safe) data races if you opt-in using |
As rust-lang/rust#26215 and others have pointed out, "thread safety" is a bad way to phrase this. When talking to people about Rust, the thing they've found most striking is compile-time errors, which is really unique to us, so it seems like a good proxy here.
Submitted rust-lang/prev.rust-lang.org#213 |
we've decided this is WONTFIX rust-lang/prev.rust-lang.org#213 (comment) |
According to the Rust website, Rust guarantees thread safety. However, I managed to write a program that appears to be thread-unsafe while it does not use external resources or
unsafe
:Expected output:
No output and exit status 0.
Actual output:
This program panics.
The text was updated successfully, but these errors were encountered: