1use crate::{
2 platform::{Lease, Mapping, Signal},
3 Error, Options, Result,
4};
5use std::{
6 cell::UnsafeCell,
7 ptr,
8 sync::{
9 atomic::{AtomicI32, AtomicI64, AtomicU64, Ordering::*},
10 OnceLock,
11 },
12 time::{Duration, Instant},
13};
14
15pub const MAX_PUBLISHERS: usize = 2048;
17const SLOT_SIZE: usize = 128;
18const TABLE_OFFSET: usize = 256;
19pub(crate) const BUFFER_OFFSET: usize = TABLE_OFFSET + MAX_PUBLISHERS * SLOT_SIZE;
20const RECOVERY_MS: u64 = 10_000;
21
22fn now_ms() -> u64 {
23 static START: OnceLock<Instant> = OnceLock::new();
24 START.get_or_init(Instant::now).elapsed().as_millis() as u64
25}
26
27#[repr(C)]
28struct Header {
29 read: AtomicI64,
30 write: AtomicI64,
31 reader: AtomicI64,
32 notification: AtomicI32,
33 participant: AtomicI32,
34}
35
36struct Shared {
37 signal: Signal,
39 mapping: Mapping,
40 options: Options,
41 #[cfg(test)]
42 fail_notification: std::sync::atomic::AtomicBool,
43}
44
45impl Shared {
46 fn open(options: Options) -> Result<Self> {
47 options.validate()?;
48 let mapping = Mapping::open(&options)?;
49 let signal = Signal::open(&options.name)?;
50 Ok(Self {
51 signal,
52 mapping,
53 options,
54 #[cfg(test)]
55 fail_notification: std::sync::atomic::AtomicBool::new(false),
56 })
57 }
58
59 #[inline]
60 fn header(&self) -> &Header {
61 unsafe { &*self.mapping.ptr.as_ptr().cast() }
62 }
63 #[inline]
64 fn gate(&self) -> &AtomicI32 {
65 unsafe { &*self.mapping.ptr.as_ptr().add(128).cast() }
66 }
67 #[inline]
68 fn owner(&self, slot: usize) -> &AtomicI64 {
69 unsafe {
70 &*self
71 .mapping
72 .ptr
73 .as_ptr()
74 .add(TABLE_OFFSET + slot * SLOT_SIZE)
75 .cast()
76 }
77 }
78 #[inline]
79 fn active(&self, slot: usize) -> &AtomicI32 {
80 unsafe {
81 &*self
82 .mapping
83 .ptr
84 .as_ptr()
85 .add(TABLE_OFFSET + slot * SLOT_SIZE + 8)
86 .cast()
87 }
88 }
89
90 fn register(&self) -> Result<(i64, Lease)> {
91 let counter = &self.header().participant;
92 let id = loop {
93 let previous = counter.load(Acquire);
94 let next = previous.checked_add(1).ok_or(Error::Exhausted)?;
95 if counter
96 .compare_exchange(previous, next, SeqCst, Acquire)
97 .is_ok()
98 {
99 break next as i64;
100 }
101 };
102 Ok((id, Lease::new(&self.options, id)?))
103 }
104
105 fn slot(&self, id: i64) -> Result<usize> {
106 for pass in 0..2 {
107 for slot in 0..MAX_PUBLISHERS {
108 let owner = self.owner(slot).load(Acquire);
109 if owner != 0 && (pass == 0 || Lease::alive(&self.options, owner)) {
110 continue;
111 }
112 if self
113 .owner(slot)
114 .compare_exchange(owner, id, SeqCst, Acquire)
115 .is_ok()
116 {
117 self.active(slot).swap(0, SeqCst);
118 return Ok(slot);
119 }
120 }
121 }
122 Err(Error::PublisherLimit)
123 }
124
125 fn any_active(&self) -> bool {
126 (0..MAX_PUBLISHERS).any(|slot| {
129 let owner = self.owner(slot).load(Acquire);
130 owner != 0 && self.active(slot).load(Acquire) != 0 && Lease::alive(&self.options, owner)
131 })
132 }
133
134 #[inline]
135 fn pointer(&self, offset: i64) -> *mut u8 {
136 debug_assert!(offset >= 0);
137 unsafe {
138 self.mapping
139 .ptr
140 .as_ptr()
141 .add(BUFFER_OFFSET + offset as usize % self.options.capacity)
142 }
143 }
144
145 #[inline]
146 fn state(&self, offset: i64) -> &AtomicI32 {
147 unsafe { &*self.pointer(offset).cast() }
148 }
149
150 unsafe fn write(&self, offset: i64, source: &[u8]) {
151 let right = source
152 .len()
153 .min(self.options.capacity - offset as usize % self.options.capacity);
154 ptr::copy_nonoverlapping(source.as_ptr(), self.pointer(offset), right);
155 ptr::copy_nonoverlapping(
156 source.as_ptr().add(right),
157 self.pointer(0),
158 source.len() - right,
159 );
160 }
161
162 unsafe fn read(&self, offset: i64, target: *mut u8, length: usize) {
164 let right = length.min(self.options.capacity - offset as usize % self.options.capacity);
165 ptr::copy_nonoverlapping(self.pointer(offset), target, right);
166 ptr::copy_nonoverlapping(self.pointer(0), target.add(right), length - right);
167 }
168
169 unsafe fn clear(&self, offset: i64, length: usize) {
170 debug_assert!(length <= self.options.capacity);
171 let right = length.min(self.options.capacity - offset as usize % self.options.capacity);
172 ptr::write_bytes(self.pointer(offset), 0, right);
173 ptr::write_bytes(self.pointer(0), 0, length - right);
174 }
175
176 fn notify(&self) {
177 if self.header().notification.swap(1, SeqCst) == 0 {
178 let _ = self.post_notification();
181 }
182 }
183
184 #[inline]
185 fn post_notification(&self) -> Result<()> {
186 #[cfg(test)]
187 if self.fail_notification.load(Relaxed) {
188 return Err(std::io::Error::other("injected notification failure").into());
189 }
190 self.signal.post()
191 }
192
193 fn empty(&self) -> bool {
194 self.header().read.load(Acquire) == self.header().write.load(Acquire)
195 }
196}
197
198pub struct Publisher {
200 _lease: Lease,
201 shared: Shared,
202 id: i64,
203 slot: usize,
204}
205
206struct Active<'a>(&'a AtomicI32);
207impl Drop for Active<'_> {
208 fn drop(&mut self) {
209 self.0.fetch_sub(1, SeqCst);
210 }
211}
212
213impl Publisher {
214 pub fn open(options: &Options) -> Result<Self> {
220 let shared = Shared::open(options.clone())?;
221 let (id, lease) = shared.register()?;
222 let slot = shared.slot(id)?;
223 Ok(Self {
224 _lease: lease,
225 shared,
226 id,
227 slot,
228 })
229 }
230
231 pub fn try_send(&self, message: &[u8]) -> Result<()> {
233 let count = self.shared.active(self.slot);
234 count.fetch_add(1, SeqCst);
235 let _active = Active(count);
236 if self.shared.gate().load(Acquire) != 0 {
237 return Err(Error::Full);
238 }
239 self.send_admitted(message)
240 }
241
242 pub fn try_send_batch(&self, messages: &[&[u8]]) -> Result<usize> {
247 let count = self.shared.active(self.slot);
248 count.fetch_add(1, SeqCst);
249 let _active = Active(count);
250 if self.shared.gate().load(Acquire) != 0 {
251 return Ok(0);
252 }
253 let mut sent = 0;
254 for message in messages {
255 match self.send_admitted(message) {
256 Ok(()) => sent += 1,
257 Err(Error::Full) => break,
258 Err(_) if sent > 0 => break,
261 Err(error) => return Err(error),
262 }
263 }
264 Ok(sent)
265 }
266
267 fn send_admitted(&self, message: &[u8]) -> Result<()> {
268 if message.len() > i32::MAX as usize {
269 return Err(Error::Invalid("message exceeds the protocol length limit"));
270 }
271 let length = (message.len() + 15) & !7;
272 let Some(max_used) = self.shared.options.capacity.checked_sub(length) else {
273 return Err(Error::Full);
274 };
275 let header = self.shared.header();
276 loop {
277 let read = header.read.load(Acquire);
278 let write = header.write.load(Acquire);
279 let Some(used) = write.checked_sub(read) else {
280 return Err(Error::Corrupt);
281 };
282 if used < 0 || used > max_used as i64 {
283 return Err(Error::Full);
284 }
285 let next = write.checked_add(length as i64).ok_or(Error::Exhausted)?;
286 if header
287 .write
288 .compare_exchange(write, next, SeqCst, Acquire)
289 .is_err()
290 {
291 continue;
292 }
293 unsafe {
295 self.shared.write(write + 8, message);
296 self.shared
297 .pointer(write)
298 .add(4)
299 .cast::<i32>()
300 .write(message.len() as i32);
301 }
302 self.shared.state(write).store(2, Release);
303 self.shared.notify();
304 return Ok(());
305 }
306 }
307}
308
309impl Drop for Publisher {
310 fn drop(&mut self) {
311 if !self._lease.is_current_process() {
313 return;
314 }
315 let _ = self
317 .shared
318 .owner(self.slot)
319 .compare_exchange(self.id, 0, SeqCst, Acquire);
320 }
321}
322
323#[derive(Clone, Copy)]
324struct Pending {
325 started: u64,
326 read: i64,
327 tail: i64,
328}
329
330pub struct Subscriber {
332 _lease: Lease,
333 shared: Shared,
334 id: i64,
335 pending: UnsafeCell<Option<Pending>>,
336 next_check: AtomicU64,
337}
338
339unsafe impl Sync for Subscriber {}
342
343struct ReadGuard<'a> {
344 owner: &'a AtomicI64,
345 id: i64,
346}
347impl Drop for ReadGuard<'_> {
348 fn drop(&mut self) {
349 let _ = self.owner.compare_exchange(self.id, 0, SeqCst, Acquire);
350 }
351}
352struct GateGuard<'a>(&'a AtomicI32);
353impl Drop for GateGuard<'_> {
354 fn drop(&mut self) {
355 self.0.swap(0, SeqCst);
356 }
357}
358
359impl Subscriber {
360 pub fn open(options: &Options) -> Result<Self> {
366 let shared = Shared::open(options.clone())?;
367 let (id, lease) = shared.register()?;
368 Ok(Self {
369 _lease: lease,
370 shared,
371 id,
372 pending: UnsafeCell::new(None),
373 next_check: AtomicU64::new(now_ms() + RECOVERY_MS),
374 })
375 }
376
377 pub fn try_recv(&self) -> Result<Option<Vec<u8>>> {
379 self.receive_with(|shared, offset, length| {
380 let mut message = Vec::with_capacity(length);
381 unsafe {
382 shared.read(offset, message.as_mut_ptr(), length);
385 message.set_len(length);
386 }
387 message
388 })
389 }
390
391 pub fn try_recv_into(&self, buffer: &mut [u8]) -> Result<Option<usize>> {
394 self.receive_with(|shared, offset, length| {
395 let length = length.min(buffer.len());
396 unsafe {
397 shared.read(offset, buffer.as_mut_ptr(), length);
398 }
399 length
400 })
401 }
402
403 pub fn recv(&self) -> Result<Vec<u8>> {
405 self.receive_wait(None)
407 .map(|message| message.expect("unbounded wait timed out"))
408 }
409
410 pub fn recv_timeout(&self, timeout: Duration) -> Result<Option<Vec<u8>>> {
413 self.receive_wait(Some(timeout))
414 }
415
416 fn receive_wait(&self, timeout: Option<Duration>) -> Result<Option<Vec<u8>>> {
417 let started = Instant::now();
418 let mut relay = false;
419 let result = (|| loop {
420 if let Some(message) = self.try_recv()? {
421 return Ok(Some(message));
422 }
423 let wait = match timeout {
424 Some(limit) => {
425 let elapsed = started.elapsed();
426 if elapsed >= limit {
427 return Ok(None);
428 }
429 (limit - elapsed).min(Duration::from_millis(5))
430 }
431 None => Duration::from_millis(5),
432 };
433 if self.shared.signal.wait(wait)? {
434 self.shared.header().notification.swap(0, SeqCst);
435 relay = true;
436 }
437 })();
438 if relay && !self.shared.empty() {
439 self.shared.notify();
440 }
441 result
442 }
443
444 fn receive_with<T>(&self, copy: impl FnOnce(&Shared, i64, usize) -> T) -> Result<Option<T>> {
445 let shared = &self.shared;
446 let header = shared.header();
447 let owner = header.reader.load(Acquire);
448 if owner != 0 {
449 self.recover_reader(owner);
450 return Ok(None);
451 }
452 if shared.empty()
455 || header
456 .reader
457 .compare_exchange(0, self.id, SeqCst, Acquire)
458 .is_err()
459 {
460 return Ok(None);
461 }
462 let _read_guard = ReadGuard {
463 owner: &header.reader,
464 id: self.id,
465 };
466 let read = header.read.load(Acquire);
467 let write = header.write.load(Acquire);
468 if read == write {
469 return Ok(None);
470 }
471 if read < 0 {
472 return Err(Error::Corrupt);
473 }
474 let pending = unsafe { &mut *self.pending.get() };
475 if shared
476 .state(read)
477 .compare_exchange(2, 1, SeqCst, Acquire)
478 .is_err()
479 {
480 let now = now_ms();
481 let previous = match *pending {
482 Some(p) if p.read == read => p,
483 _ => {
484 *pending = Some(Pending {
485 started: now,
486 read,
487 tail: header.write.load(Acquire),
488 });
489 return Ok(None);
490 }
491 };
492 if now.saturating_sub(previous.started) >= RECOVERY_MS {
493 shared.gate().swap(1, SeqCst);
494 let _gate_guard = GateGuard(shared.gate());
495 if shared.any_active() {
496 *pending = Some(Pending {
497 started: now,
498 ..previous
499 });
500 return Ok(None);
501 }
502 let length = previous.tail.checked_sub(read).ok_or(Error::Corrupt)?;
503 if length < 0 || length as usize > shared.options.capacity {
504 return Err(Error::Corrupt);
505 }
506 unsafe {
507 shared.clear(read, length as usize);
508 }
509 header.read.swap(previous.tail, SeqCst);
510 *pending = None;
511 }
512 return Ok(None);
513 }
514 *pending = None;
515 let body = unsafe { shared.pointer(read).add(4).cast::<i32>().read() };
516 if body < 0 || body as usize > shared.options.capacity - 8 {
517 shared.state(read).store(2, Release);
518 return Err(Error::Corrupt);
519 }
520 let length = (body as usize + 15) & !7;
521 let Some(next) = read
522 .checked_add(length as i64)
523 .filter(|next| *next <= write)
524 else {
525 shared.state(read).store(2, Release);
526 return Err(Error::Corrupt);
527 };
528 let result = copy(shared, read + 8, body as usize);
529 unsafe {
530 shared.clear(read, length);
531 }
532 header.read.swap(next, SeqCst);
533 Ok(Some(result))
534 }
535
536 fn recover_reader(&self, owner: i64) {
537 if owner == self.id {
538 return;
539 }
540 let next = self.next_check.load(Acquire);
541 let now = now_ms();
542 if now < next
543 || self
544 .next_check
545 .compare_exchange(next, now + RECOVERY_MS, SeqCst, Acquire)
546 .is_err()
547 {
548 return;
549 }
550 let header = self.shared.header();
551 if !Lease::alive(&self.shared.options, owner)
552 && header
553 .reader
554 .compare_exchange(owner, self.id, SeqCst, Acquire)
555 .is_ok()
556 {
557 let _guard = ReadGuard {
558 owner: &header.reader,
559 id: self.id,
560 };
561 self.shared.gate().swap(0, SeqCst);
562 }
563 }
564}
565
566impl std::fmt::Debug for Publisher {
567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568 f.debug_struct("Publisher")
569 .field("options", &self.shared.options)
570 .field("id", &self.id)
571 .finish_non_exhaustive()
572 }
573}
574impl std::fmt::Debug for Subscriber {
575 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576 f.debug_struct("Subscriber")
577 .field("options", &self.shared.options)
578 .field("id", &self.id)
579 .finish_non_exhaustive()
580 }
581}
582
583#[cfg(test)]
584mod tests;