History

remove `Clone` impl of `ItemSliceSend` and make it `Sync` instead

The `Clone` impl allows two `ItemSliceSend` to be backed by the same slice. That makes the behavior of `get_mut()` harder to reason about because we have to consider clones of the `ItemSliceSend` to determine if it's safe. By making it `Sync`, we make it more obvious that multiple threads referring to the same instance need to be considered. I renamed the type to `ItemSliceSync` since it's now also `Sync`. We could make the type also check the bounds and that an individual element has not been borrowed more than once. Then we could make `get_mut()` not `unsafe`.

Martin von Zweigbergk committed 2 years ago

Commit 43bca111 parent 7301ca13 files · +26 −44Browse files

Unified · Split

3 files
  1. gix-pack/src/cache/delta/traverse/mod.rs +12 −14
  2. gix-pack/src/cache/delta/traverse/resolve.rs +5 −11
  3. gix-pack/src/cache/delta/traverse/util.rs +9 −19
gix-pack/src/cache/delta/traverse/mod.rs+12−14
@@ -9,7 +9,7 @@ use gix_features::{ · 8 unmodified lines
99 };
1010
1111 use crate::{
12− cache::delta::{traverse::util::ItemSliceSend, Item, Tree},
12+ cache::delta::{traverse::util::ItemSliceSync, Item, Tree},
1313 data::EntryRange,
1414 };
1515
@@ -133,25 +133,23 @@ where · 117 unmodified lines
133133 let object_progress = OwnShared::new(Mutable::new(object_progress));
134134
135135 let start = std::time::Instant::now();
136+ let child_items = ItemSliceSync::new(&mut self.child_items);
137+ let child_items = &child_items;
136138 in_parallel_with_slice(
137139 &mut self.root_items,
138140 thread_limit,
139141 {
140− let child_items = ItemSliceSend::new(&mut self.child_items);
141142 {
142143 let object_progress = object_progress.clone();
143− move |thread_index| {
144− let _ = &child_items;
145− resolve::State {
146− delta_bytes: Vec::<u8>::with_capacity(4096),
147− fully_resolved_delta_bytes: Vec::<u8>::with_capacity(4096),
148− progress: Box::new(
149− threading::lock(&object_progress).add_child(format!("thread {thread_index}")),
150− ),
151− resolve: resolve.clone(),
152− modify_base: inspect_object.clone(),
153− child_items: child_items.clone(),
154− }
144+ move |thread_index| resolve::State {
145+ delta_bytes: Vec::<u8>::with_capacity(4096),
146+ fully_resolved_delta_bytes: Vec::<u8>::with_capacity(4096),
147+ progress: Box::new(
148+ threading::lock(&object_progress).add_child(format!("thread {thread_index}")),
149+ ),
150+ resolve: resolve.clone(),
151+ modify_base: inspect_object.clone(),
152+ child_items,
155153 }
156154 }
157155 },
gix-pack/src/cache/delta/traverse/resolve.rs+5−11
@@ -7,7 +7,7 @@ use gix_features::{progress::Progress, threading, zlib}; · 6 unmodified lines
77
88 use crate::{
99 cache::delta::{
10− traverse::{util::ItemSliceSend, Context, Error},
10+ traverse::{util::ItemSliceSync, Context, Error},
1111 Item,
1212 },
1313 data,
@@ -17,7 +17,7 @@ use crate::{ · 3 unmodified lines
1717 /// An item returned by `iter_root_chunks`, allowing access to the `data` stored alongside nodes in a [`Tree`].
1818 struct Node<'a, T: Send> {
1919 item: &'a mut Item<T>,
20− child_items: ItemSliceSend<'a, Item<T>>,
20+ child_items: &'a ItemSliceSync<'a, Item<T>>,
2121 }
2222
2323 impl<'a, T: Send> Node<'a, T> {
@@ -51,7 +51,7 @@ impl<'a, T: Send> Node<'a, T> { · 27 unmodified lines
5151 #[allow(unsafe_code)]
5252 self.item.children.iter().map(move |&index| Node {
5353 item: unsafe { children.get_mut(index as usize) },
54− child_items: children.clone(),
54+ child_items: children,
5555 })
5656 }
5757 }
@@ -62,7 +62,7 @@ pub(crate) struct State<'items, F, MBFN, T: Send> { · 4 unmodified lines
6262 pub progress: Box<dyn Progress>,
6363 pub resolve: F,
6464 pub modify_base: MBFN,
65− pub child_items: ItemSliceSend<'items, Item<T>>,
65+ pub child_items: &'items ItemSliceSync<'items, Item<T>>,
6666 }
6767
6868 #[allow(clippy::too_many_arguments)]
@@ -106,13 +106,7 @@ where · 37 unmodified lines
106106 // each node is a base, and its children always start out as deltas which become a base after applying them.
107107 // These will be pushed onto our stack until all are processed
108108 let root_level = 0;
109− let mut nodes: Vec<_> = vec![(
110− root_level,
111− Node {
112− item,
113− child_items: child_items.clone(),
114− },
115− )];
109+ let mut nodes: Vec<_> = vec![(root_level, Node { item, child_items })];
116110 while let Some((level, mut base)) = nodes.pop() {
117111 if should_interrupt.load(Ordering::Relaxed) {
118112 return Err(Error::Interrupted);
gix-pack/src/cache/delta/traverse/util.rs+9−19
@@ -1,6 +1,6 @@
11 use std::marker::PhantomData;
22
3−pub(crate) struct ItemSliceSend<'a, T>
3+pub(crate) struct ItemSliceSync<'a, T>
44 where
55 T: Send,
66 {
@@ -8,12 +8,12 @@ where · 1 unmodified line
88 phantom: PhantomData<&'a T>,
99 }
1010
11−impl<'a, T> ItemSliceSend<'a, T>
11+impl<'a, T> ItemSliceSync<'a, T>
1212 where
1313 T: Send,
1414 {
1515 pub fn new(items: &'a mut [T]) -> Self {
16− ItemSliceSend {
16+ ItemSliceSync {
1717 items: items.as_mut_ptr(),
1818 phantom: PhantomData,
1919 }
@@ -27,20 +27,10 @@ where · 7 unmodified lines
2727 }
2828 }
2929
30−/// SAFETY: This would be unsafe if this would ever be abused, but it's used internally and only in a way that assure that the pointers
31−/// don't violate aliasing rules.
32−impl<T> Clone for ItemSliceSend<'_, T>
33−where
34− T: Send,
35−{
36− fn clone(&self) -> Self {
37− ItemSliceSend {
38− items: self.items,
39− phantom: self.phantom,
40− }
41− }
42−}
43−
44−// SAFETY: T is `Send`, and we only ever access one T at a time. And, ptrs need that assurance, I wonder if it's always right.
30+// SAFETY: T is `Send`, and we only use the pointer for creating new pointers.
31+#[allow(unsafe_code)]
32+unsafe impl<T> Send for ItemSliceSync<'_, T> where T: Send {}
33+// SAFETY: T is `Send`, and as long as the user follows the contract of
34+// `get_mut()`, we only ever access one T at a time.
4535 #[allow(unsafe_code)]
46−unsafe impl<T> Send for ItemSliceSend<'_, T> where T: Send {}
36+unsafe impl<T> Sync for ItemSliceSync<'_, T> where T: Send {}