1use std::fmt;
4use std::num::NonZeroU64;
5
6use std::sync::atomic::{AtomicU64, Ordering};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
21pub struct ConnectionId(NonZeroU64);
22
23impl ConnectionId {
24 #[inline]
26 #[must_use]
27 pub const fn get(self) -> u64 {
28 self.0.get()
29 }
30
31 #[must_use]
36 pub const fn new(id: u64) -> Option<ConnectionId> {
37 match NonZeroU64::new(id) {
38 Some(n) => Some(ConnectionId(n)),
39 None => None,
40 }
41 }
42}
43
44impl fmt::Display for ConnectionId {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "#{}", self.0)
47 }
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
59pub struct ListenerId(pub(crate) u16);
60
61impl ListenerId {
62 #[must_use]
64 pub const fn get(self) -> u16 {
65 self.0
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
78pub struct WorkerId(pub(crate) u16);
79
80impl WorkerId {
81 #[must_use]
83 pub const fn get(self) -> u16 {
84 self.0
85 }
86
87 #[must_use]
89 pub const fn first() -> WorkerId {
90 WorkerId(0)
91 }
92}
93
94pub(crate) const BLOCK: u64 = 1024;
96
97static NEXT: AtomicU64 = AtomicU64::new(1);
100
101#[derive(Debug)]
103pub(crate) struct IdBlock {
104 next: u64,
105 end: u64,
106}
107
108impl IdBlock {
109 pub(crate) const fn new() -> IdBlock {
111 IdBlock { next: 0, end: 0 }
112 }
113
114 pub(crate) fn issue_with(&mut self, take: impl FnOnce() -> u64) -> ConnectionId {
116 if self.next >= self.end {
117 let start = take();
118 self.next = start;
119 self.end = start.saturating_add(BLOCK);
120 }
121 let id = self.next;
122 self.next += 1;
123 to_id(id)
124 }
125
126 pub(crate) fn issue(&mut self) -> ConnectionId {
128 self.issue_with(|| NEXT.fetch_add(BLOCK, Ordering::Relaxed))
129 }
130}
131
132fn to_id(n: u64) -> ConnectionId {
133 match NonZeroU64::new(n) {
134 Some(n) => ConnectionId(n),
135 None => ConnectionId(NonZeroU64::MIN),
137 }
138}
139
140pub(crate) fn issue_single() -> ConnectionId {
142 to_id(NEXT.fetch_add(1, Ordering::Relaxed))
143}
144
145#[cfg(all(test, not(loom)))]
146mod tests {
147 use super::*;
148 use std::collections::HashSet;
149
150 #[test]
151 fn test_id_zero_never_issued() {
152 let counter = AtomicU64::new(1);
153 let mut block = IdBlock::new();
154 let first = block.issue_with(|| counter.fetch_add(BLOCK, Ordering::Relaxed));
155 assert_eq!(first.get(), 1);
156 }
157
158 #[test]
159 fn test_id_block_takes_new_block_after_1024() {
160 let counter = AtomicU64::new(1);
161 let mut block = IdBlock::new();
162 for _ in 0..BLOCK {
163 block.issue_with(|| counter.fetch_add(BLOCK, Ordering::Relaxed));
164 }
165 let next = block.issue_with(|| counter.fetch_add(BLOCK, Ordering::Relaxed));
166 assert_eq!(next.get(), 1 + BLOCK);
167 assert_eq!(counter.load(Ordering::Relaxed), 1 + 2 * BLOCK);
168 }
169
170 #[test]
171 fn test_ids_unique_across_threads() {
172 let handles: Vec<_> = (0..8)
173 .map(|_| {
174 std::thread::spawn(|| {
175 let mut block = IdBlock::new();
176 (0..5000).map(|_| block.issue().get()).collect::<Vec<_>>()
177 })
178 })
179 .collect();
180 let mut all = HashSet::new();
181 for h in handles {
182 for id in h.join().unwrap() {
183 assert!(all.insert(id), "id {id} issued twice");
184 }
185 }
186 for _ in 0..100 {
187 assert!(all.insert(issue_single().get()));
188 }
189 }
190
191 #[test]
192 fn test_connection_id_new_and_display() {
193 assert!(ConnectionId::new(0).is_none());
194 assert_eq!(ConnectionId::new(9).unwrap().to_string(), "#9");
195 }
196}
197
198#[cfg(all(test, loom))]
199mod loom_tests {
200 use super::*;
201 use loom::sync::Arc;
202
203 #[test]
204 fn loom_id_blocks_never_overlap() {
205 loom::model(|| {
206 let counter = Arc::new(loom::sync::atomic::AtomicU64::new(1));
207 let c2 = counter.clone();
208 let t = loom::thread::spawn(move || {
209 let mut b = IdBlock::new();
210 b.issue_with(|| c2.fetch_add(BLOCK, loom::sync::atomic::Ordering::Relaxed))
211 .get()
212 });
213 let mut b = IdBlock::new();
214 let a = b
215 .issue_with(|| counter.fetch_add(BLOCK, loom::sync::atomic::Ordering::Relaxed))
216 .get();
217 let other = t.join().unwrap();
218 assert_ne!(a, other);
219 assert!(a.abs_diff(other) >= BLOCK);
220 });
221 }
222}