Request: Make std:馃搼:Freeze pub again 路 Issue #60715 路 rust-lang/rust (original) (raw)

I had heard tell of Freeze but didn't really know what it was until today. swym, a hybrid transactional memory library, has an accidental reimplementation of Freeze using optin_builtin_traits. Unfortunately optin_builtin_traits is the only feature keeping swym on nightly.

The ticket that removed Freeze doesn't have much of an explanation for why it was removed so I'm assuming it was a lack of motivating use cases.

Use Case

swym::tcell::TCell::borrow returns snapshots of data - shallow memcpys - that are guaranteed to not be torn, and be valid for the duration of the transaction. Those snapshots hold on to the lifetime of the TCell in order to act like a true reference, without blocking updates to the TCell from other threads. Other threads promise to not mutate the value that had its snapshot taken until the transaction has finished, but are permitted to move the value in memory.

This works great for a lot of types, but fails miserably when UnsafeCells are directly stored in the type.

let x = TCell::new(Mutex::new("hello there".to_owned()));

// .. inside a transaction let shallow_copy = x.borrow(tx, Default::default())?; // locking a shallow copy of a lock... is not really a lock at all! *shallow_copy.lock().unwrap() = "uh oh".to_owned();

Even if Mutex internally had a pointer to the "actual" mutex data structure, the above example would still be broken because the String is deallocated through the shallow copy. The String contained in the TCell would point to freed memory.

Note that having TCell::borrow require Sync would still allow the above broken example to compile.

Freeze

If swym::tcell::TCell::borrow could require Freeze then this would not be an issue as the Mutex type is definitely not Freeze.

pub(crate) unsafe auto trait Freeze {}

impl<T: ?Sized> !Freeze for UnsafeCell {} unsafe impl<T: ?Sized> Freeze for PhantomData {} unsafe impl<T: ?Sized> Freeze for *const T {} unsafe impl<T: ?Sized> Freeze for *mut T {} unsafe impl<T: ?Sized> Freeze for &T {} unsafe impl<T: ?Sized> Freeze for &mut T {}

Shallow immutability is all that is required for TCell::borrow to work. Sync is only necessary to make TCell Sync.

Alternatives

struct MyType { value: other_crate::OtherType } unsafe impl MyFreeze for MyType {}