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
9};9};
10 10
11use crate::{11use crate::{
12 cache::delta::{traverse::util::ItemSliceSend, Item, Tree},12 cache::delta::{traverse::util::ItemSliceSync, Item, Tree},
13 data::EntryRange,13 data::EntryRange,
14};14};
15 15
@@ -133,25 +133,23 @@ where · 117 unmodified lines
133 let object_progress = OwnShared::new(Mutable::new(object_progress));133 let object_progress = OwnShared::new(Mutable::new(object_progress));
134 134
135 let start = std::time::Instant::now();135 let start = std::time::Instant::now();
136 let child_items = ItemSliceSync::new(&mut self.child_items);
137 let child_items = &child_items;
136 in_parallel_with_slice(138 in_parallel_with_slice(
137 &mut self.root_items,139 &mut self.root_items,
138 thread_limit,140 thread_limit,
139 {141 {
140 let child_items = ItemSliceSend::new(&mut self.child_items);
141 {142 {
142 let object_progress = object_progress.clone();143 let object_progress = object_progress.clone();
143 move |thread_index| {144 move |thread_index| resolve::State {
144 let _ = &child_items;145 delta_bytes: Vec::<u8>::with_capacity(4096),
145 resolve::State {146 fully_resolved_delta_bytes: Vec::<u8>::with_capacity(4096),
146 delta_bytes: Vec::<u8>::with_capacity(4096),147 progress: Box::new(
147 fully_resolved_delta_bytes: Vec::<u8>::with_capacity(4096),148 threading::lock(&object_progress).add_child(format!("thread {thread_index}")),
148 progress: Box::new(149 ),
149 threading::lock(&object_progress).add_child(format!("thread {thread_index}")),150 resolve: resolve.clone(),
150 ),151 modify_base: inspect_object.clone(),
151 resolve: resolve.clone(),152 child_items,
152 modify_base: inspect_object.clone(),
153 child_items: child_items.clone(),
154 }
155 }153 }
156 }154 }
157 },155 },
gix-pack/src/cache/delta/traverse/resolve.rs+5−11
@@ -7,7 +7,7 @@ use gix_features::{progress::Progress, threading, zlib}; · 6 unmodified lines
7 7
8use crate::{8use crate::{
9 cache::delta::{9 cache::delta::{
10 traverse::{util::ItemSliceSend, Context, Error},10 traverse::{util::ItemSliceSync, Context, Error},
11 Item,11 Item,
12 },12 },
13 data,13 data,
@@ -17,7 +17,7 @@ use crate::{ · 3 unmodified lines
17/// An item returned by `iter_root_chunks`, allowing access to the `data` stored alongside nodes in a [`Tree`].17/// An item returned by `iter_root_chunks`, allowing access to the `data` stored alongside nodes in a [`Tree`].
18struct Node<'a, T: Send> {18struct Node<'a, T: Send> {
19 item: &'a mut Item<T>,19 item: &'a mut Item<T>,
20 child_items: ItemSliceSend<'a, Item<T>>,20 child_items: &'a ItemSliceSync<'a, Item<T>>,
21}21}
22 22
23impl<'a, T: Send> Node<'a, T> {23impl<'a, T: Send> Node<'a, T> {
@@ -51,7 +51,7 @@ impl<'a, T: Send> Node<'a, T> { · 27 unmodified lines
51 #[allow(unsafe_code)]51 #[allow(unsafe_code)]
52 self.item.children.iter().map(move |&index| Node {52 self.item.children.iter().map(move |&index| Node {
53 item: unsafe { children.get_mut(index as usize) },53 item: unsafe { children.get_mut(index as usize) },
54 child_items: children.clone(),54 child_items: children,
55 })55 })
56 }56 }
57}57}
@@ -62,7 +62,7 @@ pub(crate) struct State<'items, F, MBFN, T: Send> { · 4 unmodified lines
62 pub progress: Box<dyn Progress>,62 pub progress: Box<dyn Progress>,
63 pub resolve: F,63 pub resolve: F,
64 pub modify_base: MBFN,64 pub modify_base: MBFN,
65 pub child_items: ItemSliceSend<'items, Item<T>>,65 pub child_items: &'items ItemSliceSync<'items, Item<T>>,
66}66}
67 67
68#[allow(clippy::too_many_arguments)]68#[allow(clippy::too_many_arguments)]
@@ -106,13 +106,7 @@ where · 37 unmodified lines
106 // each node is a base, and its children always start out as deltas which become a base after applying them.106 // each node is a base, and its children always start out as deltas which become a base after applying them.
107 // These will be pushed onto our stack until all are processed107 // These will be pushed onto our stack until all are processed
108 let root_level = 0;108 let root_level = 0;
109 let mut nodes: Vec<_> = vec![(109 let mut nodes: Vec<_> = vec![(root_level, Node { item, child_items })];
110 root_level,
111 Node {
112 item,
113 child_items: child_items.clone(),
114 },
115 )];
116 while let Some((level, mut base)) = nodes.pop() {110 while let Some((level, mut base)) = nodes.pop() {
117 if should_interrupt.load(Ordering::Relaxed) {111 if should_interrupt.load(Ordering::Relaxed) {
118 return Err(Error::Interrupted);112 return Err(Error::Interrupted);
gix-pack/src/cache/delta/traverse/util.rs+9−19
@@ -1,6 +1,6 @@
1use std::marker::PhantomData;1use std::marker::PhantomData;
2 2
3pub(crate) struct ItemSliceSend<'a, T>3pub(crate) struct ItemSliceSync<'a, T>
4where4where
5 T: Send,5 T: Send,
6{6{
@@ -8,12 +8,12 @@ where · 1 unmodified line
8 phantom: PhantomData<&'a T>,8 phantom: PhantomData<&'a T>,
9}9}
10 10
11impl<'a, T> ItemSliceSend<'a, T>11impl<'a, T> ItemSliceSync<'a, T>
12where12where
13 T: Send,13 T: Send,
14{14{
15 pub fn new(items: &'a mut [T]) -> Self {15 pub fn new(items: &'a mut [T]) -> Self {
16 ItemSliceSend {16 ItemSliceSync {
17 items: items.as_mut_ptr(),17 items: items.as_mut_ptr(),
18 phantom: PhantomData,18 phantom: PhantomData,
19 }19 }
@@ -27,20 +27,10 @@ where · 7 unmodified lines
27 }27 }
28}28}
29 29
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 pointers30// SAFETY: T is `Send`, and we only use the pointer for creating new pointers.
31/// don't violate aliasing rules.31#[allow(unsafe_code)]
32impl<T> Clone for ItemSliceSend<'_, T>32unsafe impl<T> Send for ItemSliceSync<'_, T> where T: Send {}
33where33// SAFETY: T is `Send`, and as long as the user follows the contract of
34 T: Send,34// `get_mut()`, we only ever access one T at a time.
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.
45#[allow(unsafe_code)]35#[allow(unsafe_code)]
46unsafe impl<T> Send for ItemSliceSend<'_, T> where T: Send {}36unsafe impl<T> Sync for ItemSliceSync<'_, T> where T: Send {}