Skip to main content

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