Does this cause any problems in the Rust view of the world? Will there be unanticipated issues for developers who have been getting along just fine so far unknowingly assuming that all operations are atomic, which they have been just by virtue of how the hardware works?
So, if you wanted an atomic type, you'd find it here: https://doc.rust-lang.org/stable/std/sync/atomic/index.html
In other words, no, it shouldn't be a problem, at least in my understanding.
The entire basis of the memory protection model of Rust is that it makes the above impossible in safe code. If you attempt to create such a situation, the compiler will fail to compile your code. Every single integer operation could stop being atomic and not a single line of safe rust would break.
That's already the case: simply reading a 64-bit integer in a 32-bit processor is not atomic, and that's already possible today.
But that's not a problem in Rust. How would you access the same 128-bit integer from more than one thread?
- You pass the thread a copy of the value: the thread's copy can't be accessed by any other thread, so whether or not it's atomic doesn't matter.
- You pass the thread a mutable reference (&mut) to the value: one of Rust's rules is that there can be only one &mut to a memory location, and while that &mut exists, nothing else can read or modify the value. Therefore it's the same case: only one thread can access the value, so whether or not it's atomic doesn't matter.
- You pass the thread a non-mutable reference (&) to the value: another of Rust's rules is that while any non-mutable reference exists, nothing can modify the value, so the reads not being atomic don't matter.
- You wrap the value in something like a Mutex: the Mutex has a lock which prevents concurrent accesses in the middle of a write.
- You use an Atomic version of the 128-bit integer: this one doesn't exist, so can't be used.
The main reason for all the interest in Rust is that the compiler protects you from many kinds of mistakes. Accessing the same variable from more than one thread, without using a lock or an atomic, is one of the things the compiler protects you from.
It's up to the synchronization primitives to ensure memory barriers are executed, not the language. So e.g. a Mutex<T> would execute a memory barrier when locking and unlocking. Rust then allows you to pass ownership in a trusted manner only, i.e. through those primitives.
Of course, you can also create an unsafe block and say: "Rust, trust me, I know what I'm doing, don't bother me, and these are the invariants I want you to enforce in safe code..." Mutexes are implemented this way.
But what I was asking was at runtime, when ownership transfers how does Rust ensure that writes to the value by the previous owner appear to the new owner before the transfer of ownership appears to the new owner?
You can statically guarantee that only one thread owns the object, but you can't statically guarantee the order in which the processor will apply the instructions your compiler generates, without barriers.
But the other person answered - you need to ensure that there is an explicit memory barrier yourself when you transfer the object.
To add to/clarify this: I don't think you can transfer an object to another thread in safe Rust code without a primitive that will handle the barriers for you. Static ownership tracing doesn't actually know what threads are, because it doesn't even need to.
To be clear, you need to write the code yourself, but the compiler won't let you transfer ownership between threads without doing so.
There is no way to compile your code without proving to the compiler that you're data race free.
The channels do this.
The language itself doesn't know about memory barriers. It gives you the tools for enforcing thread safety provided your synchronization abstractions have memory barriers in the right place.
LLVM supports arbitrary length integer types, regardless of the platform. Rust already has u64 and i64 on 32bit architectures.
But even if something fits in one register, a variable isn't thread safe anyway.
Rust DOES NOT allow a mutable reference to exist simultaneously with non-mutable ones.
For thread safety, you always need special types, which Rust provides:
True, this won't come up in a run-of-the-mill program, but if Rust is going to supplant C for OS work, or DataBase implementations, this is an issue to be aware of.
Succinctly: If you're in unsafe code, using 128-bit integers, you may experience behavior you've never seen before.
The compiler would force you to use an atomic. Once you realize no AtomicUint128 atomic exists, you may try to explicitly use unsafe to make things work, but even then that requires a lot more explicitness than just an unsafe block. You will realize your mistake some point here.
Unsafe in Rust doesn't just turn off all the checks. It gives you the power to circumvent the checks, but these circumventions are still individually explicit.
Anyway, u128 doesn't worsen this situation. You could already make thread safety mistakes with wider value types if you really really wanted to, just like u128.
The good thing about Rust is that you actively have to circumvent the languages safety checks with unsafe code to run into those problems.
And if you are using unsafe code, you should be aware of such low level considerations anyway, and would refrain from using types when not appropriate.
The consistency problems you describe apply to any datastructure after all.