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