1use std::sync::atomic::{AtomicU64, Ordering};
50use std::sync::{Arc, Mutex, RwLock};
51
52use bytes::Bytes;
53
54use crate::id::NetId64;
55use crate::{NodeId, OrbitTyped};
56
57pub mod cursor;
58#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
59mod readiness;
60#[cfg(unix)]
61pub mod shm;
62
63#[cfg(any(
64 target_os = "linux",
65 target_os = "freebsd",
66 target_os = "macos"
67))]
68pub use readiness::{ParkedRingEventFd, RingEventFd};
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72#[repr(u8)]
73pub enum RingTopology {
74 Shared = 0,
76 PerNode = 1,
80 SharedOrdered = 2,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub struct RingSpec {
92 pub capacity: usize,
94 pub payload_capacity: usize,
96 pub topology: RingTopology,
98}
99
100impl RingSpec {
101 pub const fn new(
102 capacity: usize,
103 payload_capacity: usize,
104 ) -> Self {
105 Self { capacity, payload_capacity, topology: RingTopology::Shared }
106 }
107
108 pub const fn per_node(
110 capacity: usize,
111 payload_capacity: usize,
112 ) -> Self {
113 Self { capacity, payload_capacity, topology: RingTopology::PerNode }
114 }
115
116 pub const fn shared_ordered(
118 capacity: usize,
119 payload_capacity: usize,
120 ) -> Self {
121 Self {
122 capacity,
123 payload_capacity,
124 topology: RingTopology::SharedOrdered,
125 }
126 }
127
128 pub(crate) fn assert_valid(self) {
129 assert!(self.capacity > 0, "ring capacity must be > 0");
130 assert!(
131 self.capacity.is_power_of_two(),
132 "ring capacity must be a power of two"
133 );
134 assert!(
135 self.payload_capacity <= u32::MAX as usize,
136 "ring payload capacity must fit in u32"
137 );
138 }
139}
140
141struct RingLane {
142 write_pos: AtomicU64,
143 write_lock: Mutex<()>,
144 slots: Vec<RwLock<Option<Frame>>>,
145}
146
147impl RingLane {
148 fn new(capacity: usize) -> Self {
149 let mut slots = Vec::with_capacity(capacity);
150 for _ in 0..capacity {
151 slots.push(RwLock::new(None));
152 }
153 Self { write_pos: AtomicU64::new(0), write_lock: Mutex::new(()), slots }
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(
250 &self,
251 node_id: NodeId,
252 ) -> u64 {
253 self.lane(node_id).write_pos.load(Ordering::Acquire)
254 }
255
256 pub fn next_version(&self) -> u64 {
262 self.version_counter
263 .fetch_add(1, Ordering::AcqRel)
264 .checked_add(1)
265 .expect("ring semantic version exhausted")
266 }
267
268 pub fn current_version(&self) -> u64 {
270 self.version_counter.load(Ordering::Acquire)
271 }
272
273 pub fn write(
281 &self,
282 node_id: NodeId,
283 frame_kind: u8,
284 ver: u64,
285 payload: Bytes,
286 ) -> NetId64 {
287 assert!(
288 payload.len() <= self.payload_capacity,
289 "payload {} > ring payload capacity {}",
290 payload.len(),
291 self.payload_capacity
292 );
293 let lane = self.lane(node_id);
294 match self.topology {
295 RingTopology::Shared => {
296 let counter = lane.write_pos.fetch_add(1, Ordering::AcqRel);
297 self.write_frame(
298 lane, node_id, counter, frame_kind, ver, payload,
299 )
300 }
301 RingTopology::PerNode | RingTopology::SharedOrdered => {
302 let _write = lane
303 .write_lock
304 .lock()
305 .unwrap_or_else(|error| error.into_inner());
306 let counter = lane.write_pos.load(Ordering::Relaxed);
307 let id = self.write_frame(
308 lane, node_id, counter, frame_kind, ver, payload,
309 );
310 lane.write_pos
311 .store(counter.wrapping_add(1), Ordering::Release);
312 id
313 }
314 }
315 }
316
317 pub fn write_batch(
324 &self,
325 node_id: NodeId,
326 frame_kind: u8,
327 ver: u64,
328 payloads: Vec<Bytes>,
329 ) -> Vec<NetId64> {
330 assert!(
331 payloads.len() <= self.capacity,
332 "batch {} > ring capacity {}",
333 payloads.len(),
334 self.capacity
335 );
336 for payload in &payloads {
337 assert!(
338 payload.len() <= self.payload_capacity,
339 "payload {} > ring payload capacity {}",
340 payload.len(),
341 self.payload_capacity
342 );
343 }
344 if payloads.is_empty() {
345 return Vec::new();
346 }
347
348 let lane = self.lane(node_id);
349 match self.topology {
350 RingTopology::Shared => {
351 let start = lane
352 .write_pos
353 .fetch_add(payloads.len() as u64, Ordering::AcqRel);
354 payloads
355 .into_iter()
356 .enumerate()
357 .map(|(offset, payload)| {
358 self.write_frame(
359 lane,
360 node_id,
361 start.wrapping_add(offset as u64),
362 frame_kind,
363 ver,
364 payload,
365 )
366 })
367 .collect()
368 }
369 RingTopology::PerNode | RingTopology::SharedOrdered => {
370 let _write = lane
371 .write_lock
372 .lock()
373 .unwrap_or_else(|error| error.into_inner());
374 let start = lane.write_pos.load(Ordering::Relaxed);
375 let ids = payloads
376 .into_iter()
377 .enumerate()
378 .map(|(offset, payload)| {
379 self.write_frame(
380 lane,
381 node_id,
382 start.wrapping_add(offset as u64),
383 frame_kind,
384 ver,
385 payload,
386 )
387 })
388 .collect::<Vec<_>>();
389 lane.write_pos.store(
390 start.wrapping_add(ids.len() as u64),
391 Ordering::Release,
392 );
393 ids
394 }
395 }
396 }
397
398 pub fn read(
406 &self,
407 id: NetId64,
408 ) -> Option<Frame> {
409 if id.kind() != self.kind {
410 return None;
411 }
412 let lane = self.lane_for_frame(id)?;
413 let slot_idx = (id.counter() as usize) % self.capacity;
414 let guard = lane.slots[slot_idx].read().expect("ring slot poisoned");
415 match &*guard {
416 Some(f) if f.id == id => Some(f.clone()),
417 _ => None,
418 }
419 }
420
421 pub fn read_head(&self) -> Option<Frame> {
425 let head = self.head();
426 if head == 0 {
427 return None;
428 }
429 let slot_idx = ((head - 1) as usize) % self.capacity;
430 self.lanes[0].slots[slot_idx]
431 .read()
432 .expect("ring slot poisoned")
433 .clone()
434 }
435
436 pub fn read_at(
443 &self,
444 counter: u64,
445 ) -> Option<Frame> {
446 let slot_idx = (counter as usize) % self.capacity;
447 self.lanes[0].slots[slot_idx]
448 .read()
449 .expect("ring slot poisoned")
450 .clone()
451 }
452
453 pub(crate) fn read_state_at(
454 &self,
455 counter: u64,
456 ) -> cursor::RingRead {
457 match self.read_at(counter) {
458 Some(frame) if frame.id.counter() == counter => {
459 cursor::RingRead::Ready(frame)
460 }
461 Some(frame) if frame.id.counter() > counter => {
462 cursor::RingRead::Unavailable
463 }
464 Some(_) | None => cursor::RingRead::Pending,
465 }
466 }
467
468 pub(crate) fn read_lane_at(
469 &self,
470 node_id: NodeId,
471 counter: u64,
472 ) -> Option<Frame> {
473 let lane = self.lane(node_id);
474 let slot_idx = (counter as usize) % self.capacity;
475 lane.slots[slot_idx].read().expect("ring slot poisoned").clone()
476 }
477
478 pub(crate) fn read_lane_state_at(
479 &self,
480 node_id: NodeId,
481 counter: u64,
482 ) -> cursor::RingRead {
483 match self.read_lane_at(node_id, counter) {
484 Some(frame) if frame.id.counter() == counter => {
485 cursor::RingRead::Ready(frame)
486 }
487 Some(frame) if frame.id.counter() > counter => {
488 cursor::RingRead::Unavailable
489 }
490 Some(_) | None if self.topology != RingTopology::Shared => {
491 cursor::RingRead::Unavailable
492 }
493 Some(_) | None => cursor::RingRead::Pending,
494 }
495 }
496
497 pub fn reset(&self) {
502 for lane in &self.lanes {
503 for slot in &lane.slots {
504 *slot.write().expect("ring slot poisoned") = None;
505 }
506 lane.write_pos.store(0, Ordering::Release);
507 }
508 self.version_counter.store(0, Ordering::Release);
509 }
510
511 fn lane(
512 &self,
513 node_id: NodeId,
514 ) -> &RingLane {
515 let index = match self.topology {
516 RingTopology::Shared | RingTopology::SharedOrdered => 0,
517 RingTopology::PerNode => usize::from(node_id.get()),
518 };
519 self.lanes.get(index).unwrap_or_else(|| {
520 panic!(
521 "node {} is outside ring lane count {}",
522 node_id.get(),
523 self.lanes.len()
524 )
525 })
526 }
527
528 fn lane_for_frame(
529 &self,
530 id: NetId64,
531 ) -> Option<&RingLane> {
532 let index = match self.topology {
533 RingTopology::Shared | RingTopology::SharedOrdered => 0,
534 RingTopology::PerNode => usize::from(id.node()),
535 };
536 self.lanes.get(index)
537 }
538
539 fn write_frame(
540 &self,
541 lane: &RingLane,
542 node_id: NodeId,
543 counter: u64,
544 frame_kind: u8,
545 ver: u64,
546 payload: Bytes,
547 ) -> NetId64 {
548 let id = NetId64::make(self.kind, node_id.get(), counter);
549 let slot_idx = (counter as usize) % self.capacity;
550 let frame = Frame { id, kind: frame_kind, ver, payload };
551 let mut guard =
552 lane.slots[slot_idx].write().expect("ring slot poisoned");
553 *guard = Some(frame);
554 id
555 }
556}
557
558impl cursor::RingFrameSource for Ring {
559 fn kind(&self) -> u8 {
560 Ring::kind(self)
561 }
562
563 fn head(&self) -> u64 {
564 Ring::head(self)
565 }
566
567 fn capacity(&self) -> usize {
568 Ring::capacity(self)
569 }
570
571 fn read_at(
572 &self,
573 counter: u64,
574 ) -> Option<Frame> {
575 Ring::read_at(self, counter)
576 }
577
578 fn read_state_at(
579 &self,
580 counter: u64,
581 ) -> cursor::RingRead {
582 Ring::read_state_at(self, counter)
583 }
584}
585
586#[cfg(unix)]
587impl cursor::RingFrameSource for shm::ShmRing {
588 fn kind(&self) -> u8 {
589 shm::ShmRing::kind(self)
590 }
591
592 fn head(&self) -> u64 {
593 shm::ShmRing::head(self)
594 }
595
596 fn capacity(&self) -> usize {
597 shm::ShmRing::capacity(self)
598 }
599
600 fn read_at(
601 &self,
602 counter: u64,
603 ) -> Option<Frame> {
604 shm::ShmRing::read_at(self, counter)
605 }
606
607 fn read_state_at(
608 &self,
609 counter: u64,
610 ) -> cursor::RingRead {
611 shm::ShmRing::read_state_at(self, counter)
612 }
613}
614
615impl std::fmt::Debug for Ring {
616 fn fmt(
617 &self,
618 f: &mut std::fmt::Formatter<'_>,
619 ) -> std::fmt::Result {
620 f.debug_struct("Ring")
621 .field("kind", &self.kind)
622 .field("capacity", &self.capacity)
623 .field("payload_capacity", &self.payload_capacity)
624 .field("topology", &self.topology)
625 .field("lane_count", &self.lanes.len())
626 .field("head", &self.head())
627 .finish()
628 }
629}
630
631pub(crate) struct RingRegistry {
634 fleet_capacity: u16,
635 rings: dashmap::DashMap<u8, Arc<Ring>>,
636}
637
638impl RingRegistry {
639 pub fn new(fleet_capacity: u16) -> Self {
640 Self { fleet_capacity, rings: dashmap::DashMap::new() }
641 }
642
643 pub fn get_or_create<T: OrbitTyped>(&self) -> Arc<Ring> {
645 let ring = self
646 .rings
647 .entry(T::KIND)
648 .or_insert_with(|| {
649 Arc::new(Ring::new_for_fleet::<T>(self.fleet_capacity))
650 })
651 .clone();
652 assert_eq!(
653 ring.spec(),
654 T::RING_SPEC,
655 "OrbitTyped KIND {} was reused with a different ring spec",
656 T::KIND
657 );
658 ring
659 }
660
661 pub fn lookup(
663 &self,
664 kind: u8,
665 ) -> Option<Arc<Ring>> {
666 self.rings.get(&kind).map(|e| e.clone())
667 }
668}