rust/hg-core/src/dirstate_tree/owning.rs
branchstable
changeset 49000 dd6b67d5c256
parent 48999 cfd270d83169
child 49930 e98fd81bb151
child 50243 6cce0afc1454
equal deleted inserted replaced
48999:cfd270d83169 49000:dd6b67d5c256
       
     1 use crate::{DirstateError, DirstateParents};
       
     2 
     1 use super::dirstate_map::DirstateMap;
     3 use super::dirstate_map::DirstateMap;
     2 use stable_deref_trait::StableDeref;
       
     3 use std::ops::Deref;
     4 use std::ops::Deref;
     4 
     5 
     5 /*
     6 use ouroboros::self_referencing;
     6 // /!\ This is unsound and can cause use after free. It will be fixed in the
       
     7 // next patch
       
     8 
       
     9 // If we change `value` from its current use of `HgPathBuf` to `&HgPath`,
       
    10 // nothing here tells that `value` will outlive `OwningDirstateMap`
       
    11 pub fn copy_map_insert<'a,'owned>(
       
    12     &'owned mut self,
       
    13     key: &HgPath,
       
    14     value: &'a HgPath,
       
    15 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
       
    16     // `'local` is smaller than `'a` here
       
    17     let map: &'local mut DirstateMap<'local> = self.get_map_mut();
       
    18     let node: &'local mut Node<'local> = DirstateMap::get_or_insert_node(
       
    19         map.on_disk,
       
    20         &mut map.unreachable_bytes,
       
    21         &mut map.root,
       
    22         &key,
       
    23         WithBasename::to_cow_owned,
       
    24         |_ancestor| {},
       
    25     )?;
       
    26     if node.copy_source.is_none() {
       
    27         map.nodes_with_copy_source_count += 1
       
    28     }
       
    29     Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
       
    30     // and right here ----------^^^^^^^^^^^^
       
    31     // we are storing `&'a HgPath` in `Node<'local>` which is possible
       
    32     // because to the compiler, `'a` is longer than ``local`.
       
    33     // It is wrong because nothing proves that `&'a HgPath` will outlive `self`.
       
    34 }
       
    35 
       
    36 // All of this is caused by the wrong cast of the DirstateMap pointer that
       
    37 // fakes the lifetime of `DirstateMap` and ensures the compiler that it lives
       
    38 // as long as `on_disk`, which is only true for its immutable data.
       
    39 // This will be fixed in the next commit.
       
    40 */
       
    41 
     7 
    42 /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it
     8 /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it
    43 /// borrows.
     9 /// borrows.
    44 ///
    10 #[self_referencing]
    45 /// This is similar to [`OwningRef`] which is more limited because it
       
    46 /// represents exactly one `&T` reference next to the value it borrows, as
       
    47 /// opposed to a struct that may contain an arbitrary number of references in
       
    48 /// arbitrarily-nested data structures.
       
    49 ///
       
    50 /// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html
       
    51 pub struct OwningDirstateMap {
    11 pub struct OwningDirstateMap {
    52     /// Owned handle to a bytes buffer with a stable address.
       
    53     ///
       
    54     /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>.
       
    55     on_disk: Box<dyn Deref<Target = [u8]> + Send>,
    12     on_disk: Box<dyn Deref<Target = [u8]> + Send>,
    56 
    13     #[borrows(on_disk)]
    57     /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the
    14     #[covariant]
    58     /// language cannot represent a lifetime referencing a sibling field.
    15     map: DirstateMap<'this>,
    59     /// This is not quite a self-referencial struct (moving this struct is not
       
    60     /// a problem as it doesn’t change the address of the bytes buffer owned
       
    61     /// by `on_disk`) but touches similar borrow-checker limitations.
       
    62     ptr: *mut (),
       
    63 }
    16 }
    64 
    17 
    65 impl OwningDirstateMap {
    18 impl OwningDirstateMap {
    66     pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self
    19     pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self
    67     where
    20     where
    68         OnDisk: Deref<Target = [u8]> + StableDeref + Send + 'static,
    21         OnDisk: Deref<Target = [u8]> + Send + 'static,
    69     {
    22     {
    70         let on_disk = Box::new(on_disk);
    23         let on_disk = Box::new(on_disk);
    71         let bytes: &'_ [u8] = &on_disk;
       
    72         let map = DirstateMap::empty(bytes);
       
    73 
    24 
    74         // Like in `bytes` above, this `'_` lifetime parameter borrows from
    25         OwningDirstateMapBuilder {
    75         // the bytes buffer owned by `on_disk`.
    26             on_disk,
    76         let ptr: *mut DirstateMap<'_> = Box::into_raw(Box::new(map));
    27             map_builder: |bytes| DirstateMap::empty(&bytes),
    77 
    28         }
    78         // Erase the pointed type entirely in order to erase the lifetime.
    29         .build()
    79         let ptr: *mut () = ptr.cast();
       
    80 
       
    81         Self { on_disk, ptr }
       
    82     }
    30     }
    83 
    31 
    84     pub fn get_pair_mut<'a>(
    32     pub fn new_v1<OnDisk>(
    85         &'a mut self,
    33         on_disk: OnDisk,
    86     ) -> (&'a [u8], &'a mut DirstateMap<'a>) {
    34     ) -> Result<(Self, DirstateParents), DirstateError>
    87         // SAFETY: We cast the type-erased pointer back to the same type it had
    35     where
    88         // in `new`, except with a different lifetime parameter. This time we
    36         OnDisk: Deref<Target = [u8]> + Send + 'static,
    89         // connect the lifetime to that of `self`. This cast is valid because
    37     {
    90         // `self` owns the same `on_disk` whose buffer `DirstateMap`
    38         let on_disk = Box::new(on_disk);
    91         // references. That buffer has a stable memory address because our
    39         let mut parents = DirstateParents::NULL;
    92         // `Self::new_empty` counstructor requires `StableDeref`.
    40 
    93         let ptr: *mut DirstateMap<'a> = self.ptr.cast();
    41         Ok((
    94         // SAFETY: we dereference that pointer, connecting the lifetime of the
    42             OwningDirstateMapTryBuilder {
    95         // new `&mut` to that of `self`. This is valid because the
    43                 on_disk,
    96         // raw pointer is to a boxed value, and `self` owns that box.
    44                 map_builder: |bytes| {
    97         (&self.on_disk, unsafe { &mut *ptr })
    45                     DirstateMap::new_v1(&bytes).map(|(dmap, p)| {
       
    46                         parents = p.unwrap_or(DirstateParents::NULL);
       
    47                         dmap
       
    48                     })
       
    49                 },
       
    50             }
       
    51             .try_build()?,
       
    52             parents,
       
    53         ))
    98     }
    54     }
    99 
    55 
   100     pub fn get_map_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> {
    56     pub fn new_v2<OnDisk>(
   101         self.get_pair_mut().1
    57         on_disk: OnDisk,
       
    58         data_size: usize,
       
    59         metadata: &[u8],
       
    60     ) -> Result<Self, DirstateError>
       
    61     where
       
    62         OnDisk: Deref<Target = [u8]> + Send + 'static,
       
    63     {
       
    64         let on_disk = Box::new(on_disk);
       
    65 
       
    66         OwningDirstateMapTryBuilder {
       
    67             on_disk,
       
    68             map_builder: |bytes| {
       
    69                 DirstateMap::new_v2(&bytes, data_size, metadata)
       
    70             },
       
    71         }
       
    72         .try_build()
   102     }
    73     }
   103 
    74 
   104     pub fn get_map<'a>(&'a self) -> &'a DirstateMap<'a> {
    75     pub fn with_dmap_mut<R>(
   105         // SAFETY: same reasoning as in `get_pair_mut` above.
    76         &mut self,
   106         let ptr: *mut DirstateMap<'a> = self.ptr.cast();
    77         f: impl FnOnce(&mut DirstateMap) -> R,
   107         unsafe { &*ptr }
    78     ) -> R {
       
    79         self.with_map_mut(f)
   108     }
    80     }
   109 
    81 
   110     pub fn on_disk<'a>(&'a self) -> &'a [u8] {
    82     pub fn get_map(&self) -> &DirstateMap {
   111         &self.on_disk
    83         self.borrow_map()
       
    84     }
       
    85 
       
    86     pub fn on_disk(&self) -> &[u8] {
       
    87         self.borrow_on_disk()
   112     }
    88     }
   113 }
    89 }
   114 
       
   115 impl Drop for OwningDirstateMap {
       
   116     fn drop(&mut self) {
       
   117         // Silence a "field is never read" warning, and demonstrate that this
       
   118         // value is still alive.
       
   119         let _: &Box<dyn Deref<Target = [u8]> + Send> = &self.on_disk;
       
   120         // SAFETY: this cast is the same as in `get_mut`, and is valid for the
       
   121         // same reason. `self.on_disk` still exists at this point, drop glue
       
   122         // will drop it implicitly after this `drop` method returns.
       
   123         let ptr: *mut DirstateMap<'_> = self.ptr.cast();
       
   124         // SAFETY: `Box::from_raw` takes ownership of the box away from `self`.
       
   125         // This is fine because drop glue does nothing for `*mut ()` and we’re
       
   126         // in `drop`, so `get` and `get_mut` cannot be called again.
       
   127         unsafe { drop(Box::from_raw(ptr)) }
       
   128     }
       
   129 }
       
   130 
       
   131 fn _static_assert_is_send<T: Send>() {}
       
   132 
       
   133 fn _static_assert_fields_are_send() {
       
   134     _static_assert_is_send::<Box<DirstateMap<'_>>>();
       
   135 }
       
   136 
       
   137 // SAFETY: we don’t get this impl implicitly because `*mut (): !Send` because
       
   138 // thread-safety of raw pointers is unknown in the general case. However this
       
   139 // particular raw pointer represents a `Box<DirstateMap<'on_disk>>` that we
       
   140 // own. Since that `Box` is `Send` as shown in above, it is sound to mark
       
   141 // this struct as `Send` too.
       
   142 unsafe impl Send for OwningDirstateMap {}