1use core::cell::{Cell, UnsafeCell};
4use core::fmt;
5use core::marker::PhantomData;
6use core::sync::atomic::{AtomicU32, AtomicU64, Ordering, fence};
7
8use crate::layout::{AcknowledgementRoute, RoleId};
9
10#[cfg(not(target_has_atomic = "64"))]
11compile_error!("native-ipc-core requires lock-free 64-bit atomic support");
12
13pub const SLOT_HEADER_SIZE: u64 = 64;
15pub const ACKNOWLEDGEMENT_CELL_SIZE: u64 = 64;
17
18#[repr(C, align(64))]
23#[derive(Debug)]
24pub struct SlotMetadata {
25 generation: AtomicU64,
26 payload_len: AtomicU32,
27 reserved_word: UnsafeCell<u32>,
28 published_sequence: AtomicU64,
29 reserved: UnsafeCell<[u8; 40]>,
30}
31
32impl SlotMetadata {
33 pub const fn new(generation: u64) -> Self {
35 Self {
36 generation: AtomicU64::new(generation),
37 payload_len: AtomicU32::new(0),
38 reserved_word: UnsafeCell::new(0),
39 published_sequence: AtomicU64::new(0),
40 reserved: UnsafeCell::new([0; 40]),
41 }
42 }
43
44 pub unsafe fn initialize(&mut self, generation: u64) -> Result<(), SlotError> {
51 if generation == 0 {
52 return Err(SlotError::ZeroGeneration);
53 }
54 *self.generation.get_mut() = generation;
55 *self.payload_len.get_mut() = 0;
56 unsafe { *self.reserved_word.get() = 0 };
58 *self.published_sequence.get_mut() = 0;
59 unsafe { *self.reserved.get() = [0; 40] };
61 Ok(())
62 }
63}
64
65unsafe impl Sync for SlotMetadata {}
70
71#[repr(C, align(64))]
73pub struct AcknowledgementCell {
74 sequence: AtomicU64,
75 reserved: UnsafeCell<[u8; 56]>,
76}
77
78impl AcknowledgementCell {
79 pub const fn new() -> Self {
81 Self {
82 sequence: AtomicU64::new(0),
83 reserved: UnsafeCell::new([0; 56]),
84 }
85 }
86
87 pub unsafe fn initialize(&mut self) {
93 *self.sequence.get_mut() = 0;
94 unsafe { *self.reserved.get() = [0; 56] };
96 }
97}
98
99unsafe impl Sync for AcknowledgementCell {}
102
103impl Default for AcknowledgementCell {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub struct WriterSlotBinding(SlotBinding);
112
113impl WriterSlotBinding {
114 pub(crate) const fn validated(
115 role: RoleId,
116 generation: u64,
117 payload_capacity: u32,
118 slot_index: u32,
119 slot_count: u32,
120 acknowledgement_owner: RoleId,
121 acknowledgement_cell_index: u32,
122 ) -> Self {
123 Self(SlotBinding {
124 role,
125 generation,
126 payload_capacity,
127 slot_index,
128 slot_count,
129 acknowledgement_owner: Some(acknowledgement_owner),
130 acknowledgement_cell_index: Some(acknowledgement_cell_index),
131 })
132 }
133}
134
135#[derive(Clone, Copy, Debug, Eq, PartialEq)]
137pub struct ReaderSlotBinding(SlotBinding);
138
139impl ReaderSlotBinding {
140 pub(crate) const fn validated(
141 role: RoleId,
142 generation: u64,
143 payload_capacity: u32,
144 slot_index: u32,
145 slot_count: u32,
146 ) -> Self {
147 Self(SlotBinding {
148 role,
149 generation,
150 payload_capacity,
151 slot_index,
152 slot_count,
153 acknowledgement_owner: None,
154 acknowledgement_cell_index: None,
155 })
156 }
157}
158
159#[derive(Clone, Copy, Debug, Eq, PartialEq)]
160struct SlotBinding {
161 role: RoleId,
162 generation: u64,
163 payload_capacity: u32,
164 slot_index: u32,
165 slot_count: u32,
166 acknowledgement_owner: Option<RoleId>,
167 acknowledgement_cell_index: Option<u32>,
168}
169
170pub struct WriterSlot<'a> {
172 header: &'a SlotMetadata,
173 binding: SlotBinding,
174 _not_sync: PhantomData<Cell<()>>,
175}
176
177impl<'a> WriterSlot<'a> {
178 pub unsafe fn bind(
187 header: &'a SlotMetadata,
188 binding: WriterSlotBinding,
189 ) -> Result<Self, SlotError> {
190 validate_bound_generation(header, binding.0.generation)?;
191 Ok(Self {
192 header,
193 binding: binding.0,
194 _not_sync: PhantomData,
195 })
196 }
197
198 pub fn prepare_publish(
200 &mut self,
201 sequence: u64,
202 acknowledgement: Option<AcknowledgementObservation>,
203 ) -> Result<PublishReservation<'_>, SlotError> {
204 validate_bound_generation(self.header, self.binding.generation)?;
205 validate_sequence_slot(self.binding, sequence)?;
206 let current = self.header.published_sequence.load(Ordering::Relaxed);
207 if current == 0 {
208 let expected = u64::from(self.binding.slot_index) + 1;
209 if sequence != expected {
210 return Err(SlotError::UnexpectedFirstSequence {
211 expected,
212 actual: sequence,
213 });
214 }
215 } else {
216 let expected = current
217 .checked_add(u64::from(self.binding.slot_count))
218 .ok_or(SlotError::SequenceWrap)?;
219 if sequence != expected {
220 return Err(SlotError::UnexpectedNextSequence {
221 expected,
222 actual: sequence,
223 });
224 }
225 let acknowledgement =
226 acknowledgement.ok_or(SlotError::MissingAcknowledgement { sequence: current })?;
227 if acknowledgement.target != self.binding.role {
228 return Err(SlotError::WrongAcknowledgementTarget);
229 }
230 if acknowledgement.owner != self.binding.acknowledgement_owner.unwrap() {
231 return Err(SlotError::WrongAcknowledgementOwner);
232 }
233 if acknowledgement.slot_index != self.binding.slot_index {
234 return Err(SlotError::WrongAcknowledgementSlot);
235 }
236 if acknowledgement.cell_index != self.binding.acknowledgement_cell_index.unwrap() {
237 return Err(SlotError::WrongAcknowledgementCell);
238 }
239 if acknowledgement.generation != self.binding.generation {
240 return Err(SlotError::StaleAcknowledgementGeneration);
241 }
242 if acknowledgement.sequence < current {
243 return Err(SlotError::LaggingAcknowledgement {
244 expected: current,
245 actual: acknowledgement.sequence,
246 });
247 }
248 if acknowledgement.sequence > current {
249 return Err(SlotError::FutureAcknowledgement {
250 expected: current,
251 actual: acknowledgement.sequence,
252 });
253 }
254 }
255 Ok(PublishReservation {
256 header: self.header,
257 sequence,
258 capacity: self.binding.payload_capacity,
259 _exclusive: PhantomData,
260 })
261 }
262}
263
264pub struct ReaderSlot<'a> {
266 header: &'a SlotMetadata,
267 binding: SlotBinding,
268}
269
270impl<'a> ReaderSlot<'a> {
271 pub unsafe fn bind(
279 header: &'a SlotMetadata,
280 binding: ReaderSlotBinding,
281 ) -> Result<Self, SlotError> {
282 validate_bound_generation(header, binding.0.generation)?;
283 Ok(Self {
284 header,
285 binding: binding.0,
286 })
287 }
288
289 pub fn observe(&self, expected_sequence: u64) -> Result<SlotObservation, SlotError> {
291 validate_sequence_slot(self.binding, expected_sequence)?;
292 let sequence = self.header.published_sequence.load(Ordering::Acquire);
293 if sequence != expected_sequence {
294 return Err(SlotError::StaleSequence {
295 expected: expected_sequence,
296 actual: sequence,
297 });
298 }
299 validate_bound_generation(self.header, self.binding.generation)?;
300 let payload_len = self.header.payload_len.load(Ordering::Relaxed);
301 if payload_len > self.binding.payload_capacity {
302 return Err(SlotError::PayloadTooLarge {
303 length: payload_len,
304 capacity: self.binding.payload_capacity,
305 });
306 }
307 Ok(SlotObservation {
308 role: self.binding.role,
309 slot_index: self.binding.slot_index,
310 generation: self.binding.generation,
311 sequence,
312 payload_len,
313 })
314 }
315
316 pub fn recheck(&self, observation: SlotObservation) -> Result<(), SlotError> {
322 if observation.role != self.binding.role {
323 return Err(SlotError::WrongObservationRole);
324 }
325 if observation.generation != self.binding.generation {
326 return Err(SlotError::StaleGeneration {
327 expected: self.binding.generation,
328 actual: observation.generation,
329 });
330 }
331 fence(Ordering::SeqCst);
334 let sequence = self.header.published_sequence.load(Ordering::Acquire);
335 if sequence != observation.sequence {
336 return Err(SlotError::StaleSequence {
337 expected: observation.sequence,
338 actual: sequence,
339 });
340 }
341 validate_bound_generation(self.header, observation.generation)?;
342 let payload_len = self.header.payload_len.load(Ordering::Relaxed);
343 if payload_len != observation.payload_len {
344 return Err(SlotError::ChangedPayloadLength {
345 expected: observation.payload_len,
346 actual: payload_len,
347 });
348 }
349 Ok(())
350 }
351}
352
353#[must_use = "payload bytes remain unpublished until publish is called"]
355#[derive(Debug)]
356pub struct PublishReservation<'a> {
357 header: &'a SlotMetadata,
358 sequence: u64,
359 capacity: u32,
360 _exclusive: PhantomData<&'a mut ()>,
361}
362
363impl PublishReservation<'_> {
364 pub fn publish(self, payload_len: u32) -> Result<(), SlotError> {
366 if payload_len > self.capacity {
367 return Err(SlotError::PayloadTooLarge {
368 length: payload_len,
369 capacity: self.capacity,
370 });
371 }
372 self.header
373 .payload_len
374 .store(payload_len, Ordering::Relaxed);
375 self.header
376 .published_sequence
377 .store(self.sequence, Ordering::Release);
378 Ok(())
379 }
380}
381
382#[derive(Clone, Copy, Debug, Eq, PartialEq)]
384pub struct SlotObservation {
385 role: RoleId,
386 slot_index: u32,
387 generation: u64,
388 sequence: u64,
389 payload_len: u32,
390}
391
392impl SlotObservation {
393 pub const fn role(self) -> RoleId {
395 self.role
396 }
397 pub const fn slot_index(self) -> u32 {
399 self.slot_index
400 }
401 pub const fn generation(self) -> u64 {
403 self.generation
404 }
405 pub const fn sequence(self) -> u64 {
407 self.sequence
408 }
409 pub const fn payload_len(self) -> u32 {
411 self.payload_len
412 }
413}
414
415#[derive(Clone, Copy, Debug, Eq, PartialEq)]
417pub struct AcknowledgementWriterBinding(AcknowledgementBinding);
418
419impl AcknowledgementWriterBinding {
420 pub(crate) const fn validated(route: AcknowledgementRoute, generation: u64) -> Self {
421 Self(AcknowledgementBinding {
422 owner: route.owner(),
423 target: route.target(),
424 slot_index: route.slot_index(),
425 cell_index: route.cell_index(),
426 generation,
427 })
428 }
429}
430
431#[derive(Clone, Copy, Debug, Eq, PartialEq)]
433pub struct AcknowledgementReaderBinding(AcknowledgementBinding);
434
435impl AcknowledgementReaderBinding {
436 pub(crate) const fn validated(route: AcknowledgementRoute, generation: u64) -> Self {
437 Self(AcknowledgementBinding {
438 owner: route.owner(),
439 target: route.target(),
440 slot_index: route.slot_index(),
441 cell_index: route.cell_index(),
442 generation,
443 })
444 }
445}
446
447#[derive(Clone, Copy, Debug, Eq, PartialEq)]
448struct AcknowledgementBinding {
449 owner: RoleId,
450 target: RoleId,
451 slot_index: u32,
452 cell_index: u32,
453 generation: u64,
454}
455
456pub struct AcknowledgementWriter<'a> {
458 cell: &'a AcknowledgementCell,
459 binding: AcknowledgementBinding,
460 _not_sync: PhantomData<Cell<()>>,
461}
462
463impl<'a> AcknowledgementWriter<'a> {
464 pub unsafe fn bind(
472 cell: &'a AcknowledgementCell,
473 binding: AcknowledgementWriterBinding,
474 ) -> Self {
475 Self {
476 cell,
477 binding: binding.0,
478 _not_sync: PhantomData,
479 }
480 }
481
482 pub fn acknowledge(
484 &mut self,
485 observation: SlotObservation,
486 ) -> Result<(), AcknowledgementError> {
487 if observation.role != self.binding.target {
488 return Err(AcknowledgementError::WrongTarget);
489 }
490 if observation.generation != self.binding.generation {
491 return Err(AcknowledgementError::StaleGeneration);
492 }
493 if observation.slot_index != self.binding.slot_index {
494 return Err(AcknowledgementError::WrongSlot);
495 }
496 if observation.sequence == 0 {
497 return Err(AcknowledgementError::UnpublishedSequence);
498 }
499 let current = self.cell.sequence.load(Ordering::Relaxed);
500 if observation.sequence < current {
501 return Err(AcknowledgementError::NonMonotonic {
502 current,
503 next: observation.sequence,
504 });
505 }
506 if observation.sequence == current {
507 return Ok(());
508 }
509 self.cell
510 .sequence
511 .store(observation.sequence, Ordering::Release);
512 Ok(())
513 }
514}
515
516pub struct AcknowledgementReader<'a> {
518 cell: &'a AcknowledgementCell,
519 binding: AcknowledgementBinding,
520}
521
522impl<'a> AcknowledgementReader<'a> {
523 pub unsafe fn bind(
531 cell: &'a AcknowledgementCell,
532 binding: AcknowledgementReaderBinding,
533 ) -> Self {
534 Self {
535 cell,
536 binding: binding.0,
537 }
538 }
539
540 pub fn observe(&self) -> AcknowledgementObservation {
542 AcknowledgementObservation {
543 owner: self.binding.owner,
544 target: self.binding.target,
545 generation: self.binding.generation,
546 slot_index: self.binding.slot_index,
547 cell_index: self.binding.cell_index,
548 sequence: self.cell.sequence.load(Ordering::Acquire),
549 }
550 }
551}
552
553#[derive(Clone, Copy, Debug, Eq, PartialEq)]
555pub struct AcknowledgementObservation {
556 owner: RoleId,
557 target: RoleId,
558 generation: u64,
559 slot_index: u32,
560 cell_index: u32,
561 sequence: u64,
562}
563
564impl AcknowledgementObservation {
565 pub const fn owner(self) -> RoleId {
567 self.owner
568 }
569 pub const fn target(self) -> RoleId {
571 self.target
572 }
573 pub const fn generation(self) -> u64 {
575 self.generation
576 }
577 pub const fn slot_index(self) -> u32 {
579 self.slot_index
580 }
581 pub const fn cell_index(self) -> u32 {
583 self.cell_index
584 }
585 pub const fn sequence(self) -> u64 {
587 self.sequence
588 }
589}
590
591#[derive(Clone, Copy, Debug, Eq, PartialEq)]
593pub enum SlotError {
594 ZeroGeneration,
596 UnpublishedSequence,
598 StaleGeneration {
600 expected: u64,
602 actual: u64,
604 },
605 SequenceWrap,
607 WrongSlot {
609 expected: u32,
611 actual: u32,
613 },
614 UnexpectedFirstSequence {
616 expected: u64,
618 actual: u64,
620 },
621 UnexpectedNextSequence {
623 expected: u64,
625 actual: u64,
627 },
628 MissingAcknowledgement {
630 sequence: u64,
632 },
633 WrongAcknowledgementTarget,
635 WrongAcknowledgementOwner,
637 WrongAcknowledgementSlot,
639 WrongAcknowledgementCell,
641 StaleAcknowledgementGeneration,
643 LaggingAcknowledgement {
645 expected: u64,
647 actual: u64,
649 },
650 FutureAcknowledgement {
652 expected: u64,
654 actual: u64,
656 },
657 StaleSequence {
659 expected: u64,
661 actual: u64,
663 },
664 PayloadTooLarge {
666 length: u32,
668 capacity: u32,
670 },
671 ChangedPayloadLength {
673 expected: u32,
675 actual: u32,
677 },
678 WrongObservationRole,
680}
681
682#[derive(Clone, Copy, Debug, Eq, PartialEq)]
684pub enum AcknowledgementError {
685 WrongTarget,
687 StaleGeneration,
689 WrongSlot,
691 UnpublishedSequence,
693 NonMonotonic {
695 current: u64,
697 next: u64,
699 },
700}
701
702impl fmt::Display for SlotError {
703 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
704 write!(formatter, "slot operation failed: {self:?}")
705 }
706}
707impl fmt::Display for AcknowledgementError {
708 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
709 write!(formatter, "acknowledgement failed: {self:?}")
710 }
711}
712
713#[cfg(feature = "std")]
714impl std::error::Error for SlotError {}
715#[cfg(feature = "std")]
716impl std::error::Error for AcknowledgementError {}
717
718fn validate_bound_generation(header: &SlotMetadata, expected: u64) -> Result<(), SlotError> {
719 if expected == 0 {
720 return Err(SlotError::ZeroGeneration);
721 }
722 let actual = header.generation.load(Ordering::Relaxed);
723 if actual == expected {
724 Ok(())
725 } else {
726 Err(SlotError::StaleGeneration { expected, actual })
727 }
728}
729
730fn validate_sequence_slot(binding: SlotBinding, sequence: u64) -> Result<(), SlotError> {
731 if sequence == 0 {
732 return Err(SlotError::UnpublishedSequence);
733 }
734 let expected = ((sequence - 1) % u64::from(binding.slot_count)) as u32;
735 if binding.slot_index == expected {
736 Ok(())
737 } else {
738 Err(SlotError::WrongSlot {
739 expected,
740 actual: binding.slot_index,
741 })
742 }
743}
744
745const _: () = assert!(core::mem::size_of::<SlotMetadata>() == SLOT_HEADER_SIZE as usize);
746const _: () = assert!(core::mem::align_of::<SlotMetadata>() == 64);
747const _: () = assert!(core::mem::offset_of!(SlotMetadata, generation) == 0);
748const _: () = assert!(core::mem::offset_of!(SlotMetadata, payload_len) == 8);
749const _: () = assert!(core::mem::offset_of!(SlotMetadata, reserved_word) == 12);
750const _: () = assert!(core::mem::offset_of!(SlotMetadata, published_sequence) == 16);
751const _: () = assert!(core::mem::offset_of!(SlotMetadata, reserved) == 24);
752const _: () = assert!(core::mem::size_of::<AcknowledgementCell>() == 64);
753const _: () = assert!(core::mem::align_of::<AcknowledgementCell>() == 64);
754const _: () = assert!(core::mem::offset_of!(AcknowledgementCell, sequence) == 0);
755const _: () = assert!(core::mem::offset_of!(AcknowledgementCell, reserved) == 8);
756
757#[cfg(test)]
758#[path = "slot_test.rs"]
759mod tests;