5 ms·
Mutexes etc ... exist in Java.
by Thaxll 3y ago
Mutexes etc ... exist in Java.
- jjnoakes 3y agoRight, but in rust, not using one is a compile time error. In Java (as you can see by the article), not using one is a silent bug at runtime.
- kaba0 3y agoThis is a heavily optimized system library - you don’t use mutexes here. Rust wouldn’t help here, if mutexes would be fine, they would have been used. Especially that this is the result of C++ and Java code simultaneously. Hell, it’s probably one area where rust’s benefits are a “hard sell” — you would have to constantly be in unsafe rust manipulating pointers manually as the compiler can’t reason statically about what a layer built on top does without a huge runtime cost (huge, as in you really don’t want to lock/unlock, or even refcount in these hot paths).
- lostmsu 3y agoHuh? This is exactly where Rust would help. In Rust the caller of the constructor would either have to add mutex if they needed concurrency, or just use the constructor without mutex overhead if they did not.
- ironmagma 3y agoIt's a compile-time cost instead of a runtime cost.
- gizmo686 3y ago99% of the time, the calling code trivially owns the array. If you are in a situation where the compiler cannot figure that out, then you need to deal with it regardless of what String does, because the exact same problem exists by the caller itself having a reference to the object.
- j16sdiz 3y agoJava compiler have a (not too bad) escape analysis engine. For something as low level as String intern/optimisation, it can be done in compile time
- Arnavion 3y agoNo idea why Thaxll and the other comments are mentioning mutexes. The equivalent (*) API to this Java API in Rust does exist; it's `String::from_utf8(Vec<u8>) -> String`. And the bug in TFA does not exist there. Since the signature consumes the `Vec<u8>` it's impossible for the caller or any other code to still have access to it to be able to modify it concurrently. Also consider the similar API `str::from_utf8(&[u8]) -> &str`. The bug in TFA does not exist here either. Since the signature takes a `&` borrow of the slice, it is not possible for anything else in the program to have a `&mut` borrow of that slice to be able to modify it concurrently. After the function returns other parts of the program could mutate the slice, but they would only be able to do after the `&str` that is derived from the slice is dropped. So once again nothing would be able to mutate the slice and observe the effects in the `&str` itself. All these "unable to do" are enforced at compile-time, because "consuming a value makes it unavailable to other parts of the program" and "cannot get a `&mut` to a value as long as a `&` from that value is still in scope" are all typesystem concepts. No mutexes or other runtime checks are involved. (*) "Equivalent" in that it's an API to convert a sequence of bytes into a string. The Rust API doesn't have the encoding thing of the Java API because the Rust String / str are required to be utf-8 internally. But if an exact equivalent of the Java API did exist in Rust, the signature would still be the same wrt consuming `Vec<u8>` / borrowing `[u8]`, so it doesn't change the overall point re: concurrent modification. Furthermore, concurrent modification would cause problems even with Rust Strings if it was possible, because it would allow a String / str to become invalid utf-8 after they'd already been checked to be valid utf-8, which Rust considers to be UB.
- invalidname 3y agoThe problem here is that we don't want a mutex. Once you have it the performance cost would apply in runtime. In fact, to write this code in rust you would need to write unsafe code to get around the problem where Rust forces you to write correct but inefficient code. This code is intentionally not thread-safe. This isn't so much a bug but an interesting thought experiment.
- kaba0 3y agoThe scenario I was imagining and commenting on was about “implementing a JVM with Java’s semantics in Rust”. Of course if we limit the language itself to safe Rust, we get data race freedom, but at a quite significant price for a high level language (it constraints possibly correct programs down a lot). But Rust would not help with relation to the primitives here at all (implemented in C++/Java).
- Sharlin 3y agoRust absolutely helps here because in Rust it’s simply impossible for someone else to mutate something concurrently to you holding a reference to it. Code equivalent to that in the article simply won’t compile in Rust. This is, like, the very point of Rust’s borrow system. You can share, xor you can mutate, but not both at the same time. This holds equally for single and multi-threaded code.
- ironmagma 3y agoIn safe Rust, that is. For unsafe Rust, I don't know exactly which bets are off but it's more than none.
- masklinn 3y agoIn unsafe rust this is a concurrent modification of an object with shared references, which is an UB.
- SpaghettiCthulu 3y agoUnless everyone is just holding pointers
- pjmlp 3y agoOnly for in-memory data structures under Rust's control, if it is related to OS IPC, Rust cannot do anything.
- jjnoakes 3y agoThis isn't true.
- josephcsible 3y agoYes, but Java will happily accept code that doesn't use them where it needs to, leading to bugs like this one. Rust catches that mistake at compile time instead.
- pjmlp 3y agoNot if the memory has been allocated on a shared memory segment, Rust has no control over what other processes might do.
- rocqua 3y agoSound Rust code would either make functions touching the shared memory marked unsafe, or would do a defensive copy out of shared memory.
- pjmlp 3y agoThat safe layer around unsafe still has no way to validate the consistency of the data.
- Dylan16807 3y agoIt can't proactively validate the data while it's in the shared memory. If you do your validation during accesses it's fine. If you copy the data out of the shared memory it's fine. Or you could use a mutex to protect the data between validation and use. If you're worried about another process editing the memory without taking the mutex, that's equivalent to worrying about other unsafe code editing the memory without taking the mutex. The solution is the same in both place: don't share memory with completely arbitrary code. When people compare languages and techniques, they (rightfully) assume you're not doing that.
- e-dant 3y agoWhat about Rust’s borrow checker (affine types) enforces the use of mutexes (or other sync prims) here?
- chowells 3y agoWhy would it need to? Rust's borrow checker makes it a compile-time error to share a mutable array between threads. No need for run time synchronization.
- e-dant 3y agoRight, that’s my understanding. But OP and a sibling thread here seem pretty sure about the mutex thing. I think there’s some nuance, but not in the general case. Shared memory, lazy statics in async blocks, and asynchronous constructors might have different initialization order mechanics that would require synchronization — but even then, the borrow checker would at least point it out
- Sharlin 3y agoWithout a mutex, you can’t even write code equivalent to that in the article because you cant mutably share as you pointed out. With a mutex you could – and the mutex would prevent data races (but not race conditions in general) – but indeed mutexes are a red herring here (at least in the specific sense of a runtime synchronization primitive). In Java you can’t synchronize defensively because synchronization requires that everybody who has access to the shared resource cooperates with you. And even if you could, you wouldn’t want to, not in this sort of a case. In Rust mutexes own the data they protect, and make it impossible for anyone to access the data without locking the mutex first, but again, an API like this would clearly not bother with dealing with mutexes but rather take a normal compile-time-checked borrow.
- kaba0 3y agoUnfortunately I can no longer edit my comment, please have a look at my reply: https://news.ycombinator.com/item?id=36690710 https://news.ycombinator.com/item?id=36690710