left_right/
aliasing.rs

1//! Types that aid in aliasing values across the two left-right data copies.
2//!
3//! This module primarily revolves around the [`Aliased`] type, and its associated [`DropBehavior`]
4//! trait. The basic flow of using it is going to go as follows.
5//!
6//! In general, each value in your data structure should be stored wrapped in an `Aliased`, with an
7//! associated type `D` that has `DropBehavior::DO_DROP` set to `false`. In
8//! [`Absorb::absorb_first`], you then simply drop any removed `Aliased<T, D>` as normal. The
9//! backing `T` will not be dropped.
10//!
11//! In [`Absorb::absorb_second`], you first cast your datastructure from
12//!
13//! ```rust,ignore
14//! &mut DataStructure<Aliased<T, D>>
15//! ```
16//!
17//! to
18//!
19//! ```rust,ignore
20//! &mut DataStructure<Aliased<T, D2>>
21//! ```
22//!
23//! where `<D2 as DropBehavior>::DO_DROP` is `true`. This time, any `Aliased<T>` that you drop
24//! _will_ drop the inner `T`, but this should be safe since the only other alias was dropped in
25//! `absorb_first`. This is where the invariant that `absorb_*` is deterministic becomes extremely
26//! important!
27//!
28//! Sounds nice enough, right? Well, you have to be _really_ careful when working with this type.
29//! There are two primary things to watch out for:
30//!
31//! ## Mismatched dropping
32//!
33//! If `absorb_first` and `absorb_second` do not drop _exactly_ the same aliased values for a given
34//! operation from the oplog, unsoundness ensues. Specifically, what will happen is that
35//! `absorb_first` does _not_ drop some aliased `t: T`, but `absorb_second` _does_. Since
36//! `absorb_second` _assumes_ that `t` no longer has any alises (it expects that `absorb_first` got
37//! rid of such an alias), it will drop the `T`. But that `T` is still in the "other" data copy,
38//! and may still get accessed by readers, who will then be accessing a dropped value, which is
39//! unsound.
40//!
41//! While it might seem like it is easy to ensure that `absorb_first` and `absorb_second` do the
42//! same thing, it is not. A good example of this is non-deterministic (likely malicious)
43//! implementations of traits that you'd _expect_ to be deterministic like `Hash` or `Eq`. Imagine
44//! someone writes an implementation like:
45//!
46//! ```rust
47//! use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
48//! static SNEAKY: AtomicBool = AtomicBool::new(false);
49//!
50//! #[derive(Eq, Hash)]
51//! struct Sneaky(Vec<u8>);
52//! impl PartialEq for Sneaky {
53//!     fn eq(&self, other: &Self) -> bool {
54//!         if SNEAKY.swap(false, SeqCst) {
55//!             false
56//!         } else {
57//!             self.0 == other.0
58//!         }
59//!     }
60//! }
61//! ```
62//!
63//! Will your `absorb_*` calls still do the same thing? If the answer is no, then your
64//! datastructure is unsound.
65//!
66//! Every datastructure is different, so it is difficult to give good advice on how to achieve
67//! determinism. My general advice is to never call user-defined methods in `absorb_second`. Call
68//! them in `absorb_first`, and use the `&mut O` to stash the results in the oplog itself. That
69//! way, in `absorb_second`, you can use those cached values instead. This may be hard to pull off
70//! for complicated datastructures, but it does tend to guarantee determinism.
71//!
72//! If that is unrealistic, mark the constructor for your data structure as `unsafe`, with a safety
73//! comment that says that the inner types must have deterministic implementations of certain
74//! traits. It's not ideal, but hopefully consumers _know_ what types they are using your
75//! datastructures with, and will be able to check that their implementations are indeed not
76//! malicious.
77//!
78//! ## Unsafe casting
79//!
80//! The instructions above say to cast from
81//!
82//! ```rust,ignore
83//! &mut DataStructure<Aliased<T, D>>
84//! ```
85//!
86//! to
87//!
88//! ```rust,ignore
89//! &mut DataStructure<Aliased<T, D2>>
90//! ```
91//!
92//! That cast is unsafe, and rightly so! While it is _likely_ that the cast is safe, that is far
93//! from obvious, and it's worth spending some time on why, since it has implications for how you
94//! use `Aliased` in your own crate.
95//!
96//! The cast is only sound if the two types are laid out exactly the same in memory, but that is
97//! harder to guarantee than you might expect. The Rust compiler does not guarantee that
98//! `A<Aliased<T>>` and `A<T>` are laid out the same in memory for any arbitrary `A`, _even_ if
99//! both `A` and `Aliased` are `#[repr(transparent)]`. The _primary_ reason for this is associated
100//! types. Imagine that I write this code:
101//!
102//! ```rust,ignore
103//! trait Wonky { type Weird; }
104//! struct A<T: Wonky>(T, T::Weird);
105//!
106//! impl<T> Wonky for Aliased<T> { type Weird = u32; }
107//! impl<T> Wonky for T { type Weird = u16; }
108//! ```
109//!
110//! Clearly, these types will end up with different memory layouts, since one will contain a
111//! `u32` and the other a `u16` (let's ignore the fact that this particular example requires
112//! specialization). This, in turn, means that it is _not_ generally safe to transmute between a
113//! wrapper around one type and that same wrapper around a different type with the same layout! You
114//! can see this discussed in far more detail here if you're curious:
115//!
116//! <https://github.com/jonhoo/rust-evmap/pull/83#issuecomment-735504638>
117//!
118//! Now, if we can find a way to _guarantee_ that the types have the same layout, this problem
119//! changes, but how might we go about this? Our saving grace is that we are casting between
120//! `A<Aliased<T, D>>` and `A<Aliased<T, D2>>` where we control both `D` and `D2`. If we ensure
121//! that both those types are private, there is no way for any code external to your crate can
122//! implement a trait for one type but not the other. And thus there's no way (that I know of) for
123//! making it unsound to cast between the types!
124//!
125//! Now, I only say that this is likely sound because the language does not actually give
126//! this as a _guarantee_ at the moment. Though wiser minds [seem to suggest that this might
127//! be okay](https://github.com/rust-lang/unsafe-code-guidelines/issues/35#issuecomment-735858397).
128//!
129//! But this warrants repeating: **your `D` types for `Aliased` _must_ be private**.
130
131use std::marker::PhantomData;
132use std::mem::MaybeUninit;
133use std::ops::Deref;
134
135// Just to make the doc comment linking work.
136#[allow(unused_imports)]
137use crate::Absorb;
138
139/// Dictates the dropping behavior for the implementing type when used with [`Aliased`].
140pub trait DropBehavior {
141    /// An [`Aliased<T, D>`](Aliased) will drop its inner `T` if and only if `D::DO_DROP` is `true`
142    const DO_DROP: bool;
143}
144
145/// A `T` that is aliased.
146///
147/// You should be able to mostly ignore this type, as it can generally be treated exactly like a
148/// `&T`. However, there are some minor exceptions around forwarding traits -- since `Aliased` is a
149/// wrapper type around `T`, it cannot automatically forward traits it does not know about to `&T`.
150/// This means that if your `&T` implements, say, `Serialize` or some custom `Borrow<Q>`,
151/// `Aliased<T>` will not implement that same trait. You can work around this either by
152/// implementing your trait specifically for `Aliased<T>` where possible, or by manually
153/// dereferencing to get the `&T` before using the trait.
154#[repr(transparent)]
155pub struct Aliased<T, D>
156where
157    D: DropBehavior,
158{
159    aliased: MaybeUninit<T>,
160
161    drop_behavior: PhantomData<D>,
162
163    // We cannot implement Send just because T is Send since we're aliasing it.
164    _no_auto_send: PhantomData<*const T>,
165}
166
167impl<T, D> Aliased<T, D>
168where
169    D: DropBehavior,
170{
171    /// Create an alias of the inner `T`.
172    ///
173    /// # Safety
174    ///
175    /// This method is only safe to call as long as you ensure that the alias is never used after
176    /// an `Aliased<T, D>` of `self` where `D::DO_DROP` is `true` is dropped, **and** as long
177    /// as no `&mut T` is ever given out while some `Aliased<T>` may still be used. The returned
178    /// type assumes that it is always safe to dereference into `&T`, which would not be true if
179    /// either of those invariants were broken.
180    pub unsafe fn alias(&self) -> Self {
181        // safety:
182        //   We are aliasing T here, but it is okay because:
183        //    a) the T is behind a MaybeUninit, and so will cannot be accessed safely; and
184        //    b) we only expose _either_ &T while aliased, or &mut after the aliasing ends.
185        Aliased {
186            aliased: std::ptr::read(&self.aliased),
187            drop_behavior: PhantomData,
188            _no_auto_send: PhantomData,
189        }
190    }
191
192    /// Construct an aliased value around a `T`.
193    ///
194    /// This method is safe since it effectively leaks `T`. Note that we do not implement `From<T>`
195    /// because we do not want users to construct `Aliased<T>`s on their own. If they did, they
196    /// would almost certain end up with incorrect drop behavior.
197    pub fn from(t: T) -> Self {
198        Self {
199            aliased: MaybeUninit::new(t),
200            drop_behavior: PhantomData,
201            _no_auto_send: PhantomData,
202        }
203    }
204
205    /// Turn this aliased `T` into one with a different drop behavior.
206    ///
207    /// # Safety
208    ///
209    /// It is always safe to change an `Aliased` from a dropping `D` to a non-dropping `D`. Going
210    /// the other way around is only safe if `self` is the last alias for the `T`.
211    pub unsafe fn change_drop<D2: DropBehavior>(self) -> Aliased<T, D2> {
212        Aliased {
213            // safety:
214            aliased: std::ptr::read(&self.aliased),
215            drop_behavior: PhantomData,
216            _no_auto_send: PhantomData,
217        }
218    }
219}
220
221// Aliased gives &T across threads if shared or sent across thread boundaries.
222// Aliased gives &mut T across threads (for drop) if sent across thread boundaries.
223// This implies that we are only Send if T is Send+Sync, and Sync if T is Sync.
224//
225// Note that these bounds are stricter than what the compiler would auto-generate for the type.
226unsafe impl<T, D> Send for Aliased<T, D>
227where
228    D: DropBehavior,
229    T: Send + Sync,
230{
231}
232unsafe impl<T, D> Sync for Aliased<T, D>
233where
234    D: DropBehavior,
235    T: Sync,
236{
237}
238
239impl<T, D> Drop for Aliased<T, D>
240where
241    D: DropBehavior,
242{
243    fn drop(&mut self) {
244        if D::DO_DROP {
245            // safety:
246            //   MaybeUninit<T> was created from a valid T.
247            //   That T has not been dropped (getting a Aliased<T, DoDrop> is unsafe).
248            //   T is no longer aliased (by the safety assumption of getting a Aliased<T, DoDrop>),
249            //   so we are allowed to re-take ownership of the T.
250            unsafe { std::ptr::drop_in_place(self.aliased.as_mut_ptr()) }
251        }
252    }
253}
254
255impl<T, D> AsRef<T> for Aliased<T, D>
256where
257    D: DropBehavior,
258{
259    fn as_ref(&self) -> &T {
260        // safety:
261        //   MaybeUninit<T> was created from a valid T.
262        //   That T has not been dropped (getting a Aliased<T, DoDrop> is unsafe).
263        //   All we have done to T is alias it. But, since we only give out &T
264        //   (which should be legal anyway), we're fine.
265        unsafe { &*self.aliased.as_ptr() }
266    }
267}
268
269impl<T, D> Deref for Aliased<T, D>
270where
271    D: DropBehavior,
272{
273    type Target = T;
274    fn deref(&self) -> &Self::Target {
275        self.as_ref()
276    }
277}
278
279use std::hash::{Hash, Hasher};
280impl<T, D> Hash for Aliased<T, D>
281where
282    D: DropBehavior,
283    T: Hash,
284{
285    fn hash<H>(&self, state: &mut H)
286    where
287        H: Hasher,
288    {
289        self.as_ref().hash(state)
290    }
291}
292
293use std::fmt;
294impl<T, D> fmt::Debug for Aliased<T, D>
295where
296    D: DropBehavior,
297    T: fmt::Debug,
298{
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        self.as_ref().fmt(f)
301    }
302}
303
304impl<T, D> PartialEq for Aliased<T, D>
305where
306    D: DropBehavior,
307    T: PartialEq,
308{
309    fn eq(&self, other: &Self) -> bool {
310        self.as_ref().eq(other.as_ref())
311    }
312}
313
314impl<T, D> Eq for Aliased<T, D>
315where
316    D: DropBehavior,
317    T: Eq,
318{
319}
320
321impl<T, D> PartialOrd for Aliased<T, D>
322where
323    D: DropBehavior,
324    T: PartialOrd,
325{
326    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
327        self.as_ref().partial_cmp(other.as_ref())
328    }
329
330    fn lt(&self, other: &Self) -> bool {
331        self.as_ref().lt(other.as_ref())
332    }
333
334    fn le(&self, other: &Self) -> bool {
335        self.as_ref().le(other.as_ref())
336    }
337
338    fn gt(&self, other: &Self) -> bool {
339        self.as_ref().gt(other.as_ref())
340    }
341
342    fn ge(&self, other: &Self) -> bool {
343        self.as_ref().ge(other.as_ref())
344    }
345}
346
347impl<T, D> Ord for Aliased<T, D>
348where
349    D: DropBehavior,
350    T: Ord,
351{
352    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
353        self.as_ref().cmp(other.as_ref())
354    }
355}
356
357use std::borrow::Borrow;
358impl<T, D> Borrow<T> for Aliased<T, D>
359where
360    D: DropBehavior,
361{
362    fn borrow(&self) -> &T {
363        self.as_ref()
364    }
365}
366// What we _really_ want here is:
367// ```
368// impl<T, D> Borrow<D> for Aliased<T>
369// where
370//     T: Borrow<D>,
371// {
372//     fn borrow(&self) -> &U {
373//         self.as_ref().borrow()
374//     }
375// }
376// ```
377// But unfortunately that won't work due to trait coherence.
378// Instead, we manually write the nice Borrow impls from std.
379// This won't help with custom Borrow impls, but gets you pretty far.
380impl<D> Borrow<str> for Aliased<String, D>
381where
382    D: DropBehavior,
383{
384    fn borrow(&self) -> &str {
385        self.as_ref()
386    }
387}
388impl<D> Borrow<std::path::Path> for Aliased<std::path::PathBuf, D>
389where
390    D: DropBehavior,
391{
392    fn borrow(&self) -> &std::path::Path {
393        self.as_ref()
394    }
395}
396impl<T, D> Borrow<[T]> for Aliased<Vec<T>, D>
397where
398    D: DropBehavior,
399{
400    fn borrow(&self) -> &[T] {
401        self.as_ref()
402    }
403}
404impl<T, D> Borrow<T> for Aliased<Box<T>, D>
405where
406    T: ?Sized,
407    D: DropBehavior,
408{
409    fn borrow(&self) -> &T {
410        self.as_ref()
411    }
412}
413impl<T, D> Borrow<T> for Aliased<std::sync::Arc<T>, D>
414where
415    T: ?Sized,
416    D: DropBehavior,
417{
418    fn borrow(&self) -> &T {
419        self.as_ref()
420    }
421}
422impl<T, D> Borrow<T> for Aliased<std::rc::Rc<T>, D>
423where
424    T: ?Sized,
425    D: DropBehavior,
426{
427    fn borrow(&self) -> &T {
428        self.as_ref()
429    }
430}