Skip to main content

atomr_remote/
address_uid.rs

1//! `AddressUidExtension`. akka.net: `Remote/AddressUidExtension.cs`.
2//!
3//! Each `ActorSystem` incarnation gets a fresh UID at startup. The UID
4//! travels in every association handshake; if we see the same `Address`
5//! associate with a *different* UID than we last knew, the peer crashed
6//! and was restarted, so we drop our previous endpoint state, surface
7//! `Terminated` for everything we were watching there, and start fresh.
8
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::sync::Arc;
11use std::time::{SystemTime, UNIX_EPOCH};
12
13#[derive(Debug, Clone)]
14pub struct AddressUid {
15    inner: Arc<AddressUidInner>,
16}
17
18#[derive(Debug)]
19struct AddressUidInner {
20    value: AtomicU64,
21}
22
23impl Default for AddressUid {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl AddressUid {
30    /// Pick a UID derived from the wall clock, falling back to a
31    /// monotonic counter on systems without a usable clock.
32    pub fn new() -> Self {
33        let nanos = SystemTime::now()
34            .duration_since(UNIX_EPOCH)
35            .map(|d| d.as_nanos() as u64)
36            .unwrap_or_else(|_| next_fallback());
37        // Mix in a small entropy seed so two systems started in the same
38        // nanosecond on the same machine still differ.
39        let mixed = nanos.wrapping_mul(2862933555777941757).wrapping_add(3037000493);
40        Self { inner: Arc::new(AddressUidInner { value: AtomicU64::new(mixed) }) }
41    }
42
43    pub fn get(&self) -> u64 {
44        self.inner.value.load(Ordering::Acquire)
45    }
46}
47
48fn next_fallback() -> u64 {
49    static COUNTER: AtomicU64 = AtomicU64::new(1);
50    COUNTER.fetch_add(1, Ordering::SeqCst)
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn distinct_systems_get_distinct_uids() {
59        let a = AddressUid::new();
60        std::thread::sleep(std::time::Duration::from_micros(10));
61        let b = AddressUid::new();
62        assert_ne!(a.get(), b.get());
63    }
64
65    #[test]
66    fn cloned_handle_observes_same_uid() {
67        let a = AddressUid::new();
68        let b = a.clone();
69        assert_eq!(a.get(), b.get());
70    }
71}