1use std::sync::atomic::{AtomicU64, Ordering};
50use std::sync::{Arc, Mutex, RwLock};
51
52use bytes::Bytes;
53
54use crate::NodeId;
55use crate::OrbitTyped;
56use crate::id::NetId64;
57
58pub mod cursor;
59#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
60mod readiness;
61#[cfg(unix)]
62pub mod shm;
63
64#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
65pub use readiness::RingEventFd;
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69#[repr(u8)]
70pub enum RingTopology {
71 Shared = 0,
73 PerNode = 1,
77 SharedOrdered = 2,
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub struct RingSpec {
89 pub capacity: usize,
91 pub payload_capacity: usize,
93 pub topology: RingTopology,
95}
96
97impl RingSpec {
98 pub const fn new(capacity: usize, payload_capacity: usize) -> Self {
99 Self {
100 capacity,
101 payload_capacity,
102 topology: RingTopology::Shared,
103 }
104 }
105
106 pub const fn per_node(capacity: usize, payload_capacity: usize) -> Self {
108 Self {
109 capacity,
110 payload_capacity,
111 topology: RingTopology::PerNode,
112 }
113 }
114
115 pub const fn shared_ordered(capacity: usize, payload_capacity: usize) -> Self {
117 Self {
118 capacity,
119 payload_capacity,
120 topology: RingTopology::SharedOrdered,
121 }
122 }
123
124 pub(crate) fn assert_valid(self) {
125 assert!(self.capacity > 0, "ring capacity must be > 0");
126 assert!(
127 self.capacity.is_power_of_two(),
128 "ring capacity must be a power of two"
129 );
130 assert!(
131 self.payload_capacity <= u32::MAX as usize,
132 "ring payload capacity must fit in u32"
133 );
134 }
135}
136
137struct RingLane {
138 write_pos: AtomicU64,
139 write_lock: Mutex<()>,
140 slots: Vec<RwLock<Option<Frame>>>,
141}
142
143impl RingLane {
144 fn new(capacity: usize) -> Self {
145 let mut slots = Vec::with_capacity(capacity);
146 for _ in 0..capacity {
147 slots.push(RwLock::new(None));
148 }
149 Self {
150 write_pos: AtomicU64::new(0),
151 write_lock: Mutex::new(()),
152 slots,
153 }
154 }
155}
156
157#[derive(Clone, Debug, PartialEq, Eq)]
159pub struct Frame {
160 pub id: NetId64,
161 pub kind: u8,
162 pub ver: u64,
163 pub payload: Bytes,
164}
165
166pub struct Ring {
170 kind: u8,
173 capacity: usize,
175 payload_capacity: usize,
177 topology: RingTopology,
178 version_counter: AtomicU64,
180 lanes: Vec<RingLane>,
181}
182
183impl Ring {
184 pub fn new<T: OrbitTyped>() -> Self {
187 Self::new_for_fleet::<T>(1)
188 }
189
190 pub fn new_for_fleet<T: OrbitTyped>(fleet_capacity: u16) -> Self {
193 assert!(fleet_capacity > 0, "ring fleet capacity must be > 0");
194 let spec = T::RING_SPEC;
195 spec.assert_valid();
196 let capacity = spec.capacity;
197 let lane_count = match spec.topology {
198 RingTopology::Shared | RingTopology::SharedOrdered => 1,
199 RingTopology::PerNode => usize::from(fleet_capacity),
200 };
201 let mut lanes = Vec::with_capacity(lane_count);
202 for _ in 0..lane_count {
203 lanes.push(RingLane::new(capacity));
204 }
205 Self {
206 kind: T::KIND,
207 capacity,
208 payload_capacity: spec.payload_capacity,
209 topology: spec.topology,
210 version_counter: AtomicU64::new(0),
211 lanes,
212 }
213 }
214
215 pub fn kind(&self) -> u8 {
217 self.kind
218 }
219
220 pub fn capacity(&self) -> usize {
222 self.capacity
223 }
224
225 pub fn payload_capacity(&self) -> usize {
227 self.payload_capacity
228 }
229
230 pub fn spec(&self) -> RingSpec {
231 RingSpec {
232 capacity: self.capacity,
233 payload_capacity: self.payload_capacity,
234 topology: self.topology,
235 }
236 }
237
238 pub fn head(&self) -> u64 {
240 self.lanes[0].write_pos.load(Ordering::Acquire)
241 }
242
243 pub fn lane_count(&self) -> usize {
245 self.lanes.len()
246 }
247
248 pub fn lane_head(&self, node_id: NodeId) -> u64 {
250 self.lane(node_id).write_pos.load(Ordering::Acquire)
251 }
252
253 pub fn next_version(&self) -> u64 {
259 self.version_counter
260 .fetch_add(1, Ordering::AcqRel)
261 .checked_add(1)
262 .expect("ring semantic version exhausted")
263 }
264
265 pub fn current_version(&self) -> u64 {
267 self.version_counter.load(Ordering::Acquire)
268 }
269
270 pub fn write(&self, node_id: NodeId, frame_kind: u8, ver: u64, payload: Bytes) -> NetId64 {
278 assert!(
279 payload.len() <= self.payload_capacity,
280 "payload {} > ring payload capacity {}",
281 payload.len(),
282 self.payload_capacity
283 );
284 let lane = self.lane(node_id);
285 match self.topology {
286 RingTopology::Shared => {
287 let counter = lane.write_pos.fetch_add(1, Ordering::AcqRel);
288 self.write_frame(lane, node_id, counter, frame_kind, ver, payload)
289 }
290 RingTopology::PerNode | RingTopology::SharedOrdered => {
291 let _write = lane
292 .write_lock
293 .lock()
294 .unwrap_or_else(|error| error.into_inner());
295 let counter = lane.write_pos.load(Ordering::Relaxed);
296 let id = self.write_frame(lane, node_id, counter, frame_kind, ver, payload);
297 lane.write_pos
298 .store(counter.wrapping_add(1), Ordering::Release);
299 id
300 }
301 }
302 }
303
304 pub fn write_batch(
311 &self,
312 node_id: NodeId,
313 frame_kind: u8,
314 ver: u64,
315 payloads: Vec<Bytes>,
316 ) -> Vec<NetId64> {
317 assert!(
318 payloads.len() <= self.capacity,
319 "batch {} > ring capacity {}",
320 payloads.len(),
321 self.capacity
322 );
323 for payload in &payloads {
324 assert!(
325 payload.len() <= self.payload_capacity,
326 "payload {} > ring payload capacity {}",
327 payload.len(),
328 self.payload_capacity
329 );
330 }
331 if payloads.is_empty() {
332 return Vec::new();
333 }
334
335 let lane = self.lane(node_id);
336 match self.topology {
337 RingTopology::Shared => {
338 let start = lane
339 .write_pos
340 .fetch_add(payloads.len() as u64, Ordering::AcqRel);
341 payloads
342 .into_iter()
343 .enumerate()
344 .map(|(offset, payload)| {
345 self.write_frame(
346 lane,
347 node_id,
348 start.wrapping_add(offset as u64),
349 frame_kind,
350 ver,
351 payload,
352 )
353 })
354 .collect()
355 }
356 RingTopology::PerNode | RingTopology::SharedOrdered => {
357 let _write = lane
358 .write_lock
359 .lock()
360 .unwrap_or_else(|error| error.into_inner());
361 let start = lane.write_pos.load(Ordering::Relaxed);
362 let ids = payloads
363 .into_iter()
364 .enumerate()
365 .map(|(offset, payload)| {
366 self.write_frame(
367 lane,
368 node_id,
369 start.wrapping_add(offset as u64),
370 frame_kind,
371 ver,
372 payload,
373 )
374 })
375 .collect::<Vec<_>>();
376 lane.write_pos
377 .store(start.wrapping_add(ids.len() as u64), Ordering::Release);
378 ids
379 }
380 }
381 }
382
383 pub fn read(&self, id: NetId64) -> Option<Frame> {
391 if id.kind() != self.kind {
392 return None;
393 }
394 let lane = self.lane_for_frame(id)?;
395 let slot_idx = (id.counter() as usize) % self.capacity;
396 let guard = lane.slots[slot_idx].read().expect("ring slot poisoned");
397 match &*guard {
398 Some(f) if f.id == id => Some(f.clone()),
399 _ => None,
400 }
401 }
402
403 pub fn read_head(&self) -> Option<Frame> {
407 let head = self.head();
408 if head == 0 {
409 return None;
410 }
411 let slot_idx = ((head - 1) as usize) % self.capacity;
412 self.lanes[0].slots[slot_idx]
413 .read()
414 .expect("ring slot poisoned")
415 .clone()
416 }
417
418 pub fn read_at(&self, counter: u64) -> Option<Frame> {
425 let slot_idx = (counter as usize) % self.capacity;
426 self.lanes[0].slots[slot_idx]
427 .read()
428 .expect("ring slot poisoned")
429 .clone()
430 }
431
432 pub(crate) fn read_state_at(&self, counter: u64) -> cursor::RingRead {
433 match self.read_at(counter) {
434 Some(frame) if frame.id.counter() == counter => cursor::RingRead::Ready(frame),
435 Some(frame) if frame.id.counter() > counter => cursor::RingRead::Unavailable,
436 Some(_) | None => cursor::RingRead::Pending,
437 }
438 }
439
440 pub(crate) fn read_lane_at(&self, node_id: NodeId, counter: u64) -> Option<Frame> {
441 let lane = self.lane(node_id);
442 let slot_idx = (counter as usize) % self.capacity;
443 lane.slots[slot_idx]
444 .read()
445 .expect("ring slot poisoned")
446 .clone()
447 }
448
449 pub(crate) fn read_lane_state_at(&self, node_id: NodeId, counter: u64) -> cursor::RingRead {
450 match self.read_lane_at(node_id, counter) {
451 Some(frame) if frame.id.counter() == counter => cursor::RingRead::Ready(frame),
452 Some(frame) if frame.id.counter() > counter => cursor::RingRead::Unavailable,
453 Some(_) | None if self.topology != RingTopology::Shared => {
454 cursor::RingRead::Unavailable
455 }
456 Some(_) | None => cursor::RingRead::Pending,
457 }
458 }
459
460 pub fn reset(&self) {
465 for lane in &self.lanes {
466 for slot in &lane.slots {
467 *slot.write().expect("ring slot poisoned") = None;
468 }
469 lane.write_pos.store(0, Ordering::Release);
470 }
471 self.version_counter.store(0, Ordering::Release);
472 }
473
474 fn lane(&self, node_id: NodeId) -> &RingLane {
475 let index = match self.topology {
476 RingTopology::Shared | RingTopology::SharedOrdered => 0,
477 RingTopology::PerNode => usize::from(node_id.get()),
478 };
479 self.lanes.get(index).unwrap_or_else(|| {
480 panic!(
481 "node {} is outside ring lane count {}",
482 node_id.get(),
483 self.lanes.len()
484 )
485 })
486 }
487
488 fn lane_for_frame(&self, id: NetId64) -> Option<&RingLane> {
489 let index = match self.topology {
490 RingTopology::Shared | RingTopology::SharedOrdered => 0,
491 RingTopology::PerNode => usize::from(id.node()),
492 };
493 self.lanes.get(index)
494 }
495
496 fn write_frame(
497 &self,
498 lane: &RingLane,
499 node_id: NodeId,
500 counter: u64,
501 frame_kind: u8,
502 ver: u64,
503 payload: Bytes,
504 ) -> NetId64 {
505 let id = NetId64::make(self.kind, node_id.get(), counter);
506 let slot_idx = (counter as usize) % self.capacity;
507 let frame = Frame {
508 id,
509 kind: frame_kind,
510 ver,
511 payload,
512 };
513 let mut guard = lane.slots[slot_idx].write().expect("ring slot poisoned");
514 *guard = Some(frame);
515 id
516 }
517}
518
519impl cursor::RingFrameSource for Ring {
520 fn kind(&self) -> u8 {
521 Ring::kind(self)
522 }
523
524 fn head(&self) -> u64 {
525 Ring::head(self)
526 }
527
528 fn capacity(&self) -> usize {
529 Ring::capacity(self)
530 }
531
532 fn read_at(&self, counter: u64) -> Option<Frame> {
533 Ring::read_at(self, counter)
534 }
535
536 fn read_state_at(&self, counter: u64) -> cursor::RingRead {
537 Ring::read_state_at(self, counter)
538 }
539}
540
541#[cfg(unix)]
542impl cursor::RingFrameSource for shm::ShmRing {
543 fn kind(&self) -> u8 {
544 shm::ShmRing::kind(self)
545 }
546
547 fn head(&self) -> u64 {
548 shm::ShmRing::head(self)
549 }
550
551 fn capacity(&self) -> usize {
552 shm::ShmRing::capacity(self)
553 }
554
555 fn read_at(&self, counter: u64) -> Option<Frame> {
556 shm::ShmRing::read_at(self, counter)
557 }
558
559 fn read_state_at(&self, counter: u64) -> cursor::RingRead {
560 shm::ShmRing::read_state_at(self, counter)
561 }
562}
563
564impl std::fmt::Debug for Ring {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 f.debug_struct("Ring")
567 .field("kind", &self.kind)
568 .field("capacity", &self.capacity)
569 .field("payload_capacity", &self.payload_capacity)
570 .field("topology", &self.topology)
571 .field("lane_count", &self.lanes.len())
572 .field("head", &self.head())
573 .finish()
574 }
575}
576
577pub(crate) struct RingRegistry {
580 fleet_capacity: u16,
581 rings: dashmap::DashMap<u8, Arc<Ring>>,
582}
583
584impl RingRegistry {
585 pub fn new(fleet_capacity: u16) -> Self {
586 Self {
587 fleet_capacity,
588 rings: dashmap::DashMap::new(),
589 }
590 }
591
592 pub fn get_or_create<T: OrbitTyped>(&self) -> Arc<Ring> {
594 let ring = self
595 .rings
596 .entry(T::KIND)
597 .or_insert_with(|| Arc::new(Ring::new_for_fleet::<T>(self.fleet_capacity)))
598 .clone();
599 assert_eq!(
600 ring.spec(),
601 T::RING_SPEC,
602 "OrbitTyped KIND {} was reused with a different ring spec",
603 T::KIND
604 );
605 ring
606 }
607
608 pub fn lookup(&self, kind: u8) -> Option<Arc<Ring>> {
610 self.rings.get(&kind).map(|e| e.clone())
611 }
612}