Spaces:
Build error
Build error
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; | |
use std::sync::Arc; | |
pub struct ClockSet { | |
clocks: Vec<Arc<Clock>>, | |
} | |
impl ClockSet { | |
pub fn new() -> Self { | |
Self::default() | |
} | |
/// Get the first available clock from the set, or create a new one. | |
pub fn get_clock(&mut self) -> ClockGuard { | |
for (id, clock) in self.clocks.iter().enumerate() { | |
if clock.try_lock() { | |
return ClockGuard::new(id as u32, clock.clone()); | |
} | |
} | |
let id = self.clocks.len() as u32; | |
let clock = Arc::new(Clock::new_locked()); | |
self.clocks.push(clock.clone()); | |
ClockGuard::new(id, clock) | |
} | |
} | |
pub struct ClockGuard { | |
id: u32, | |
clock: Arc<Clock>, | |
} | |
impl ClockGuard { | |
fn new(id: u32, clock: Arc<Clock>) -> Self { | |
Self { id, clock } | |
} | |
pub fn id(&self) -> u32 { | |
self.id | |
} | |
/// Return current clock tick. | |
pub fn current_tick(&self) -> Option<u64> { | |
self.clock.current_tick() | |
} | |
/// Advance clock by a single tick and return current tick. | |
pub fn tick_once(&mut self) -> u64 { | |
self.clock.tick_once() | |
} | |
/// Advance clock to `new_tick`, if `new_tick` is newer than current tick. | |
pub fn advance_to(&mut self, new_tick: u64) { | |
self.clock.advance_to(new_tick) | |
} | |
} | |
impl Drop for ClockGuard { | |
fn drop(&mut self) { | |
self.clock.release(); | |
} | |
} | |
struct Clock { | |
/// Tracks the *next* clock tick | |
next_tick: AtomicU64, | |
available: AtomicBool, | |
} | |
impl Clock { | |
fn new_locked() -> Self { | |
Self { | |
next_tick: 0.into(), | |
available: false.into(), | |
} | |
} | |
/// Return current clock tick. | |
fn current_tick(&self) -> Option<u64> { | |
let next_tick = self.next_tick.load(Ordering::Relaxed); | |
next_tick.checked_sub(1) | |
} | |
/// Advance clock by a single tick and return current tick. | |
/// | |
/// # Thread safety | |
/// | |
/// Clock *has to* be locked (using [`Clock::lock`]) before calling `tick_once`! | |
fn tick_once(&self) -> u64 { | |
// `Clock` tracks *next* tick, so we increment `next_tick` by 1 and return *previous* value | |
// of `next_tick` (which is *exactly* what `fetch_add(1)` does) | |
let current_tick = self.next_tick.fetch_add(1, Ordering::Relaxed); | |
// If `current_tick` is `0`, then "revert" `next_tick` back to `0`. | |
// We expect that `advance_to` would be used to advance "past" the initial `0`. | |
// | |
// Executing multiple atomic operations sequentially is not strictly thread-safe, | |
// but we expect that `Clock` would be "locked" before calling `tick_once`. | |
if current_tick == 0 { | |
self.next_tick.store(0, Ordering::Relaxed); | |
} | |
current_tick | |
} | |
/// Advance clock to `new_tick`, if `new_tick` is newer than current tick. | |
fn advance_to(&self, new_tick: u64) { | |
// `Clock` tracks *next* tick, so if we want to advance *current* tick to `new_tick`, | |
// we have to advance `next_tick` to `new_tick + 1` | |
self.next_tick.fetch_max(new_tick + 1, Ordering::Relaxed); | |
} | |
/// Try to acquire exclusive lock over this clock. | |
/// | |
/// Returns `true` if the lock was successfully acquired, or `false` if the clock is already | |
/// locked. | |
fn try_lock(&self) -> bool { | |
self.available.swap(false, Ordering::Relaxed) | |
} | |
/// Release the exclusive lock over this clock. | |
/// | |
/// No-op if the clock is not locked. | |
fn release(&self) { | |
self.available.store(true, Ordering::Relaxed); | |
} | |
} | |
mod tests { | |
use std::iter; | |
use rand::prelude::*; | |
use super::*; | |
/// Tick a single clock, it should increment after we advance it from 0 (or higher). | |
fn test_clock_set_single_tick() { | |
let mut clock_set = ClockSet::new(); | |
// Don't tick from 0 unless we explicitly advance | |
assert_eq!(clock_set.get_clock().tick_once(), 0); | |
assert_eq!(clock_set.get_clock().tick_once(), 0); | |
assert_eq!(clock_set.get_clock().tick_once(), 0); | |
clock_set.get_clock().advance_to(0); | |
// Following ticks should increment | |
assert_eq!(clock_set.get_clock().tick_once(), 1); | |
assert_eq!(clock_set.get_clock().tick_once(), 2); | |
assert_eq!(clock_set.get_clock().tick_once(), 3); | |
assert_eq!(clock_set.get_clock().tick_once(), 4); | |
} | |
/// Test a single clock, but tick it multiple times on the same guard. | |
fn test_clock_set_single_tick_twice() { | |
let mut clock_set = ClockSet::new(); | |
// Don't tick from 0 unless we explicitly advance | |
{ | |
let mut clock = clock_set.get_clock(); | |
assert_eq!(clock.tick_once(), 0); | |
assert_eq!(clock.tick_once(), 0); | |
clock.advance_to(0); | |
} | |
// Following ticks should increment | |
{ | |
let mut clock = clock_set.get_clock(); | |
assert_eq!(clock.tick_once(), 1); | |
assert_eq!(clock.tick_once(), 2); | |
assert_eq!(clock.tick_once(), 3); | |
assert_eq!(clock.tick_once(), 4); | |
} | |
} | |
/// Advance a clock to a higher number, which should increase it. | |
fn test_clock_set_single_advance_high() { | |
let mut clock_set = ClockSet::new(); | |
// Bring the clock up to 4 | |
assert_eq!(clock_set.get_clock().tick_once(), 0); | |
clock_set.get_clock().advance_to(0); | |
assert_eq!(clock_set.get_clock().tick_once(), 1); | |
assert_eq!(clock_set.get_clock().tick_once(), 2); | |
assert_eq!(clock_set.get_clock().tick_once(), 3); | |
assert_eq!(clock_set.get_clock().tick_once(), 4); | |
// If we advance to 100, we should continue from 101 | |
clock_set.get_clock().advance_to(100); | |
assert_eq!(clock_set.get_clock().tick_once(), 101); | |
} | |
/// Advance a clock to a lower number, which should not do anything. | |
fn test_clock_set_single_advance_low() { | |
let mut clock_set = ClockSet::new(); | |
// Bring the clock up to 4 | |
assert_eq!(clock_set.get_clock().tick_once(), 0); | |
clock_set.get_clock().advance_to(0); | |
assert_eq!(clock_set.get_clock().tick_once(), 1); | |
assert_eq!(clock_set.get_clock().tick_once(), 2); | |
assert_eq!(clock_set.get_clock().tick_once(), 3); | |
assert_eq!(clock_set.get_clock().tick_once(), 4); | |
// If we advance to a low number, just continue | |
clock_set.get_clock().advance_to(0); | |
assert_eq!(clock_set.get_clock().tick_once(), 5); | |
clock_set.get_clock().advance_to(1); | |
assert_eq!(clock_set.get_clock().tick_once(), 6); | |
} | |
/// Test multiple clocks in various configurations. | |
fn test_clock_multi_tick() { | |
let mut clock_set = ClockSet::new(); | |
// 2 parallel operations, that fails and doesn't advance | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 0); | |
assert_eq!(clock2.tick_once(), 0); | |
} | |
// 2 parallel operations | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 0); | |
assert_eq!(clock2.tick_once(), 0); | |
clock1.advance_to(0); | |
clock2.advance_to(0); | |
} | |
// 1 operation | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 1); | |
clock1.advance_to(1); | |
} | |
// 1 operation, without advancing should still tick | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 2); | |
} | |
// 2 parallel operations | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 3); | |
assert_eq!(clock2.tick_once(), 1); | |
clock1.advance_to(3); | |
clock2.advance_to(1); | |
} | |
// 3 parallel operations, but clock 2 was much newer on some node | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 4); | |
assert_eq!(clock2.tick_once(), 2); | |
assert_eq!(clock3.tick_once(), 0); | |
clock1.advance_to(4); | |
clock2.advance_to(10); | |
clock3.advance_to(0); | |
} | |
// 3 parallel operations, advancing in a different order should not matter | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 5); | |
assert_eq!(clock2.tick_once(), 11); | |
assert_eq!(clock3.tick_once(), 1); | |
clock3.advance_to(1); | |
clock2.advance_to(11); | |
clock1.advance_to(5); | |
} | |
// 3 parallel operations, advancing just some should still tick all | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 6); | |
assert_eq!(clock2.tick_once(), 12); | |
assert_eq!(clock3.tick_once(), 2); | |
clock2.advance_to(12); | |
} | |
// 1 operation | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 7); | |
} | |
// Test final state of all clocks | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
let mut clock4 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 8); | |
assert_eq!(clock2.tick_once(), 13); | |
assert_eq!(clock3.tick_once(), 3); | |
assert_eq!(clock4.tick_once(), 0); | |
} | |
} | |
/// Test a number of parallel operations with some running for a long time. Clocks are resolved | |
/// unordered. | |
fn test_clock_set_long_running_unordered() { | |
let mut clock_set = ClockSet::new(); | |
// Clock 1 runs for a long while | |
let mut clock1 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 0); | |
// 2 quick parallel operations | |
{ | |
let mut clock2 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
assert_eq!(clock2.tick_once(), 0); | |
assert_eq!(clock3.tick_once(), 0); | |
clock2.advance_to(0); | |
clock3.advance_to(0); | |
} | |
// Clock 2 runs for a long while | |
let clock2 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 0); | |
// 2 quick parallel operations | |
{ | |
let mut clock3 = clock_set.get_clock(); | |
let mut clock4 = clock_set.get_clock(); | |
assert_eq!(clock3.tick_once(), 1); | |
assert_eq!(clock4.tick_once(), 0); | |
clock4.advance_to(0); | |
} | |
// Clock 1 finally resolves | |
clock1.advance_to(0); | |
drop(clock1); | |
// 3 quick parallel operations | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
let mut clock4 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 1); | |
assert_eq!(clock3.tick_once(), 2); | |
assert_eq!(clock4.tick_once(), 1); | |
} | |
// Clock 2 finally resolves | |
drop(clock2); | |
// Test final state of all clocks | |
{ | |
let mut clock1 = clock_set.get_clock(); | |
let mut clock2 = clock_set.get_clock(); | |
let mut clock3 = clock_set.get_clock(); | |
let mut clock4 = clock_set.get_clock(); | |
let mut clock5 = clock_set.get_clock(); | |
assert_eq!(clock1.tick_once(), 2); | |
assert_eq!(clock2.tick_once(), 1); | |
assert_eq!(clock3.tick_once(), 3); | |
assert_eq!(clock4.tick_once(), 2); | |
assert_eq!(clock5.tick_once(), 0); | |
} | |
} | |
/// Test and increment a lot of clocks, but at some point 10% of the clocks gets stuck. | |
fn test_clock_set_many() { | |
const N: usize = 5000; | |
let mut clock_set = ClockSet::new(); | |
// Tick all clocks past 0 | |
{ | |
let mut clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N) | |
.collect::<Vec<_>>(); | |
clocks.shuffle(&mut rand::thread_rng()); | |
for clock in &mut clocks { | |
assert_eq!(clock.tick_once(), 0); | |
clock.advance_to(0); | |
} | |
} | |
// Tick all clocks 10 times | |
{ | |
for tick in 0..10 { | |
let mut clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N) | |
.collect::<Vec<_>>(); | |
clocks.shuffle(&mut rand::thread_rng()); | |
for clock in &mut clocks { | |
assert_eq!(clock.tick_once(), 1 + tick); | |
} | |
} | |
} | |
// Now the first 10% gets stuck | |
let mut stuck_clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N / 10) | |
.collect::<Vec<_>>(); | |
for clock in stuck_clocks.iter_mut() { | |
assert_eq!(clock.tick_once(), 11); | |
} | |
// Tick all other clocks 10 times | |
{ | |
for tick in 0..10 { | |
let mut clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N - (N / 10)) | |
.collect::<Vec<_>>(); | |
clocks.shuffle(&mut rand::thread_rng()); | |
for clock in clocks.iter_mut() { | |
assert_eq!(clock.tick_once(), 11 + tick); | |
} | |
} | |
} | |
// All stuck clocks resolve | |
drop(stuck_clocks); | |
// Test all clocks | |
{ | |
let mut stuck_clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N / 10) | |
.collect::<Vec<_>>(); | |
let mut clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N - (N / 10)) | |
.collect::<Vec<_>>(); | |
for clock in stuck_clocks.iter_mut() { | |
assert_eq!(clock.tick_once(), 12); | |
} | |
for clock in clocks.iter_mut() { | |
assert_eq!(clock.tick_once(), 21); | |
} | |
} | |
} | |
/// Test and increment a lot of clocks, but each iteration, we get on more clock stuck. | |
fn test_clock_set_many_staggered_stuck() { | |
const N: usize = 500; | |
let mut clock_set = ClockSet::new(); | |
let mut stuck_clocks = Vec::new(); | |
for i in 0..N { | |
let mut clock_to_stuck = clock_set.get_clock(); | |
let mut clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N - i - 1) | |
.collect::<Vec<_>>(); | |
if clock_to_stuck.tick_once() == 0 { | |
clock_to_stuck.advance_to(0); | |
} | |
for clock in clocks.iter_mut() { | |
if clock.tick_once() == 0 { | |
clock.advance_to(0); | |
} | |
} | |
stuck_clocks.push(clock_to_stuck); | |
} | |
// All stuck clocks resolve now | |
drop(stuck_clocks); | |
// Test all clocks | |
{ | |
let mut clocks = iter::repeat_with(|| clock_set.get_clock()) | |
.take(N) | |
.enumerate() | |
.collect::<Vec<_>>(); | |
for (i, clock) in clocks.iter_mut() { | |
assert_eq!(clock.tick_once(), *i as u64 + 1); | |
} | |
} | |
} | |
} | |