1#![forbid(unsafe_code)]
2
3use std::array;
44use std::fmt;
45use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
46use std::sync::{Arc, Condvar, Mutex, TryLockError};
47use std::time::{Duration, Instant};
48
49mod block;
50pub use block::{BlockError, BlockLane, Exchange};
51pub mod events;
52#[cfg(test)]
53mod serial_tests;
54use events::{CONTEXT_WORDS, EVENT_WORDS, METADATA_WORDS, PACKET_WORDS};
55pub use events::{Event, EventBuffer, EventData, MAX_EVENTS, MAX_SYSEX_BYTES, Transport};
56#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
57#[repr(u32)]
58pub enum SampleFormat {
59 #[default]
60 F32 = 0,
61 F64 = 1,
62}
63impl SampleFormat {
64 pub fn words(self) -> usize {
65 if self == Self::F64 { 2 } else { 1 }
66 }
67}
68
69pub const MAGIC: u32 = u32::from_le_bytes(*b"SSRR");
70pub const LAYOUT_VERSION: u32 = 4;
71pub const HEADER_BYTES: usize = 512;
72pub const CACHE_LINE: usize = 64;
73pub const MAX_CHANNELS: u32 = 64;
74pub const MAX_FRAMES: u32 = 65_536;
75pub const MAX_SLOTS: u32 = 16;
76pub const MAX_REGION_BYTES: usize = 128 * 1024 * 1024;
77pub const COUNTER_BYTES: usize = 2 * CACHE_LINE;
80pub const COUNTER_WORDS: usize = COUNTER_BYTES / 8;
82pub const MIN_REGION_BYTES: usize = HEADER_BYTES + COUNTER_BYTES;
84const HEADER_WORDS: usize = HEADER_BYTES / 4;
85
86const MAGIC_I: usize = 0;
87const VERSION_I: usize = 1;
88const TOTAL_I: usize = 2;
89const LANES_I: usize = 3;
90const INPUTS_I: usize = 4;
91const OUTPUTS_I: usize = 5;
92const FRAMES_I: usize = 6;
93const RATE_I: usize = 7;
94const LATENCY_I: usize = 8;
95pub const LATENCY_BOUND_OFFSET: usize = LATENCY_I * 4;
98const STATE_I: usize = 9;
99const FAULT_I: usize = 10;
100const PRODUCER_PID_I: usize = 11;
101const CONSUMER_PID_I: usize = 12;
102const FORMAT_I: usize = 13;
103const SLOTS_I: usize = 14;
104const LANE_GEOMETRY_I: usize = 16;
105const SEQUENCE_I: usize = 32;
106const COMPLETED_I: usize = 48;
107
108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
109#[repr(u32)]
110pub enum State {
111 Empty = 0,
112 Announced = 1,
113 Ready = 2,
114 Closing = 3,
115 Fault = 4,
116}
117
118impl State {
119 fn from_wire(value: u32) -> Option<Self> {
120 Some(match value {
121 0 => Self::Empty,
122 1 => Self::Announced,
123 2 => Self::Ready,
124 3 => Self::Closing,
125 4 => Self::Fault,
126 _ => return None,
127 })
128 }
129}
130
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
132#[repr(u32)]
133pub enum Fault {
134 None = 0,
135 LayoutMismatch = 1,
136 EngineFailed = 2,
137 HostShuttingDown = 3,
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
141pub struct Geometry {
142 pub sample_format: SampleFormat,
143 pub lanes: u32,
144 pub slots: u32,
145 pub input_channels: u32,
146 pub output_channels: u32,
147 pub max_frames: u32,
148 pub sample_rate: u32,
149 pub latency_bound_frames: u32,
150}
151
152impl Geometry {
153 pub fn validate(self) -> Result<Self, GeometryError> {
154 if !(1..=2).contains(&self.lanes) {
155 return Err(GeometryError::LaneCount);
156 }
157 if !matches!(self.slots, 2 | 4 | 8 | 16) {
158 return Err(GeometryError::Slots);
159 }
160 if self.input_channels > MAX_CHANNELS || self.output_channels > MAX_CHANNELS {
161 return Err(GeometryError::Channels);
162 }
163 if !(1..=MAX_FRAMES).contains(&self.max_frames) {
164 return Err(GeometryError::Frames);
165 }
166 if !(1_000..=1_000_000).contains(&self.sample_rate) {
167 return Err(GeometryError::SampleRate);
168 }
169 if layout(self).is_none() {
170 return Err(GeometryError::RegionTooLarge);
171 }
172 Ok(self)
173 }
174
175 pub fn planes(self) -> usize {
176 self.input_channels.max(self.output_channels) as usize
177 }
178
179 pub fn samples_per_slot(self) -> usize {
180 self.planes() * self.max_frames as usize
181 }
182
183 pub fn total_bytes(self) -> Result<usize, GeometryError> {
184 self.validate()?;
185 Ok(layout(self)
186 .expect("validated geometry has a layout")
187 .total_bytes)
188 }
189
190 pub fn mapping_split(self, mapping_bytes: usize) -> Result<usize, GeometryError> {
195 let total = self.total_bytes()?;
196 if total > mapping_bytes {
197 return Err(GeometryError::RegionTooLarge);
198 }
199 Ok(total - COUNTER_BYTES)
200 }
201}
202
203#[derive(Clone, Copy, Debug, Eq, PartialEq)]
204pub enum GeometryError {
205 LaneCount,
206 Slots,
207 Channels,
208 Frames,
209 SampleRate,
210 RegionTooLarge,
211}
212
213#[derive(Clone, Copy, Debug)]
214struct Layout {
215 slot_bytes: usize,
216 first_slot_offsets: [usize; 2],
217 slots: u32,
218 prefix_bytes: usize,
219 total_bytes: usize,
220}
221
222impl Layout {
223 fn slot_offset(self, lane: usize, sequence: u32) -> usize {
224 self.first_slot_offsets[lane] + (sequence & (self.slots - 1)) as usize * self.slot_bytes
225 }
226}
227
228fn round_up(value: usize, alignment: usize) -> Option<usize> {
229 value
230 .checked_add(alignment - 1)
231 .map(|v| v / alignment * alignment)
232}
233
234fn layout(geometry: Geometry) -> Option<Layout> {
235 let audio_words = geometry
236 .samples_per_slot()
237 .checked_mul(geometry.sample_format.words())?;
238 let slot_bytes = round_up(
239 audio_words.checked_add(METADATA_WORDS)?.checked_mul(4)?,
240 CACHE_LINE,
241 )?;
242 let mut offset = HEADER_BYTES;
243 let mut first_slot_offsets = [0; 2];
244 for first_slot_offset in first_slot_offsets.iter_mut().take(geometry.lanes as usize) {
245 *first_slot_offset = offset;
246 offset = offset.checked_add(slot_bytes.checked_mul(geometry.slots as usize)?)?;
247 }
248 let prefix_bytes = round_up(offset, CACHE_LINE)?;
249 let total_bytes = prefix_bytes.checked_add(COUNTER_BYTES)?;
250 (total_bytes < MAX_REGION_BYTES).then_some(Layout {
251 slot_bytes,
252 first_slot_offsets,
253 slots: geometry.slots,
254 prefix_bytes,
255 total_bytes,
256 })
257}
258
259pub trait Signal: Send + Sync {
260 fn signal(&self);
261}
262
263impl<T: Signal + ?Sized> Signal for Arc<T> {
264 fn signal(&self) {
265 T::signal(self);
266 }
267}
268
269impl<T: Signal + ?Sized> Signal for Box<T> {
270 fn signal(&self) {
271 T::signal(self);
272 }
273}
274
275pub trait Wait: Send + Sync {
276 fn wait(&self, timeout: Duration) -> Waited;
277}
278
279#[derive(Clone, Copy, Debug, Eq, PartialEq)]
280pub enum Waited {
281 Signalled,
282 TimedOut,
283 PeerGone,
284}
285
286#[derive(Debug)]
287struct EventInner {
288 generation: AtomicU64,
289 gone: AtomicBool,
290 contended: AtomicBool,
291 observed: Mutex<u64>,
292 changed: Condvar,
293}
294
295#[derive(Clone, Debug)]
299pub struct InProcessEvent(Arc<EventInner>);
300
301impl InProcessEvent {
302 pub fn new() -> Self {
303 Self(Arc::new(EventInner {
304 generation: AtomicU64::new(0),
305 gone: AtomicBool::new(false),
306 contended: AtomicBool::new(false),
307 observed: Mutex::new(0),
308 changed: Condvar::new(),
309 }))
310 }
311
312 pub fn peer_gone(&self) {
313 self.0.gone.store(true, Ordering::Release);
314 self.notify(false);
315 }
316
317 fn notify(&self, one: bool) {
318 match self.0.observed.try_lock() {
322 Ok(_guard) => {
323 if one {
324 self.0.changed.notify_one();
325 } else {
326 self.0.changed.notify_all();
327 }
328 }
329 Err(TryLockError::Poisoned(error)) => {
330 let _guard = error.into_inner();
331 if one {
332 self.0.changed.notify_one();
333 } else {
334 self.0.changed.notify_all();
335 }
336 }
337 Err(TryLockError::WouldBlock) => {
338 self.0.contended.store(true, Ordering::Release);
339 if one {
340 self.0.changed.notify_one();
341 } else {
342 self.0.changed.notify_all();
343 }
344 }
345 }
346 }
347}
348
349impl Default for InProcessEvent {
350 fn default() -> Self {
351 Self::new()
352 }
353}
354
355impl Signal for InProcessEvent {
356 fn signal(&self) {
357 self.0.generation.fetch_add(1, Ordering::Release);
358 self.notify(true);
359 }
360}
361
362impl Wait for InProcessEvent {
363 fn wait(&self, timeout: Duration) -> Waited {
364 if self.0.gone.load(Ordering::Acquire) {
365 return Waited::PeerGone;
366 }
367 let mut observed = self.0.observed.lock().unwrap_or_else(|e| e.into_inner());
368 let current = self.0.generation.load(Ordering::Acquire);
369 if current != *observed {
370 *observed = current;
371 return Waited::Signalled;
372 }
373 if self.0.contended.swap(false, Ordering::AcqRel) {
374 let current = self.0.generation.load(Ordering::Acquire);
375 if current != *observed {
376 *observed = current;
377 return Waited::Signalled;
378 }
379 if self.0.gone.load(Ordering::Acquire) {
380 return Waited::PeerGone;
381 }
382 }
383 let deadline = Instant::now() + timeout;
384 loop {
385 let now = Instant::now();
386 if now >= deadline {
387 return Waited::TimedOut;
388 }
389 let slice = deadline
390 .saturating_duration_since(now)
391 .min(Duration::from_millis(1));
392 let (next_observed, _) = self
393 .0
394 .changed
395 .wait_timeout_while(observed, slice, |seen| {
396 !self.0.gone.load(Ordering::Acquire)
397 && !self.0.contended.swap(false, Ordering::AcqRel)
398 && self.0.generation.load(Ordering::Acquire) == *seen
399 })
400 .unwrap_or_else(|e| e.into_inner());
401 observed = next_observed;
402 if self.0.gone.load(Ordering::Acquire) {
403 return Waited::PeerGone;
404 }
405 let current = self.0.generation.load(Ordering::Acquire);
406 if current != *observed {
407 *observed = current;
408 return Waited::Signalled;
409 }
410 }
411 }
412}
413
414#[derive(Clone, Copy, Debug)]
417pub struct Region<'a> {
418 words32: &'a [AtomicU32],
419 words64: &'a [AtomicU64],
420}
421
422impl<'a> Region<'a> {
423 pub fn new(words32: &'a [AtomicU32], words64: &'a [AtomicU64]) -> Self {
424 Self { words32, words64 }
425 }
426
427 pub fn state(self) -> Option<State> {
428 State::from_wire(self.load32(STATE_I, Ordering::Acquire))
429 }
430
431 pub fn fault_code(self) -> u32 {
432 self.load32(FAULT_I, Ordering::Acquire)
433 }
434
435 pub fn heartbeat(self) -> u64 {
436 self.words64
437 .first()
438 .map_or(0, |v| v.load(Ordering::Acquire))
439 }
440
441 pub fn last_error(self) -> u64 {
442 self.words64
443 .get(CACHE_LINE / 8)
444 .map_or(0, |v| v.load(Ordering::Acquire))
445 }
446
447 fn load32(self, index: usize, ordering: Ordering) -> u32 {
448 self.words32.get(index).map_or(0, |v| v.load(ordering))
449 }
450
451 fn store32(self, index: usize, value: u32, ordering: Ordering) {
452 if let Some(word) = self.words32.get(index) {
453 word.store(value, ordering);
454 }
455 }
456}
457
458#[derive(Debug)]
459pub struct RegionBuf {
460 words32: Box<[AtomicU32]>,
461 words64: Box<[AtomicU64]>,
462}
463
464impl RegionBuf {
465 pub fn new(geometry: Geometry) -> Result<Self, GeometryError> {
466 let geometry = geometry.validate()?;
467 let layout = layout(geometry).expect("validated geometry has a layout");
468 Ok(Self {
469 words32: (0..layout.prefix_bytes / 4)
470 .map(|_| AtomicU32::new(0))
471 .collect(),
472 words64: (0..COUNTER_WORDS).map(|_| AtomicU64::new(0)).collect(),
473 })
474 }
475
476 pub fn region(&self) -> Region<'_> {
477 Region::new(&self.words32, &self.words64)
478 }
479}
480
481pub trait RegionMemory: Send + Sync {
486 fn region(&self) -> Region<'_>;
487}
488
489impl RegionMemory for RegionBuf {
490 fn region(&self) -> Region<'_> {
491 self.region()
492 }
493}
494
495impl<T: RegionMemory + ?Sized> RegionMemory for Arc<T> {
496 fn region(&self) -> Region<'_> {
497 T::region(self)
498 }
499}
500
501impl<T: RegionMemory + ?Sized> RegionMemory for Box<T> {
502 fn region(&self) -> Region<'_> {
503 T::region(self)
504 }
505}
506
507#[derive(Clone, Copy, Debug, Eq, PartialEq)]
508pub enum HeaderError {
509 TooShort,
510 Magic,
511 Version,
512 State,
513 Geometry(GeometryError),
514 TotalBytes,
515 LaneGeometry,
516}
517
518#[derive(Clone, Copy, Debug, Eq, PartialEq)]
519pub enum Publish {
520 Published,
521 Dropped,
522}
523
524#[derive(Clone, Copy, Debug)]
535pub struct Publisher {
536 geometry: Geometry,
537 layout: Layout,
538}
539
540impl Publisher {
541 pub fn geometry(&self) -> Geometry {
542 self.geometry
543 }
544
545 pub fn state(&self, region: Region<'_>) -> Option<State> {
546 region.state()
547 }
548
549 pub fn published(&self, region: Region<'_>, lane: usize) -> u32 {
552 if lane >= self.geometry.lanes as usize {
553 return 0;
554 }
555 u32::from_le(region.load32(SEQUENCE_I + lane, Ordering::Acquire))
556 }
557
558 pub fn completed(&self, region: Region<'_>, lane: usize) -> u32 {
561 if lane >= self.geometry.lanes as usize {
562 return 0;
563 }
564 u32::from_le(region.load32(COMPLETED_I + lane, Ordering::Acquire))
565 }
566
567 pub fn try_publish<F>(
572 &self,
573 region: Region<'_>,
574 lane: usize,
575 sequence: u32,
576 work: Option<&dyn Signal>,
577 write: F,
578 ) -> Publish
579 where
580 F: FnOnce(&mut SlotWriter<'_>),
581 {
582 if region.state() != Some(State::Ready) || lane >= self.geometry.lanes as usize {
583 return Publish::Dropped;
584 }
585 let completed = self.completed(region, lane);
586 if !sequence_at_least(completed, sequence.wrapping_sub(self.geometry.slots)) {
587 return Publish::Dropped;
588 }
589 let offset = self.layout.slot_offset(lane, sequence) / 4;
590 let Some(samples) = region.words32.get(
591 offset..offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words(),
592 ) else {
593 return Publish::Dropped;
594 };
595 let meta_offset =
596 offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words();
597 let Some(metadata) = region
598 .words32
599 .get(meta_offset..meta_offset + METADATA_WORDS)
600 else {
601 return Publish::Dropped;
602 };
603 let mut writer = SlotWriter {
604 samples,
605 metadata,
606 format: self.geometry.sample_format,
607 frames: self.geometry.max_frames as usize,
608 planes: self.geometry.planes(),
609 };
610 writer.set_transport(Transport::default());
611 writer.set_events(&[]);
612 metadata[CONTEXT_WORDS + PACKET_WORDS].store(0, Ordering::Relaxed);
613 metadata[CONTEXT_WORDS + PACKET_WORDS + 1].store(0, Ordering::Relaxed);
614 write(&mut writer);
615 region.store32(SEQUENCE_I + lane, sequence.to_le(), Ordering::Release);
616 if let Some(signal) = work {
617 signal.signal();
618 }
619 Publish::Published
620 }
621
622 pub fn try_read<F>(&self, region: Region<'_>, lane: usize, sequence: u32, read: F) -> bool
624 where
625 F: FnOnce(SlotReader<'_>),
626 {
627 if lane >= self.geometry.lanes as usize
628 || !sequence_at_least(self.completed(region, lane), sequence)
629 {
630 return false;
631 }
632 let published = self.published(region, lane);
633 if published.wrapping_sub(sequence) >= self.geometry.slots {
634 return false;
635 }
636 let offset = self.layout.slot_offset(lane, sequence) / 4;
637 let Some(samples) = region.words32.get(
638 offset..offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words(),
639 ) else {
640 return false;
641 };
642 let meta_offset =
643 offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words();
644 let Some(metadata) = region
645 .words32
646 .get(meta_offset..meta_offset + METADATA_WORDS)
647 else {
648 return false;
649 };
650 read(SlotReader {
651 samples,
652 metadata,
653 format: self.geometry.sample_format,
654 frames: self.geometry.max_frames as usize,
655 planes: self.geometry.planes(),
656 });
657 true
658 }
659}
660
661pub struct Producer<'a> {
662 region: Region<'a>,
663 geometry: Option<Geometry>,
664 layout: Option<Layout>,
665 work: [Option<&'a dyn Signal>; 2],
666 ready: &'a dyn Wait,
667}
668
669impl fmt::Debug for Producer<'_> {
670 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
671 formatter
672 .debug_struct("Producer")
673 .field("region", &self.region)
674 .field("geometry", &self.geometry)
675 .field("layout", &self.layout)
676 .finish_non_exhaustive()
677 }
678}
679
680impl<'a> Producer<'a> {
681 pub fn new(region: Region<'a>, work: [Option<&'a dyn Signal>; 2], ready: &'a dyn Wait) -> Self {
682 Self {
683 region,
684 geometry: None,
685 layout: None,
686 work,
687 ready,
688 }
689 }
690
691 pub fn announce(&mut self, geometry: Geometry, producer_pid: u32) -> Result<(), GeometryError> {
692 self.announce_at(geometry, producer_pid, 0)
693 }
694
695 pub fn announce_at(
697 &mut self,
698 geometry: Geometry,
699 producer_pid: u32,
700 origin: u32,
701 ) -> Result<(), GeometryError> {
702 let geometry = geometry.validate()?;
703 let layout = layout(geometry).expect("validated geometry has a layout");
704 if self.region.words32.len() * 4 < layout.prefix_bytes
705 || self.region.words64.len() < COUNTER_WORDS
706 {
707 return Err(GeometryError::RegionTooLarge);
708 }
709 for word in self.region.words32.iter().take(HEADER_WORDS) {
710 word.store(0, Ordering::Relaxed);
711 }
712 for word in self.region.words64 {
713 word.store(0, Ordering::Relaxed);
714 }
715 let fields = [
716 (MAGIC_I, MAGIC),
717 (VERSION_I, LAYOUT_VERSION),
718 (TOTAL_I, layout.total_bytes as u32),
719 (LANES_I, geometry.lanes),
720 (INPUTS_I, geometry.input_channels),
721 (OUTPUTS_I, geometry.output_channels),
722 (FRAMES_I, geometry.max_frames),
723 (RATE_I, geometry.sample_rate),
724 (FORMAT_I, geometry.sample_format as u32),
725 (SLOTS_I, geometry.slots),
726 (LATENCY_I, geometry.latency_bound_frames),
727 (PRODUCER_PID_I, producer_pid),
728 ];
729 for (index, value) in fields {
730 self.region.store32(index, value.to_le(), Ordering::Relaxed);
731 }
732 for lane in 0..geometry.lanes as usize {
733 self.region
734 .store32(SEQUENCE_I + lane, origin.to_le(), Ordering::Relaxed);
735 self.region
736 .store32(COMPLETED_I + lane, origin.to_le(), Ordering::Relaxed);
737 let base = LANE_GEOMETRY_I + lane * 3;
738 self.region.store32(
739 base,
740 (layout.first_slot_offsets[lane] as u32).to_le(),
741 Ordering::Relaxed,
742 );
743 self.region.store32(
744 base + 1,
745 (layout.slot_bytes as u32).to_le(),
746 Ordering::Relaxed,
747 );
748 self.region
749 .store32(base + 2, geometry.slots.to_le(), Ordering::Relaxed);
750 }
751 self.region
752 .store32(FAULT_I, Fault::None as u32, Ordering::Release);
753 self.region
754 .store32(STATE_I, State::Announced as u32, Ordering::Release);
755 self.geometry = Some(geometry);
756 self.layout = Some(layout);
757 Ok(())
758 }
759
760 pub fn wait_ready(&self, timeout: Duration) -> Waited {
761 let deadline = Instant::now() + timeout;
762 loop {
763 match self.region.state() {
764 Some(State::Ready) => return Waited::Signalled,
765 Some(State::Fault | State::Closing) | None => return Waited::PeerGone,
766 _ => {}
767 }
768 let now = Instant::now();
769 if now >= deadline {
770 return Waited::TimedOut;
771 }
772 match self.ready.wait(deadline.saturating_duration_since(now)) {
773 Waited::Signalled => {}
774 other => return other,
775 }
776 }
777 }
778
779 pub fn publisher(&self) -> Option<Publisher> {
781 Some(Publisher {
782 geometry: self.geometry?,
783 layout: self.layout?,
784 })
785 }
786
787 pub fn completed(&self, lane: usize) -> u32 {
788 self.publisher()
789 .map_or(0, |publisher| publisher.completed(self.region, lane))
790 }
791
792 pub fn try_publish<F>(&self, lane: usize, sequence: u32, write: F) -> Publish
793 where
794 F: FnOnce(&mut SlotWriter<'_>),
795 {
796 let Some(publisher) = self.publisher() else {
797 return Publish::Dropped;
798 };
799 let Some(work) = self.work.get(lane).copied() else {
800 return Publish::Dropped;
801 };
802 publisher.try_publish(self.region, lane, sequence, work, write)
803 }
804
805 pub fn try_read<F>(&self, lane: usize, sequence: u32, read: F) -> bool
806 where
807 F: FnOnce(SlotReader<'_>),
808 {
809 self.publisher()
810 .is_some_and(|publisher| publisher.try_read(self.region, lane, sequence, read))
811 }
812
813 pub fn close(&self) {
814 self.region
815 .store32(STATE_I, State::Closing as u32, Ordering::Release);
816 for signal in self.work.iter().flatten() {
817 signal.signal();
818 }
819 }
820}
821
822#[derive(Debug)]
823pub struct SlotWriter<'a> {
824 metadata: &'a [AtomicU32],
825 format: SampleFormat,
826 samples: &'a [AtomicU32],
827 frames: usize,
828 planes: usize,
829}
830
831impl SlotWriter<'_> {
832 pub fn frames(&self) -> usize {
833 self.frames
834 }
835
836 pub fn planes(&self) -> usize {
837 self.planes
838 }
839
840 pub fn write(&mut self, plane: usize, frame: usize, sample: f32) {
841 self.write_f64(plane, frame, f64::from(sample));
842 }
843
844 pub fn write_f64(&mut self, plane: usize, frame: usize, sample: f64) {
845 let index = (plane * self.frames + frame) * self.format.words();
846 if self.format == SampleFormat::F64 {
847 self.samples[index].store((sample.to_bits() as u32).to_le(), Ordering::Relaxed);
848 self.samples[index + 1]
849 .store(((sample.to_bits() >> 32) as u32).to_le(), Ordering::Relaxed);
850 } else {
851 self.samples[index].store((sample as f32).to_bits().to_le(), Ordering::Relaxed);
852 }
853 }
854
855 pub fn write_plane(&mut self, plane: usize, samples: &[f32]) -> bool {
856 if plane >= self.planes || samples.len() != self.frames {
857 return false;
858 }
859 let words = self.format.words();
860 let destination =
861 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
862 if self.format == SampleFormat::F64 {
863 for (to, from) in destination.as_chunks::<2>().0.iter().zip(samples) {
864 let bits = f64::from(*from).to_bits();
865 to[0].store((bits as u32).to_le(), Ordering::Relaxed);
866 to[1].store(((bits >> 32) as u32).to_le(), Ordering::Relaxed);
867 }
868 } else {
869 for (to, from) in destination.iter().zip(samples) {
870 to.store(from.to_bits().to_le(), Ordering::Relaxed);
871 }
872 }
873 true
874 }
875
876 pub fn write_plane_f64(&mut self, plane: usize, samples: &[f64]) -> bool {
877 if plane >= self.planes || samples.len() != self.frames {
878 return false;
879 }
880 let words = self.format.words();
881 let destination =
882 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
883 if self.format == SampleFormat::F64 {
884 for (to, from) in destination.as_chunks::<2>().0.iter().zip(samples) {
885 let bits = from.to_bits();
886 to[0].store((bits as u32).to_le(), Ordering::Relaxed);
887 to[1].store(((bits >> 32) as u32).to_le(), Ordering::Relaxed);
888 }
889 } else {
890 for (to, from) in destination.iter().zip(samples) {
891 to.store((*from as f32).to_bits().to_le(), Ordering::Relaxed);
892 }
893 }
894 true
895 }
896
897 pub fn clear_plane(&mut self, plane: usize) -> bool {
898 if plane >= self.planes {
899 return false;
900 }
901 let words = self.format.words();
902 let destination =
903 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
904 for word in destination {
905 word.store(0, Ordering::Relaxed);
906 }
907 true
908 }
909
910 pub fn set_transport(&mut self, transport: Transport) {
911 for (to, from) in self.metadata.iter().zip(transport.words()) {
912 to.store(from.to_le(), Ordering::Relaxed);
913 }
914 }
915 pub fn set_events(&mut self, events: &[Event]) -> bool {
916 if events.len() > MAX_EVENTS || events.iter().any(|e| !e.valid(self.frames as u32)) {
917 return false;
918 }
919 store_packet(
920 &self.metadata[CONTEXT_WORDS..CONTEXT_WORDS + PACKET_WORDS],
921 events,
922 0,
923 );
924 true
925 }
926 pub fn copy_from(&mut self, samples: &[f32]) -> bool {
927 if self.format != SampleFormat::F32 || samples.len() != self.samples.len() {
928 return false;
929 }
930 for (destination, sample) in self.samples.iter().zip(samples) {
931 destination.store(sample.to_bits(), Ordering::Relaxed);
932 }
933 true
934 }
935}
936
937#[derive(Clone, Copy, Debug)]
938pub struct SlotReader<'a> {
939 metadata: &'a [AtomicU32],
940 format: SampleFormat,
941 samples: &'a [AtomicU32],
942 frames: usize,
943 planes: usize,
944}
945
946impl SlotReader<'_> {
947 pub fn frames(self) -> usize {
948 self.frames
949 }
950
951 pub fn planes(self) -> usize {
952 self.planes
953 }
954
955 pub fn read(self, plane: usize, frame: usize) -> f32 {
956 self.read_f64(plane, frame) as f32
957 }
958
959 pub fn read_f64(self, plane: usize, frame: usize) -> f64 {
960 let index = (plane * self.frames + frame) * self.format.words();
961 let lo = u32::from_le(self.samples[index].load(Ordering::Relaxed));
962 if self.format == SampleFormat::F64 {
963 let hi = u32::from_le(self.samples[index + 1].load(Ordering::Relaxed));
964 f64::from_bits(u64::from(lo) | (u64::from(hi) << 32))
965 } else {
966 f64::from(f32::from_bits(lo))
967 }
968 }
969
970 pub fn read_plane(self, plane: usize, samples: &mut [f32]) -> bool {
971 if plane >= self.planes || samples.len() != self.frames {
972 return false;
973 }
974 let words = self.format.words();
975 let source = &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
976 if self.format == SampleFormat::F64 {
977 for (to, from) in samples.iter_mut().zip(source.as_chunks::<2>().0.iter()) {
978 let lo = u32::from_le(from[0].load(Ordering::Relaxed));
979 let hi = u32::from_le(from[1].load(Ordering::Relaxed));
980 *to = f64::from_bits(u64::from(lo) | (u64::from(hi) << 32)) as f32;
981 }
982 } else {
983 for (to, from) in samples.iter_mut().zip(source) {
984 *to = f32::from_bits(u32::from_le(from.load(Ordering::Relaxed)));
985 }
986 }
987 true
988 }
989
990 pub fn read_plane_f64(self, plane: usize, samples: &mut [f64]) -> bool {
991 if plane >= self.planes || samples.len() != self.frames {
992 return false;
993 }
994 let words = self.format.words();
995 let source = &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
996 if self.format == SampleFormat::F64 {
997 for (to, from) in samples.iter_mut().zip(source.as_chunks::<2>().0.iter()) {
998 let lo = u32::from_le(from[0].load(Ordering::Relaxed));
999 let hi = u32::from_le(from[1].load(Ordering::Relaxed));
1000 *to = f64::from_bits(u64::from(lo) | (u64::from(hi) << 32));
1001 }
1002 } else {
1003 for (to, from) in samples.iter_mut().zip(source) {
1004 *to = f64::from(f32::from_bits(u32::from_le(from.load(Ordering::Relaxed))));
1005 }
1006 }
1007 true
1008 }
1009
1010 pub fn output_events(self, events: &mut EventBuffer) -> bool {
1011 load_packet(
1012 &self.metadata[CONTEXT_WORDS + PACKET_WORDS..],
1013 self.frames as u32,
1014 events,
1015 )
1016 }
1017 pub fn copy_to(self, samples: &mut [f32]) -> bool {
1018 if self.format != SampleFormat::F32 || samples.len() != self.samples.len() {
1019 return false;
1020 }
1021 for (destination, sample) in samples.iter_mut().zip(self.samples) {
1022 *destination = f32::from_bits(sample.load(Ordering::Relaxed));
1023 }
1024 true
1025 }
1026}
1027
1028fn store_packet(words: &[AtomicU32], events: &[Event], dropped: u32) {
1029 words[0].store((events.len() as u32).to_le(), Ordering::Relaxed);
1030 words[1].store(dropped.to_le(), Ordering::Relaxed);
1031 for (target, event) in words[2..].as_chunks::<EVENT_WORDS>().0.iter().zip(events) {
1032 for (to, from) in target.iter().zip(event.words()) {
1033 to.store(from.to_le(), Ordering::Relaxed);
1034 }
1035 }
1036}
1037fn load_packet(words: &[AtomicU32], frames: u32, events: &mut EventBuffer) -> bool {
1038 events.clear();
1039 let count = u32::from_le(words[0].load(Ordering::Relaxed)) as usize;
1040 if count > MAX_EVENTS {
1041 return false;
1042 }
1043 events.dropped = u32::from_le(words[1].load(Ordering::Relaxed));
1044 for source in words[2..].as_chunks::<EVENT_WORDS>().0.iter().take(count) {
1045 let mut values = [0; EVENT_WORDS];
1046 for (to, from) in values.iter_mut().zip(source) {
1047 *to = u32::from_le(from.load(Ordering::Relaxed));
1048 }
1049 let Some(event) = Event::from_words(&values, frames) else {
1050 events.clear();
1051 return false;
1052 };
1053 events.push(event);
1054 }
1055 true
1056}
1057
1058pub struct Consumer<'a> {
1065 region: Region<'a>,
1066 geometry: Option<Geometry>,
1067 layout: Option<Layout>,
1068 ready: &'a dyn Signal,
1069 done: [Option<&'a dyn Signal>; 2],
1070}
1071
1072impl fmt::Debug for Consumer<'_> {
1073 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1074 formatter
1075 .debug_struct("Consumer")
1076 .field("region", &self.region)
1077 .field("geometry", &self.geometry)
1078 .field("layout", &self.layout)
1079 .finish_non_exhaustive()
1080 }
1081}
1082
1083impl<'a> Consumer<'a> {
1084 pub fn new(
1085 region: Region<'a>,
1086 ready: &'a dyn Signal,
1087 done: [Option<&'a dyn Signal>; 2],
1088 ) -> Self {
1089 Self {
1090 region,
1091 geometry: None,
1092 layout: None,
1093 ready,
1094 done,
1095 }
1096 }
1097
1098 pub fn validate_and_ready(&mut self, consumer_pid: u32) -> Result<Geometry, HeaderError> {
1099 let geometry = validate_header(self.region)?;
1100 let layout = layout(geometry).expect("validated geometry has a layout");
1101 self.geometry = Some(geometry);
1102 self.layout = Some(layout);
1103 self.region
1104 .store32(CONSUMER_PID_I, consumer_pid.to_le(), Ordering::Relaxed);
1105 self.region
1106 .store32(FAULT_I, Fault::None as u32, Ordering::Relaxed);
1107 self.region
1108 .store32(STATE_I, State::Ready as u32, Ordering::Release);
1109 self.ready.signal();
1110 Ok(geometry)
1111 }
1112
1113 pub fn fault(&self, code: u32) {
1114 self.region.store32(FAULT_I, code, Ordering::Relaxed);
1115 self.region
1116 .store32(STATE_I, State::Fault as u32, Ordering::Release);
1117 self.ready.signal();
1118 for signal in self.done.iter().flatten() {
1119 signal.signal();
1120 }
1121 }
1122
1123 pub fn next_published(&self, lane: usize) -> Option<u32> {
1126 let geometry = self.geometry?;
1127 if lane >= geometry.lanes as usize || self.region.state() != Some(State::Ready) {
1128 return None;
1129 }
1130 let published = u32::from_le(self.region.load32(SEQUENCE_I + lane, Ordering::Acquire));
1131 let completed = u32::from_le(self.region.load32(COMPLETED_I + lane, Ordering::Acquire));
1132 let next = completed.wrapping_add(1);
1133 (published != completed && sequence_at_least(published, next)).then_some(next)
1134 }
1135
1136 pub fn slot_mut<F>(&self, lane: usize, sequence: u32, buffer: &mut [f32], process: F) -> bool
1139 where
1140 F: FnOnce(&mut [f32]),
1141 {
1142 let Some((geometry, layout)) = self.geometry.zip(self.layout) else {
1143 return false;
1144 };
1145 if geometry.sample_format != SampleFormat::F32
1146 || lane >= geometry.lanes as usize
1147 || buffer.len() != geometry.samples_per_slot()
1148 {
1149 return false;
1150 }
1151 let offset = layout.slot_offset(lane, sequence) / 4;
1152 let samples = &self.region.words32[offset..offset + buffer.len()];
1153 for (destination, sample) in buffer.iter_mut().zip(samples) {
1154 *destination = f32::from_bits(sample.load(Ordering::Relaxed));
1155 }
1156 process(buffer);
1157 for (destination, sample) in samples.iter().zip(buffer) {
1158 destination.store(sample.to_bits(), Ordering::Relaxed);
1159 }
1160 true
1161 }
1162
1163 pub fn slot_mut_f64<F>(
1164 &self,
1165 lane: usize,
1166 sequence: u32,
1167 buffer: &mut [f64],
1168 process: F,
1169 ) -> bool
1170 where
1171 F: FnOnce(&mut [f64]),
1172 {
1173 let Some((geometry, layout)) = self.geometry.zip(self.layout) else {
1174 return false;
1175 };
1176 if geometry.sample_format != SampleFormat::F64
1177 || lane >= geometry.lanes as usize
1178 || buffer.len() != geometry.samples_per_slot()
1179 {
1180 return false;
1181 }
1182 let offset = layout.slot_offset(lane, sequence) / 4;
1183 let samples = &self.region.words32[offset..offset + buffer.len() * 2];
1184 for (to, from) in buffer.iter_mut().zip(samples.as_chunks::<2>().0.iter()) {
1185 *to = f64::from_bits(
1186 u64::from(u32::from_le(from[0].load(Ordering::Relaxed)))
1187 | (u64::from(u32::from_le(from[1].load(Ordering::Relaxed))) << 32),
1188 );
1189 }
1190 process(buffer);
1191 for (to, from) in samples.as_chunks::<2>().0.iter().zip(buffer) {
1192 to[0].store((from.to_bits() as u32).to_le(), Ordering::Relaxed);
1193 to[1].store(((from.to_bits() >> 32) as u32).to_le(), Ordering::Relaxed);
1194 }
1195 true
1196 }
1197 fn metadata(&self, lane: usize, sequence: u32) -> Option<&[AtomicU32]> {
1198 let geometry = self.geometry?;
1199 let layout = self.layout?;
1200 if lane >= geometry.lanes as usize {
1201 return None;
1202 }
1203 let offset = layout.slot_offset(lane, sequence) / 4
1204 + geometry.samples_per_slot() * geometry.sample_format.words();
1205 self.region.words32.get(offset..offset + METADATA_WORDS)
1206 }
1207 pub fn input_packet(
1208 &self,
1209 lane: usize,
1210 sequence: u32,
1211 context: &mut Transport,
1212 events: &mut EventBuffer,
1213 ) -> bool {
1214 let Some(words) = self.metadata(lane, sequence) else {
1215 return false;
1216 };
1217 let mut raw = [0; CONTEXT_WORDS];
1218 for (to, from) in raw.iter_mut().zip(words) {
1219 *to = u32::from_le(from.load(Ordering::Relaxed));
1220 }
1221 let Some(value) = Transport::from_words(&raw) else {
1222 return false;
1223 };
1224 *context = value;
1225 load_packet(
1226 &words[CONTEXT_WORDS..CONTEXT_WORDS + PACKET_WORDS],
1227 self.geometry.expect("validated").max_frames,
1228 events,
1229 )
1230 }
1231 pub fn output_packet(&self, lane: usize, sequence: u32, events: &EventBuffer) -> bool {
1232 let Some(words) = self.metadata(lane, sequence) else {
1233 return false;
1234 };
1235 if events
1236 .as_slice()
1237 .iter()
1238 .any(|e| !e.valid(self.geometry.expect("validated").max_frames))
1239 {
1240 return false;
1241 }
1242 store_packet(
1243 &words[CONTEXT_WORDS + PACKET_WORDS..],
1244 events.as_slice(),
1245 events.dropped,
1246 );
1247 true
1248 }
1249
1250 pub fn complete(&self, lane: usize, sequence: u32) {
1253 let Some(geometry) = self.geometry else {
1254 return;
1255 };
1256 if lane >= geometry.lanes as usize {
1257 return;
1258 }
1259 self.region
1260 .store32(COMPLETED_I + lane, sequence.to_le(), Ordering::Release);
1261 if let Some(signal) = self.done[lane] {
1262 signal.signal();
1263 }
1264 }
1265
1266 pub fn beat(&self) -> u64 {
1267 self.region
1268 .words64
1269 .first()
1270 .map_or(0, |v| v.fetch_add(1, Ordering::Release).wrapping_add(1))
1271 }
1272
1273 pub fn set_last_error(&self, error: u64) {
1274 if let Some(word) = self.region.words64.get(CACHE_LINE / 8) {
1275 word.store(error, Ordering::Release);
1276 }
1277 }
1278}
1279
1280fn validate_header(region: Region<'_>) -> Result<Geometry, HeaderError> {
1281 if region.words32.len() < HEADER_WORDS || region.words64.len() < COUNTER_WORDS {
1282 return Err(HeaderError::TooShort);
1283 }
1284 if u32::from_le(region.load32(MAGIC_I, Ordering::Relaxed)) != MAGIC {
1285 return Err(HeaderError::Magic);
1286 }
1287 if u32::from_le(region.load32(VERSION_I, Ordering::Relaxed)) != LAYOUT_VERSION {
1288 return Err(HeaderError::Version);
1289 }
1290 if region.state() != Some(State::Announced) {
1291 return Err(HeaderError::State);
1292 }
1293 let geometry = Geometry {
1294 sample_format: match u32::from_le(region.load32(FORMAT_I, Ordering::Relaxed)) {
1295 0 => SampleFormat::F32,
1296 1 => SampleFormat::F64,
1297 _ => return Err(HeaderError::Version),
1298 },
1299 lanes: u32::from_le(region.load32(LANES_I, Ordering::Relaxed)),
1300 slots: u32::from_le(region.load32(SLOTS_I, Ordering::Relaxed)),
1301 input_channels: u32::from_le(region.load32(INPUTS_I, Ordering::Relaxed)),
1302 output_channels: u32::from_le(region.load32(OUTPUTS_I, Ordering::Relaxed)),
1303 max_frames: u32::from_le(region.load32(FRAMES_I, Ordering::Relaxed)),
1304 sample_rate: u32::from_le(region.load32(RATE_I, Ordering::Relaxed)),
1305 latency_bound_frames: u32::from_le(region.load32(LATENCY_I, Ordering::Relaxed)),
1306 }
1307 .validate()
1308 .map_err(HeaderError::Geometry)?;
1309 let expected = layout(geometry).expect("validated geometry has a layout");
1310 if u32::from_le(region.load32(TOTAL_I, Ordering::Relaxed)) as usize != expected.total_bytes
1311 || region.words32.len() * 4 < expected.prefix_bytes
1312 {
1313 return Err(HeaderError::TotalBytes);
1314 }
1315 for lane in 0..geometry.lanes as usize {
1316 let base = LANE_GEOMETRY_I + lane * 3;
1317 let actual = [
1318 u32::from_le(region.load32(base, Ordering::Relaxed)) as usize,
1319 u32::from_le(region.load32(base + 1, Ordering::Relaxed)) as usize,
1320 u32::from_le(region.load32(base + 2, Ordering::Relaxed)) as usize,
1321 ];
1322 if actual
1323 != [
1324 expected.first_slot_offsets[lane],
1325 expected.slot_bytes,
1326 expected.slots as usize,
1327 ]
1328 {
1329 return Err(HeaderError::LaneGeometry);
1330 }
1331 }
1332 Ok(geometry)
1333}
1334
1335pub fn sequence_at_least(value: u32, reference: u32) -> bool {
1340 value.wrapping_sub(reference) as i32 >= 0
1341}
1342
1343#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1344struct PipelineEntry {
1345 sequence: u32,
1346 app_block: u64,
1347 generation: u64,
1348 valid: bool,
1349}
1350
1351#[derive(Debug)]
1356pub struct Pipeline {
1357 entries: [PipelineEntry; MAX_SLOTS as usize],
1358 depth: u32,
1359}
1360
1361impl Default for Pipeline {
1362 fn default() -> Self {
1363 Self::with_depth(1)
1364 }
1365}
1366
1367impl Pipeline {
1368 pub fn new() -> Self {
1369 Self::default()
1370 }
1371
1372 pub fn with_depth(depth: u32) -> Self {
1373 assert!((1..=MAX_SLOTS).contains(&depth));
1374 Self {
1375 entries: array::from_fn(|_| PipelineEntry::default()),
1376 depth,
1377 }
1378 }
1379
1380 pub fn depth(&self) -> u32 {
1381 self.depth
1382 }
1383
1384 pub fn set_depth(&mut self, depth: u32) {
1385 assert!((1..=MAX_SLOTS).contains(&depth));
1386 self.depth = depth;
1387 self.clear();
1388 }
1389
1390 pub fn published(&mut self, sequence: u32, app_block: u64, generation: u64) {
1391 self.entries[sequence as usize % MAX_SLOTS as usize] = PipelineEntry {
1392 sequence,
1393 app_block,
1394 generation,
1395 valid: true,
1396 };
1397 }
1398
1399 pub fn output_sequence(
1402 &mut self,
1403 app_block: u64,
1404 generation: u64,
1405 completed: u32,
1406 ) -> Option<u32> {
1407 let wanted = app_block.checked_sub(u64::from(self.depth))?;
1408 let found = self.entries.iter_mut().find(|entry| {
1409 entry.valid && entry.app_block == wanted && entry.generation == generation
1410 })?;
1411 if sequence_at_least(completed, found.sequence) {
1412 found.valid = false;
1413 Some(found.sequence)
1414 } else {
1415 found.valid = false;
1417 None
1418 }
1419 }
1420
1421 pub fn clear(&mut self) {
1422 self.entries = array::from_fn(|_| PipelineEntry::default());
1423 }
1424}