1#![allow(unsafe_code)]
5
6use std::ptr;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
9
10const MOST_SLOTS: u32 = 0x8000_0000;
12
13struct Ring<T> {
15 slots: Box<[AtomicPtr<T>]>,
16 mask: u32,
18 head: AtomicU32,
20 tail: AtomicU32,
22}
23
24impl<T> Ring<T> {
25 fn slot(&self, at: u32) -> &AtomicPtr<T> {
26 &self.slots[usize::try_from(at & self.mask).unwrap_or(0)]
27 }
28}
29
30impl<T> Drop for Ring<T> {
31 fn drop(&mut self) {
32 for slot in &mut *self.slots {
33 let held = *slot.get_mut();
34 if !held.is_null() {
35 drop(unsafe { Box::from_raw(held) });
39 }
40 }
41 }
42}
43
44pub fn handover<T: Send>(slots: usize) -> (HandTo<T>, HandOut<T>) {
49 let slots = u32::try_from(slots)
50 .unwrap_or(MOST_SLOTS)
51 .clamp(1, MOST_SLOTS)
52 .next_power_of_two();
53 let ring = Arc::new(Ring {
54 slots: (0..slots)
55 .map(|_| AtomicPtr::new(ptr::null_mut()))
56 .collect(),
57 mask: slots - 1,
58 head: AtomicU32::new(0),
59 tail: AtomicU32::new(0),
60 });
61 (
62 HandTo {
63 ring: Arc::clone(&ring),
64 },
65 HandOut { ring },
66 )
67}
68
69pub struct HandTo<T> {
71 ring: Arc<Ring<T>>,
72}
73
74impl<T> HandTo<T> {
75 pub fn send(&mut self, value: Box<T>) -> Result<(), Box<T>> {
77 let ring = &*self.ring;
78 let head = ring.head.load(Ordering::Relaxed);
79 let tail = ring.tail.load(Ordering::Acquire);
80 if head.wrapping_sub(tail) > ring.mask {
81 return Err(value);
82 }
83 ring.slot(head)
84 .store(Box::into_raw(value), Ordering::Release);
85 ring.head.store(head.wrapping_add(1), Ordering::Release);
86 Ok(())
87 }
88}
89
90pub struct HandOut<T> {
92 ring: Arc<Ring<T>>,
93}
94
95impl<T> HandOut<T> {
96 pub fn take(&mut self) -> Option<Box<T>> {
98 let ring = &*self.ring;
99 let tail = ring.tail.load(Ordering::Relaxed);
100 if ring.head.load(Ordering::Acquire) == tail {
101 return None;
102 }
103 let held = ring.slot(tail).swap(ptr::null_mut(), Ordering::AcqRel);
104 ring.tail.store(tail.wrapping_add(1), Ordering::Release);
105 if held.is_null() {
106 return None;
107 }
108 Some(unsafe { Box::from_raw(held) })
112 }
113}
114
115unsafe impl<T: Send> Send for HandTo<T> {}
119unsafe impl<T: Send> Send for HandOut<T> {}
121
122#[cfg(target_arch = "wasm32")]
125pub(crate) mod web {
126 const PLUGIN_VERSION: u32 = 1;
130
131 unsafe extern "C" {
132 pub(crate) safe fn shuyi_sound_open(rate: u32, frames: u32, discard: u32) -> u32;
135 pub(crate) safe fn shuyi_sound_hold(on: u32);
137 safe fn shuyi_sound_vet(pointer: *const u8, bytes: u32) -> u32;
140 pub(crate) safe fn shuyi_sound_vetted(number: u32) -> u32;
142 }
143
144 pub(crate) fn vet(bytes: &[u8]) -> u32 {
146 shuyi_sound_vet(bytes.as_ptr(), u32::try_from(bytes.len()).unwrap_or(0))
147 }
148
149 #[unsafe(no_mangle)]
151 pub extern "C" fn shuyi_sound_crate_version() -> u32 {
152 PLUGIN_VERSION
153 }
154
155 #[unsafe(no_mangle)]
157 pub extern "C" fn shuyi_sound_fill(frames: u32) -> *const i16 {
158 crate::web::fill(frames)
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use std::sync::atomic::AtomicUsize;
165
166 use super::*;
167
168 struct Tracked(Arc<AtomicUsize>);
170
171 impl Drop for Tracked {
172 fn drop(&mut self) {
173 self.0.fetch_add(1, Ordering::AcqRel);
174 }
175 }
176
177 #[test]
178 fn values_come_out_in_order_and_a_full_ring_gives_one_back() {
179 let (mut to, mut out) = handover::<u32>(3);
180 for value in 0..4 {
181 assert!(to.send(Box::new(value)).is_ok());
182 }
183 assert_eq!(to.send(Box::new(9)).map_err(|back| *back), Err(9));
184 for value in 0..4 {
185 assert_eq!(out.take().map(|got| *got), Some(value));
186 }
187 assert!(out.take().is_none());
188 assert!(to.send(Box::new(5)).is_ok());
189 assert_eq!(out.take().map(|got| *got), Some(5));
190 }
191
192 #[test]
193 fn counters_wrap_past_their_width() {
194 let (mut to, mut out) = handover::<u32>(4);
195 to.ring.head.store(u32::MAX - 1, Ordering::Relaxed);
196 to.ring.tail.store(u32::MAX - 1, Ordering::Relaxed);
197 for value in 0..32 {
198 assert!(to.send(Box::new(value)).is_ok());
199 assert!(to.send(Box::new(value + 100)).is_ok());
200 assert_eq!(out.take().map(|got| *got), Some(value));
201 assert_eq!(out.take().map(|got| *got), Some(value + 100));
202 assert!(out.take().is_none());
203 }
204 }
205
206 #[test]
207 fn what_was_never_taken_is_freed_with_the_ring_and_only_once() {
208 let freed = Arc::new(AtomicUsize::new(0));
209 let (mut to, mut out) = handover(4);
210 for _ in 0..3 {
211 assert!(to.send(Box::new(Tracked(Arc::clone(&freed)))).is_ok());
212 }
213 drop(out.take());
214 assert_eq!(freed.load(Ordering::Acquire), 1);
215 drop(to);
216 assert_eq!(
217 freed.load(Ordering::Acquire),
218 1,
219 "the other end still holds"
220 );
221 drop(out);
222 assert_eq!(freed.load(Ordering::Acquire), 3);
223 }
224
225 #[test]
226 fn nothing_is_lost_or_doubled_between_real_threads() {
227 const COUNT: u32 = 200_000;
228 let (mut to, mut out) = handover::<u32>(8);
229 let writer = std::thread::spawn(move || {
230 for value in 0..COUNT {
231 let mut held = Box::new(value);
232 loop {
233 match to.send(held) {
234 Ok(()) => break,
235 Err(back) => {
236 held = back;
237 std::thread::yield_now();
238 }
239 }
240 }
241 }
242 });
243 let mut next = 0;
244 while next < COUNT {
245 match out.take() {
246 Some(value) => {
247 assert_eq!(*value, next);
248 next += 1;
249 }
250 None => std::thread::yield_now(),
251 }
252 }
253 assert!(writer.join().is_ok());
254 assert!(out.take().is_none());
255 }
256}