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