tokio-rs / loom

Concurrency permutation testing tool for Rust.
MIT License
2.13k stars 111 forks source link

cell::MutPtr isn't Send #294

Closed sporksmith closed 1 year ago

sporksmith commented 1 year ago

I'm writing a mutex guard that is Send when its inner type T is Send. It holds a MutPtr internally, which is !Send.

I'm currently working around it by wrapping in a newtype that implements Send when T is Send, but it'd be nice to avoid that by having loom::cell::MutPtr do this itself.

Similarly I suppose ConstPtr could implement Send and Sync when T is Sync? (Though that isn't blocking me personally)

hawkw commented 1 year ago

The MutPtr<T> type is equivalent to an *mut T, but one that participates in loom's access tracking, and should be treated as equivalent to a *mut T. A *mut T is never Send (or Sync), and therefore, a MutPtr<T> is also !Send[^1].

Much like a std::cell::UnsafeCell handing out *mut Ts, a loom::cell::UnsafeCell that hands out MutPtr<T>s is primarily intended as a building block for higher-level safe structures and is not aware of the safety invariants of the structures it's used to implement. Therefore, it cannot know under what conditions a pointer to data in that UnsafeCell can and cannot be safely shared. It's the responsibility of the code that uses an UnsafeCell to implement Send and/or Sync only when a value containing a pointer to that UnsafeCell can be shared safely. Just like how std::sync::MutexGuard must manually implement Send and Sync, it's correct for determining whether your guard type should be Send/Sync to be left up to the user code as well.

I hope that's a useful explanation of why loom's cell::MutPtr should not have Send or Sync implementations of its own. Let me know if you have any questions!

[^1]: In fact, the reason the MutPtr<T> type is !Send is because it contains a *mut T and doesn't have a manual Send implementation.

sporksmith commented 1 year ago

Fair enough; I suppose my thinking for why MutPtr ought to be Send if T is send is roughly equivalent to the argument that the rustnomicon strikes down re making pointers Send

Doing anything useful with a raw pointer requires dereferencing it, which is already unsafe. In that sense, one could argue that it would be "fine" for them to be marked as thread safe. However it's important that they aren't thread-safe to prevent types that contain them from being automatically marked as thread-safe. These types have non-trivial untracked ownership, and it's unlikely that their author was necessarily thinking hard about thread safety.