• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use rustc_index::Idx;
2 use rustc_span::{Span, SpanData, DUMMY_SP};
3 use smallvec::SmallVec;
4 use std::{
5     cmp::Ordering,
6     fmt::Debug,
7     ops::{Index, IndexMut},
8 };
9 
10 /// A vector clock index, this is associated with a thread id
11 /// but in some cases one vector index may be shared with
12 /// multiple thread ids if it safe to do so.
13 #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
14 pub struct VectorIdx(u32);
15 
16 impl VectorIdx {
17     #[inline(always)]
to_u32(self) -> u3218     pub fn to_u32(self) -> u32 {
19         self.0
20     }
21 
22     pub const MAX_INDEX: VectorIdx = VectorIdx(u32::MAX);
23 }
24 
25 impl Idx for VectorIdx {
26     #[inline]
new(idx: usize) -> Self27     fn new(idx: usize) -> Self {
28         VectorIdx(u32::try_from(idx).unwrap())
29     }
30 
31     #[inline]
index(self) -> usize32     fn index(self) -> usize {
33         usize::try_from(self.0).unwrap()
34     }
35 }
36 
37 impl From<u32> for VectorIdx {
38     #[inline]
from(id: u32) -> Self39     fn from(id: u32) -> Self {
40         Self(id)
41     }
42 }
43 
44 /// The size of the vector-clock to store inline
45 /// clock vectors larger than this will be stored on the heap
46 const SMALL_VECTOR: usize = 4;
47 
48 /// The time-stamps recorded in the data-race detector consist of both
49 /// a 32-bit unsigned integer which is the actual timestamp, and a `Span`
50 /// so that diagnostics can report what code was responsible for an operation.
51 #[derive(Clone, Copy, Debug)]
52 pub struct VTimestamp {
53     time: u32,
54     pub span: Span,
55 }
56 
57 impl VTimestamp {
58     pub const ZERO: VTimestamp = VTimestamp { time: 0, span: DUMMY_SP };
59 
span_data(&self) -> SpanData60     pub fn span_data(&self) -> SpanData {
61         self.span.data()
62     }
63 }
64 
65 impl PartialEq for VTimestamp {
eq(&self, other: &Self) -> bool66     fn eq(&self, other: &Self) -> bool {
67         self.time == other.time
68     }
69 }
70 
71 impl Eq for VTimestamp {}
72 
73 impl PartialOrd for VTimestamp {
partial_cmp(&self, other: &Self) -> Option<Ordering>74     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
75         Some(self.cmp(other))
76     }
77 }
78 
79 impl Ord for VTimestamp {
cmp(&self, other: &Self) -> Ordering80     fn cmp(&self, other: &Self) -> Ordering {
81         self.time.cmp(&other.time)
82     }
83 }
84 
85 /// A vector clock for detecting data-races, this is conceptually
86 /// a map from a vector index (and thus a thread id) to a timestamp.
87 /// The compare operations require that the invariant that the last
88 /// element in the internal timestamp slice must not be a 0, hence
89 /// all zero vector clocks are always represented by the empty slice;
90 /// and allows for the implementation of compare operations to short
91 /// circuit the calculation and return the correct result faster,
92 /// also this means that there is only one unique valid length
93 /// for each set of vector clock values and hence the PartialEq
94 /// and Eq derivations are correct.
95 #[derive(PartialEq, Eq, Default, Debug)]
96 pub struct VClock(SmallVec<[VTimestamp; SMALL_VECTOR]>);
97 
98 impl VClock {
99     /// Create a new vector-clock containing all zeros except
100     /// for a value at the given index
new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock101     pub fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock {
102         let len = index.index() + 1;
103         let mut vec = smallvec::smallvec![VTimestamp::ZERO; len];
104         vec[index.index()] = timestamp;
105         VClock(vec)
106     }
107 
108     /// Load the internal timestamp slice in the vector clock
109     #[inline]
as_slice(&self) -> &[VTimestamp]110     pub fn as_slice(&self) -> &[VTimestamp] {
111         self.0.as_slice()
112     }
113 
114     /// Get a mutable slice to the internal vector with minimum `min_len`
115     /// elements, to preserve invariants this vector must modify
116     /// the `min_len`-1 nth element to a non-zero value
117     #[inline]
get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp]118     fn get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp] {
119         if self.0.len() < min_len {
120             self.0.resize(min_len, VTimestamp::ZERO);
121         }
122         assert!(self.0.len() >= min_len);
123         self.0.as_mut_slice()
124     }
125 
126     /// Increment the vector clock at a known index
127     /// this will panic if the vector index overflows
128     #[inline]
increment_index(&mut self, idx: VectorIdx, current_span: Span)129     pub fn increment_index(&mut self, idx: VectorIdx, current_span: Span) {
130         let idx = idx.index();
131         let mut_slice = self.get_mut_with_min_len(idx + 1);
132         let idx_ref = &mut mut_slice[idx];
133         idx_ref.time = idx_ref.time.checked_add(1).expect("Vector clock overflow");
134         if !current_span.is_dummy() {
135             idx_ref.span = current_span;
136         }
137     }
138 
139     // Join the two vector-clocks together, this
140     // sets each vector-element to the maximum value
141     // of that element in either of the two source elements.
join(&mut self, other: &Self)142     pub fn join(&mut self, other: &Self) {
143         let rhs_slice = other.as_slice();
144         let lhs_slice = self.get_mut_with_min_len(rhs_slice.len());
145         for (l, &r) in lhs_slice.iter_mut().zip(rhs_slice.iter()) {
146             let l_span = l.span;
147             let r_span = r.span;
148             *l = r.max(*l);
149             l.span = l.span.substitute_dummy(r_span).substitute_dummy(l_span);
150         }
151     }
152 
153     /// Set the element at the current index of the vector
set_at_index(&mut self, other: &Self, idx: VectorIdx)154     pub fn set_at_index(&mut self, other: &Self, idx: VectorIdx) {
155         let mut_slice = self.get_mut_with_min_len(idx.index() + 1);
156 
157         let prev_span = mut_slice[idx.index()].span;
158 
159         mut_slice[idx.index()] = other[idx];
160 
161         let span = &mut mut_slice[idx.index()].span;
162         *span = span.substitute_dummy(prev_span);
163     }
164 
165     /// Set the vector to the all-zero vector
166     #[inline]
set_zero_vector(&mut self)167     pub fn set_zero_vector(&mut self) {
168         self.0.clear();
169     }
170 
171     /// Return if this vector is the all-zero vector
is_zero_vector(&self) -> bool172     pub fn is_zero_vector(&self) -> bool {
173         self.0.is_empty()
174     }
175 }
176 
177 impl Clone for VClock {
clone(&self) -> Self178     fn clone(&self) -> Self {
179         VClock(self.0.clone())
180     }
181 
182     // Optimized clone-from, can be removed
183     // and replaced with a derive once a similar
184     // optimization is inserted into SmallVec's
185     // clone implementation.
clone_from(&mut self, source: &Self)186     fn clone_from(&mut self, source: &Self) {
187         let source_slice = source.as_slice();
188         self.0.clear();
189         self.0.extend_from_slice(source_slice);
190     }
191 }
192 
193 impl PartialOrd for VClock {
partial_cmp(&self, other: &VClock) -> Option<Ordering>194     fn partial_cmp(&self, other: &VClock) -> Option<Ordering> {
195         // Load the values as slices
196         let lhs_slice = self.as_slice();
197         let rhs_slice = other.as_slice();
198 
199         // Iterate through the combined vector slice continuously updating
200         // the value of `order` to the current comparison of the vector from
201         // index 0 to the currently checked index.
202         // An Equal ordering can be converted into Less or Greater ordering
203         // on finding an element that is less than or greater than the other
204         // but if one Greater and one Less element-wise comparison is found
205         // then no ordering is possible and so directly return an ordering
206         // of None.
207         let mut iter = lhs_slice.iter().zip(rhs_slice.iter());
208         let mut order = match iter.next() {
209             Some((lhs, rhs)) => lhs.cmp(rhs),
210             None => Ordering::Equal,
211         };
212         for (l, r) in iter {
213             match order {
214                 Ordering::Equal => order = l.cmp(r),
215                 Ordering::Less =>
216                     if l > r {
217                         return None;
218                     },
219                 Ordering::Greater =>
220                     if l < r {
221                         return None;
222                     },
223             }
224         }
225 
226         // Now test if either left or right have trailing elements,
227         // by the invariant the trailing elements have at least 1
228         // non zero value, so no additional calculation is required
229         // to determine the result of the PartialOrder.
230         let l_len = lhs_slice.len();
231         let r_len = rhs_slice.len();
232         match l_len.cmp(&r_len) {
233             // Equal means no additional elements: return current order
234             Ordering::Equal => Some(order),
235             // Right has at least 1 element > than the implicit 0,
236             // so the only valid values are Ordering::Less or None.
237             Ordering::Less =>
238                 match order {
239                     Ordering::Less | Ordering::Equal => Some(Ordering::Less),
240                     Ordering::Greater => None,
241                 },
242             // Left has at least 1 element > than the implicit 0,
243             // so the only valid values are Ordering::Greater or None.
244             Ordering::Greater =>
245                 match order {
246                     Ordering::Greater | Ordering::Equal => Some(Ordering::Greater),
247                     Ordering::Less => None,
248                 },
249         }
250     }
251 
lt(&self, other: &VClock) -> bool252     fn lt(&self, other: &VClock) -> bool {
253         // Load the values as slices
254         let lhs_slice = self.as_slice();
255         let rhs_slice = other.as_slice();
256 
257         // If l_len > r_len then at least one element
258         // in l_len is > than r_len, therefore the result
259         // is either Some(Greater) or None, so return false
260         // early.
261         let l_len = lhs_slice.len();
262         let r_len = rhs_slice.len();
263         if l_len <= r_len {
264             // If any elements on the left are greater than the right
265             // then the result is None or Some(Greater), both of which
266             // return false, the earlier test asserts that no elements in the
267             // extended tail violate this assumption. Otherwise l <= r, finally
268             // the case where the values are potentially equal needs to be considered
269             // and false returned as well
270             let mut equal = l_len == r_len;
271             for (&l, &r) in lhs_slice.iter().zip(rhs_slice.iter()) {
272                 if l > r {
273                     return false;
274                 } else if l < r {
275                     equal = false;
276                 }
277             }
278             !equal
279         } else {
280             false
281         }
282     }
283 
le(&self, other: &VClock) -> bool284     fn le(&self, other: &VClock) -> bool {
285         // Load the values as slices
286         let lhs_slice = self.as_slice();
287         let rhs_slice = other.as_slice();
288 
289         // If l_len > r_len then at least one element
290         // in l_len is > than r_len, therefore the result
291         // is either Some(Greater) or None, so return false
292         // early.
293         let l_len = lhs_slice.len();
294         let r_len = rhs_slice.len();
295         if l_len <= r_len {
296             // If any elements on the left are greater than the right
297             // then the result is None or Some(Greater), both of which
298             // return false, the earlier test asserts that no elements in the
299             // extended tail violate this assumption. Otherwise l <= r
300             !lhs_slice.iter().zip(rhs_slice.iter()).any(|(&l, &r)| l > r)
301         } else {
302             false
303         }
304     }
305 
gt(&self, other: &VClock) -> bool306     fn gt(&self, other: &VClock) -> bool {
307         // Load the values as slices
308         let lhs_slice = self.as_slice();
309         let rhs_slice = other.as_slice();
310 
311         // If r_len > l_len then at least one element
312         // in r_len is > than l_len, therefore the result
313         // is either Some(Less) or None, so return false
314         // early.
315         let l_len = lhs_slice.len();
316         let r_len = rhs_slice.len();
317         if l_len >= r_len {
318             // If any elements on the left are less than the right
319             // then the result is None or Some(Less), both of which
320             // return false, the earlier test asserts that no elements in the
321             // extended tail violate this assumption. Otherwise l >=, finally
322             // the case where the values are potentially equal needs to be considered
323             // and false returned as well
324             let mut equal = l_len == r_len;
325             for (&l, &r) in lhs_slice.iter().zip(rhs_slice.iter()) {
326                 if l < r {
327                     return false;
328                 } else if l > r {
329                     equal = false;
330                 }
331             }
332             !equal
333         } else {
334             false
335         }
336     }
337 
ge(&self, other: &VClock) -> bool338     fn ge(&self, other: &VClock) -> bool {
339         // Load the values as slices
340         let lhs_slice = self.as_slice();
341         let rhs_slice = other.as_slice();
342 
343         // If r_len > l_len then at least one element
344         // in r_len is > than l_len, therefore the result
345         // is either Some(Less) or None, so return false
346         // early.
347         let l_len = lhs_slice.len();
348         let r_len = rhs_slice.len();
349         if l_len >= r_len {
350             // If any elements on the left are less than the right
351             // then the result is None or Some(Less), both of which
352             // return false, the earlier test asserts that no elements in the
353             // extended tail violate this assumption. Otherwise l >= r
354             !lhs_slice.iter().zip(rhs_slice.iter()).any(|(&l, &r)| l < r)
355         } else {
356             false
357         }
358     }
359 }
360 
361 impl Index<VectorIdx> for VClock {
362     type Output = VTimestamp;
363 
364     #[inline]
index(&self, index: VectorIdx) -> &VTimestamp365     fn index(&self, index: VectorIdx) -> &VTimestamp {
366         self.as_slice().get(index.to_u32() as usize).unwrap_or(&VTimestamp::ZERO)
367     }
368 }
369 
370 impl IndexMut<VectorIdx> for VClock {
371     #[inline]
index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp372     fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp {
373         self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap()
374     }
375 }
376 
377 /// Test vector clock ordering operations
378 ///  data-race detection is tested in the external
379 ///  test suite
380 #[cfg(test)]
381 mod tests {
382 
383     use super::{VClock, VTimestamp, VectorIdx};
384     use rustc_span::DUMMY_SP;
385     use std::cmp::Ordering;
386 
387     #[test]
test_equal()388     fn test_equal() {
389         let mut c1 = VClock::default();
390         let mut c2 = VClock::default();
391         assert_eq!(c1, c2);
392         c1.increment_index(VectorIdx(5), DUMMY_SP);
393         assert_ne!(c1, c2);
394         c2.increment_index(VectorIdx(53), DUMMY_SP);
395         assert_ne!(c1, c2);
396         c1.increment_index(VectorIdx(53), DUMMY_SP);
397         assert_ne!(c1, c2);
398         c2.increment_index(VectorIdx(5), DUMMY_SP);
399         assert_eq!(c1, c2);
400     }
401 
402     #[test]
test_partial_order()403     fn test_partial_order() {
404         // Small test
405         assert_order(&[1], &[1], Some(Ordering::Equal));
406         assert_order(&[1], &[2], Some(Ordering::Less));
407         assert_order(&[2], &[1], Some(Ordering::Greater));
408         assert_order(&[1], &[1, 2], Some(Ordering::Less));
409         assert_order(&[2], &[1, 2], None);
410 
411         // Misc tests
412         assert_order(&[400], &[0, 1], None);
413 
414         // Large test
415         assert_order(
416             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
417             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0],
418             Some(Ordering::Equal),
419         );
420         assert_order(
421             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
422             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 0],
423             Some(Ordering::Less),
424         );
425         assert_order(
426             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11],
427             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0],
428             Some(Ordering::Greater),
429         );
430         assert_order(
431             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11],
432             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 0],
433             None,
434         );
435         assert_order(
436             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9],
437             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0],
438             Some(Ordering::Less),
439         );
440         assert_order(
441             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9],
442             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 0],
443             Some(Ordering::Less),
444         );
445     }
446 
from_slice(mut slice: &[u32]) -> VClock447     fn from_slice(mut slice: &[u32]) -> VClock {
448         while let Some(0) = slice.last() {
449             slice = &slice[..slice.len() - 1]
450         }
451         VClock(slice.iter().copied().map(|time| VTimestamp { time, span: DUMMY_SP }).collect())
452     }
453 
assert_order(l: &[u32], r: &[u32], o: Option<Ordering>)454     fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) {
455         let l = from_slice(l);
456         let r = from_slice(r);
457 
458         //Test partial_cmp
459         let compare = l.partial_cmp(&r);
460         assert_eq!(compare, o, "Invalid comparison\n l: {l:?}\n r: {r:?}");
461         let alt_compare = r.partial_cmp(&l);
462         assert_eq!(
463             alt_compare,
464             o.map(Ordering::reverse),
465             "Invalid alt comparison\n l: {l:?}\n r: {r:?}"
466         );
467 
468         //Test operators with faster implementations
469         assert_eq!(
470             matches!(compare, Some(Ordering::Less)),
471             l < r,
472             "Invalid (<):\n l: {l:?}\n r: {r:?}"
473         );
474         assert_eq!(
475             matches!(compare, Some(Ordering::Less) | Some(Ordering::Equal)),
476             l <= r,
477             "Invalid (<=):\n l: {l:?}\n r: {r:?}"
478         );
479         assert_eq!(
480             matches!(compare, Some(Ordering::Greater)),
481             l > r,
482             "Invalid (>):\n l: {l:?}\n r: {r:?}"
483         );
484         assert_eq!(
485             matches!(compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
486             l >= r,
487             "Invalid (>=):\n l: {l:?}\n r: {r:?}"
488         );
489         assert_eq!(
490             matches!(alt_compare, Some(Ordering::Less)),
491             r < l,
492             "Invalid alt (<):\n l: {l:?}\n r: {r:?}"
493         );
494         assert_eq!(
495             matches!(alt_compare, Some(Ordering::Less) | Some(Ordering::Equal)),
496             r <= l,
497             "Invalid alt (<=):\n l: {l:?}\n r: {r:?}"
498         );
499         assert_eq!(
500             matches!(alt_compare, Some(Ordering::Greater)),
501             r > l,
502             "Invalid alt (>):\n l: {l:?}\n r: {r:?}"
503         );
504         assert_eq!(
505             matches!(alt_compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
506             r >= l,
507             "Invalid alt (>=):\n l: {l:?}\n r: {r:?}"
508         );
509     }
510 }
511