Skip to main content

shuttle_engine/
sync_types.rs

1use const_siphasher::sip::SipHasher;
2use std::fmt::{Debug, Display};
3use std::hash::{Hash, Hasher};
4use std::panic::Location;
5
6const CONST_CONTEXT_SIGNATURE: u64 = 0;
7
8/// A stable signature for identifying resources in Shuttle tests.
9/// This is used internally to track and differentiate between different resource instances
10/// *across* shuttle iterations. This signature hash is not *guaranteed* to be unique (two
11/// different resources may have the same hash). In particular, resources which provide
12/// `const` constructors cannot provide any *runtime* context to their signatures, so any
13/// two resources created at the same source location will collide.
14#[derive(Clone, Debug, Hash, PartialEq, Eq)]
15pub enum ResourceType {
16    Atomic,
17    BatchSemaphore,
18    Barrier,
19    Condvar,
20    Mutex,
21    RwLock,
22    Once,
23    MpscChannel,
24}
25
26#[allow(unused)]
27#[derive(Clone)]
28pub struct ResourceSignature {
29    resource_type: ResourceType,
30    static_create_location: &'static Location<'static>,
31    parent_task_signature: u64,
32    create_location_counter: u32,
33    static_create_location_hash: u64,
34    signature_hash: u64,
35}
36
37impl ResourceSignature {
38    #[track_caller]
39    pub const fn new_const(resource_type: ResourceType) -> Self {
40        let static_create_location = Location::caller();
41        Self::new(resource_type, static_create_location, CONST_CONTEXT_SIGNATURE, 1)
42    }
43
44    pub const fn new(
45        resource_type: ResourceType,
46        static_create_location: &'static Location<'static>,
47        parent_task_signature: u64,
48        create_location_counter: u32,
49    ) -> Self {
50        let mut hasher = SipHasher::new();
51        let file: &'static str = static_create_location.file();
52        hasher.hash(file.as_bytes());
53        hasher.write_u32(static_create_location.line());
54        hasher.write_u32(static_create_location.column());
55        let static_create_location_hash = hasher.finish();
56        hasher.write_u64(static_create_location_hash);
57        hasher.write_u64(parent_task_signature);
58        hasher.write_u32(create_location_counter);
59        let signature_hash = hasher.finish();
60
61        Self {
62            resource_type,
63            static_create_location,
64            parent_task_signature,
65            static_create_location_hash,
66            create_location_counter,
67            signature_hash,
68        }
69    }
70
71    /// Hash of the static location within the source code where the task was spawned
72    pub fn static_create_location_hash(&self) -> u64 {
73        self.static_create_location_hash
74    }
75
76    /// Combined signature of the static location and dynamic context
77    /// context where the task was spawned.
78    pub fn signature_hash(&self) -> u64 {
79        self.signature_hash
80    }
81}
82
83impl Debug for ResourceSignature {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        f.debug_struct("ResourceSignature")
86            .field("type", &self.resource_type)
87            .field("static_create_location", &self.static_create_location)
88            .field("parent_task_signature", &self.parent_task_signature)
89            .field("create_location_counter", &self.create_location_counter)
90            .finish()
91    }
92}
93
94impl Display for ResourceSignature {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        let suffix = match self.create_location_counter {
97            1 => "st",
98            2 => "nd",
99            3 => "rd",
100            _ => "th",
101        };
102        write!(
103            f,
104            "{:?}[ {:?}{}@{}:{:?}:{:?} on {} ]",
105            self.resource_type,
106            self.create_location_counter,
107            suffix,
108            self.static_create_location.file(),
109            self.static_create_location.line(),
110            self.static_create_location.column(),
111            self.parent_task_signature,
112        )
113    }
114}
115
116impl Hash for ResourceSignature {
117    fn hash<H: Hasher>(&self, state: &mut H) {
118        self.static_create_location_hash().hash(state);
119        self.parent_task_signature.hash(state);
120        self.create_location_counter.hash(state);
121    }
122}
123
124impl PartialEq for ResourceSignature {
125    fn eq(&self, other: &Self) -> bool {
126        self.signature_hash() == other.signature_hash()
127    }
128}
129
130impl Eq for ResourceSignature {}