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