miri/concurrency/
vector_clock.rs

1use std::cmp::Ordering;
2use std::fmt::Debug;
3use std::ops::{Index, Shr};
4
5use rustc_index::Idx;
6use rustc_span::{DUMMY_SP, Span, SpanData};
7use smallvec::SmallVec;
8
9use super::data_race::NaReadType;
10use crate::helpers::ToUsize;
11
12/// A vector clock index, this is associated with a thread id
13/// but in some cases one vector index may be shared with
14/// multiple thread ids if it's safe to do so.
15#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
16pub(super) struct VectorIdx(u32);
17
18impl VectorIdx {
19    #[inline(always)]
20    fn to_u32(self) -> u32 {
21        self.0
22    }
23}
24
25impl Idx for VectorIdx {
26    #[inline]
27    fn new(idx: usize) -> Self {
28        VectorIdx(u32::try_from(idx).unwrap())
29    }
30
31    #[inline]
32    fn index(self) -> usize {
33        usize::try_from(self.0).unwrap()
34    }
35}
36
37impl From<u32> for VectorIdx {
38    #[inline]
39    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.
46const 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)]
52pub(super) struct VTimestamp {
53    /// The lowest bit indicates read type, the rest is the time.
54    /// `1` indicates a retag read, `0` a regular read.
55    time_and_read_type: u32,
56    pub span: Span,
57}
58
59impl VTimestamp {
60    pub const ZERO: VTimestamp = VTimestamp::new(0, NaReadType::Read, DUMMY_SP);
61
62    #[inline]
63    const fn encode_time_and_read_type(time: u32, read_type: NaReadType) -> u32 {
64        let read_type_bit = match read_type {
65            NaReadType::Read => 0,
66            NaReadType::Retag => 1,
67        };
68        // Put the `read_type` in the lowest bit and `time` in the rest
69        read_type_bit | time.checked_mul(2).expect("Vector clock overflow")
70    }
71
72    #[inline]
73    const fn new(time: u32, read_type: NaReadType, span: Span) -> Self {
74        Self { time_and_read_type: Self::encode_time_and_read_type(time, read_type), span }
75    }
76
77    #[inline]
78    fn time(&self) -> u32 {
79        self.time_and_read_type.shr(1)
80    }
81
82    #[inline]
83    fn set_time(&mut self, time: u32) {
84        self.time_and_read_type = Self::encode_time_and_read_type(time, self.read_type());
85    }
86
87    #[inline]
88    pub(super) fn read_type(&self) -> NaReadType {
89        if self.time_and_read_type & 1 == 0 { NaReadType::Read } else { NaReadType::Retag }
90    }
91
92    #[inline]
93    pub(super) fn set_read_type(&mut self, read_type: NaReadType) {
94        self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type);
95    }
96
97    #[inline]
98    pub(super) fn span_data(&self) -> SpanData {
99        self.span.data()
100    }
101}
102
103impl PartialEq for VTimestamp {
104    fn eq(&self, other: &Self) -> bool {
105        self.time() == other.time()
106    }
107}
108
109impl Eq for VTimestamp {}
110
111impl PartialOrd for VTimestamp {
112    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
113        Some(self.cmp(other))
114    }
115}
116
117impl Ord for VTimestamp {
118    fn cmp(&self, other: &Self) -> Ordering {
119        self.time().cmp(&other.time())
120    }
121}
122
123/// A vector clock for detecting data-races, this is conceptually
124/// a map from a vector index (and thus a thread id) to a timestamp.
125/// The compare operations require that the invariant that the last
126/// element in the internal timestamp slice must not be a 0, hence
127/// all zero vector clocks are always represented by the empty slice;
128/// and allows for the implementation of compare operations to short
129/// circuit the calculation and return the correct result faster,
130/// also this means that there is only one unique valid length
131/// for each set of vector clock values and hence the PartialEq
132/// and Eq derivations are correct.
133///
134/// This means we cannot represent a clock where the last entry is a timestamp-0 read that occurs
135/// because of a retag. That's fine, all it does is risk wrong diagnostics in a extreme corner case.
136#[derive(PartialEq, Eq, Default, Debug)]
137pub struct VClock(SmallVec<[VTimestamp; SMALL_VECTOR]>);
138
139impl VClock {
140    /// Create a new vector clock containing all zeros except
141    /// for a value at the given index
142    pub(super) fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock {
143        if timestamp.time() == 0 {
144            return VClock::default();
145        }
146        let len = index.index() + 1;
147        let mut vec = smallvec::smallvec![VTimestamp::ZERO; len];
148        vec[index.index()] = timestamp;
149        VClock(vec)
150    }
151
152    /// Load the internal timestamp slice in the vector clock
153    #[inline]
154    pub(super) fn as_slice(&self) -> &[VTimestamp] {
155        debug_assert!(self.0.last().is_none_or(|t| t.time() != 0));
156        self.0.as_slice()
157    }
158
159    #[inline]
160    pub(super) fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp {
161        self.0.as_mut_slice().get_mut(index.to_u32().to_usize()).unwrap()
162    }
163
164    /// Get a mutable slice to the internal vector with minimum `min_len`
165    /// elements. To preserve invariants, the caller must modify
166    /// the `min_len`-1 nth element to a non-zero value
167    #[inline]
168    fn get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp] {
169        if self.0.len() < min_len {
170            self.0.resize(min_len, VTimestamp::ZERO);
171        }
172        assert!(self.0.len() >= min_len);
173        self.0.as_mut_slice()
174    }
175
176    /// Increment the vector clock at a known index
177    /// this will panic if the vector index overflows
178    #[inline]
179    pub(super) fn increment_index(&mut self, idx: VectorIdx, current_span: Span) {
180        let idx = idx.index();
181        let mut_slice = self.get_mut_with_min_len(idx + 1);
182        let idx_ref = &mut mut_slice[idx];
183        idx_ref.set_time(idx_ref.time().checked_add(1).expect("Vector clock overflow"));
184        if !current_span.is_dummy() {
185            idx_ref.span = current_span;
186        }
187    }
188
189    // Join the two vector clocks together, this
190    // sets each vector element to the maximum value
191    // of that element in either of the two source elements.
192    pub fn join(&mut self, other: &Self) {
193        let rhs_slice = other.as_slice();
194        let lhs_slice = self.get_mut_with_min_len(rhs_slice.len());
195        for (l, &r) in lhs_slice.iter_mut().zip(rhs_slice.iter()) {
196            let l_span = l.span;
197            let r_span = r.span;
198            *l = r.max(*l);
199            l.span = l.span.substitute_dummy(r_span).substitute_dummy(l_span);
200        }
201    }
202
203    /// Set the element at the current index of the vector. May only increase elements.
204    pub(super) fn set_at_index(&mut self, other: &Self, idx: VectorIdx) {
205        let new_timestamp = other[idx];
206        // Setting to 0 is different, since the last element cannot be 0.
207        if new_timestamp.time() == 0 {
208            if idx.index() >= self.0.len() {
209                // This index does not even exist yet in our clock. Just do nothing.
210                return;
211            }
212            // This changes an existing element. Since it can only increase, that
213            // can never make the last element 0.
214        }
215
216        let mut_slice = self.get_mut_with_min_len(idx.index() + 1);
217        let mut_timestamp = &mut mut_slice[idx.index()];
218
219        let prev_span = mut_timestamp.span;
220
221        assert!(*mut_timestamp <= new_timestamp, "set_at_index: may only increase the timestamp");
222        *mut_timestamp = new_timestamp;
223
224        let span = &mut mut_timestamp.span;
225        *span = span.substitute_dummy(prev_span);
226    }
227
228    /// Set the vector to the all-zero vector
229    #[inline]
230    pub(super) fn set_zero_vector(&mut self) {
231        self.0.clear();
232    }
233}
234
235impl Clone for VClock {
236    fn clone(&self) -> Self {
237        VClock(self.0.clone())
238    }
239
240    // Optimized clone-from, can be removed
241    // and replaced with a derive once a similar
242    // optimization is inserted into SmallVec's
243    // clone implementation.
244    fn clone_from(&mut self, source: &Self) {
245        let source_slice = source.as_slice();
246        self.0.clear();
247        self.0.extend_from_slice(source_slice);
248    }
249}
250
251impl PartialOrd for VClock {
252    fn partial_cmp(&self, other: &VClock) -> Option<Ordering> {
253        // Load the values as slices
254        let lhs_slice = self.as_slice();
255        let rhs_slice = other.as_slice();
256
257        // Iterate through the combined vector slice continuously updating
258        // the value of `order` to the current comparison of the vector from
259        // index 0 to the currently checked index.
260        // An Equal ordering can be converted into Less or Greater ordering
261        // on finding an element that is less than or greater than the other
262        // but if one Greater and one Less element-wise comparison is found
263        // then no ordering is possible and so directly return an ordering
264        // of None.
265        let mut iter = lhs_slice.iter().zip(rhs_slice.iter());
266        let mut order = match iter.next() {
267            Some((lhs, rhs)) => lhs.cmp(rhs),
268            None => Ordering::Equal,
269        };
270        for (l, r) in iter {
271            match order {
272                Ordering::Equal => order = l.cmp(r),
273                Ordering::Less =>
274                    if l > r {
275                        return None;
276                    },
277                Ordering::Greater =>
278                    if l < r {
279                        return None;
280                    },
281            }
282        }
283
284        // Now test if either left or right have trailing elements,
285        // by the invariant the trailing elements have at least 1
286        // non zero value, so no additional calculation is required
287        // to determine the result of the PartialOrder.
288        let l_len = lhs_slice.len();
289        let r_len = rhs_slice.len();
290        match l_len.cmp(&r_len) {
291            // Equal means no additional elements: return current order
292            Ordering::Equal => Some(order),
293            // Right has at least 1 element > than the implicit 0,
294            // so the only valid values are Ordering::Less or None.
295            Ordering::Less =>
296                match order {
297                    Ordering::Less | Ordering::Equal => Some(Ordering::Less),
298                    Ordering::Greater => None,
299                },
300            // Left has at least 1 element > than the implicit 0,
301            // so the only valid values are Ordering::Greater or None.
302            Ordering::Greater =>
303                match order {
304                    Ordering::Greater | Ordering::Equal => Some(Ordering::Greater),
305                    Ordering::Less => None,
306                },
307        }
308    }
309
310    fn lt(&self, other: &VClock) -> bool {
311        // Load the values as slices
312        let lhs_slice = self.as_slice();
313        let rhs_slice = other.as_slice();
314
315        // If l_len > r_len then at least one element
316        // in l_len is > than r_len, therefore the result
317        // is either Some(Greater) or None, so return false
318        // early.
319        let l_len = lhs_slice.len();
320        let r_len = rhs_slice.len();
321        if l_len <= r_len {
322            // If any elements on the left are greater than the right
323            // then the result is None or Some(Greater), both of which
324            // return false, the earlier test asserts that no elements in the
325            // extended tail violate this assumption. Otherwise l <= r, finally
326            // the case where the values are potentially equal needs to be considered
327            // and false returned as well
328            let mut equal = l_len == r_len;
329            for (&l, &r) in lhs_slice.iter().zip(rhs_slice.iter()) {
330                if l > r {
331                    return false;
332                } else if l < r {
333                    equal = false;
334                }
335            }
336            !equal
337        } else {
338            false
339        }
340    }
341
342    fn le(&self, other: &VClock) -> bool {
343        // Load the values as slices
344        let lhs_slice = self.as_slice();
345        let rhs_slice = other.as_slice();
346
347        // If l_len > r_len then at least one element
348        // in l_len is > than r_len, therefore the result
349        // is either Some(Greater) or None, so return false
350        // early.
351        let l_len = lhs_slice.len();
352        let r_len = rhs_slice.len();
353        if l_len <= r_len {
354            // If any elements on the left are greater than the right
355            // then the result is None or Some(Greater), both of which
356            // return false, the earlier test asserts that no elements in the
357            // extended tail violate this assumption. Otherwise l <= r
358            !lhs_slice.iter().zip(rhs_slice.iter()).any(|(&l, &r)| l > r)
359        } else {
360            false
361        }
362    }
363
364    fn gt(&self, other: &VClock) -> bool {
365        // Load the values as slices
366        let lhs_slice = self.as_slice();
367        let rhs_slice = other.as_slice();
368
369        // If r_len > l_len then at least one element
370        // in r_len is > than l_len, therefore the result
371        // is either Some(Less) or None, so return false
372        // early.
373        let l_len = lhs_slice.len();
374        let r_len = rhs_slice.len();
375        if l_len >= r_len {
376            // If any elements on the left are less than the right
377            // then the result is None or Some(Less), both of which
378            // return false, the earlier test asserts that no elements in the
379            // extended tail violate this assumption. Otherwise l >=, finally
380            // the case where the values are potentially equal needs to be considered
381            // and false returned as well
382            let mut equal = l_len == r_len;
383            for (&l, &r) in lhs_slice.iter().zip(rhs_slice.iter()) {
384                if l < r {
385                    return false;
386                } else if l > r {
387                    equal = false;
388                }
389            }
390            !equal
391        } else {
392            false
393        }
394    }
395
396    fn ge(&self, other: &VClock) -> bool {
397        // Load the values as slices
398        let lhs_slice = self.as_slice();
399        let rhs_slice = other.as_slice();
400
401        // If r_len > l_len then at least one element
402        // in r_len is > than l_len, therefore the result
403        // is either Some(Less) or None, so return false
404        // early.
405        let l_len = lhs_slice.len();
406        let r_len = rhs_slice.len();
407        if l_len >= r_len {
408            // If any elements on the left are less than the right
409            // then the result is None or Some(Less), both of which
410            // return false, the earlier test asserts that no elements in the
411            // extended tail violate this assumption. Otherwise l >= r
412            !lhs_slice.iter().zip(rhs_slice.iter()).any(|(&l, &r)| l < r)
413        } else {
414            false
415        }
416    }
417}
418
419impl Index<VectorIdx> for VClock {
420    type Output = VTimestamp;
421
422    #[inline]
423    fn index(&self, index: VectorIdx) -> &VTimestamp {
424        self.as_slice().get(index.to_u32().to_usize()).unwrap_or(&VTimestamp::ZERO)
425    }
426}
427
428/// Test vector clock ordering operations
429///  data-race detection is tested in the external
430///  test suite
431#[cfg(test)]
432mod tests {
433    use std::cmp::Ordering;
434
435    use rustc_span::DUMMY_SP;
436
437    use super::{VClock, VTimestamp, VectorIdx};
438    use crate::concurrency::data_race::NaReadType;
439
440    #[test]
441    fn test_equal() {
442        let mut c1 = VClock::default();
443        let mut c2 = VClock::default();
444        assert_eq!(c1, c2);
445        c1.increment_index(VectorIdx(5), DUMMY_SP);
446        assert_ne!(c1, c2);
447        c2.increment_index(VectorIdx(53), DUMMY_SP);
448        assert_ne!(c1, c2);
449        c1.increment_index(VectorIdx(53), DUMMY_SP);
450        assert_ne!(c1, c2);
451        c2.increment_index(VectorIdx(5), DUMMY_SP);
452        assert_eq!(c1, c2);
453    }
454
455    #[test]
456    fn test_partial_order() {
457        // Small test
458        assert_order(&[1], &[1], Some(Ordering::Equal));
459        assert_order(&[1], &[2], Some(Ordering::Less));
460        assert_order(&[2], &[1], Some(Ordering::Greater));
461        assert_order(&[1], &[1, 2], Some(Ordering::Less));
462        assert_order(&[2], &[1, 2], None);
463
464        // Misc tests
465        assert_order(&[400], &[0, 1], None);
466
467        // Large test
468        assert_order(
469            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
470            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0],
471            Some(Ordering::Equal),
472        );
473        assert_order(
474            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
475            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 0],
476            Some(Ordering::Less),
477        );
478        assert_order(
479            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11],
480            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0],
481            Some(Ordering::Greater),
482        );
483        assert_order(
484            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11],
485            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 0],
486            None,
487        );
488        assert_order(
489            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9],
490            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0],
491            Some(Ordering::Less),
492        );
493        assert_order(
494            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9],
495            &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 0],
496            Some(Ordering::Less),
497        );
498    }
499
500    fn from_slice(mut slice: &[u32]) -> VClock {
501        while let Some(0) = slice.last() {
502            slice = &slice[..slice.len() - 1]
503        }
504        VClock(
505            slice
506                .iter()
507                .copied()
508                .map(|time| VTimestamp::new(time, NaReadType::Read, DUMMY_SP))
509                .collect(),
510        )
511    }
512
513    fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) {
514        let l = from_slice(l);
515        let r = from_slice(r);
516
517        //Test partial_cmp
518        let compare = l.partial_cmp(&r);
519        assert_eq!(compare, o, "Invalid comparison\n l: {l:?}\n r: {r:?}");
520        let alt_compare = r.partial_cmp(&l);
521        assert_eq!(
522            alt_compare,
523            o.map(Ordering::reverse),
524            "Invalid alt comparison\n l: {l:?}\n r: {r:?}"
525        );
526
527        //Test operators with faster implementations
528        assert_eq!(
529            matches!(compare, Some(Ordering::Less)),
530            l < r,
531            "Invalid (<):\n l: {l:?}\n r: {r:?}"
532        );
533        assert_eq!(
534            matches!(compare, Some(Ordering::Less) | Some(Ordering::Equal)),
535            l <= r,
536            "Invalid (<=):\n l: {l:?}\n r: {r:?}"
537        );
538        assert_eq!(
539            matches!(compare, Some(Ordering::Greater)),
540            l > r,
541            "Invalid (>):\n l: {l:?}\n r: {r:?}"
542        );
543        assert_eq!(
544            matches!(compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
545            l >= r,
546            "Invalid (>=):\n l: {l:?}\n r: {r:?}"
547        );
548        assert_eq!(
549            matches!(alt_compare, Some(Ordering::Less)),
550            r < l,
551            "Invalid alt (<):\n l: {l:?}\n r: {r:?}"
552        );
553        assert_eq!(
554            matches!(alt_compare, Some(Ordering::Less) | Some(Ordering::Equal)),
555            r <= l,
556            "Invalid alt (<=):\n l: {l:?}\n r: {r:?}"
557        );
558        assert_eq!(
559            matches!(alt_compare, Some(Ordering::Greater)),
560            r > l,
561            "Invalid alt (>):\n l: {l:?}\n r: {r:?}"
562        );
563        assert_eq!(
564            matches!(alt_compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
565            r >= l,
566            "Invalid alt (>=):\n l: {l:?}\n r: {r:?}"
567        );
568    }
569
570    #[test]
571    fn set_index_to_0() {
572        let mut clock1 = from_slice(&[0, 1, 2, 3]);
573        let clock2 = from_slice(&[0, 2, 3, 4, 0, 5]);
574        // Naively, this would extend clock1 with a new index and set it to 0, making
575        // the last index 0. Make sure that does not happen.
576        clock1.set_at_index(&clock2, VectorIdx(4));
577        // This must not have made the last element 0.
578        assert!(clock1.0.last().unwrap().time() != 0);
579    }
580}