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]) {
163 let right = target
164 .len()
165 .min(self.options.capacity - offset as usize % self.options.capacity);
166 ptr::copy_nonoverlapping(self.pointer(offset), target.as_mut_ptr(), right);
167 ptr::copy_nonoverlapping(
168 self.pointer(0),
169 target.as_mut_ptr().add(right),
170 target.len() - right,
171 );
172 }
173
174 unsafe fn clear(&self, offset: i64, length: usize) {
175 debug_assert!(length <= self.options.capacity);
176 let right = length.min(self.options.capacity - offset as usize % self.options.capacity);
177 ptr::write_bytes(self.pointer(offset), 0, right);
178 ptr::write_bytes(self.pointer(0), 0, length - right);
179 }
180
181 fn notify(&self) {
182 if self.header().notification.swap(1, SeqCst) == 0 {
183 let _ = self.post_notification();
186 }
187 }
188
189 #[inline]
190 fn post_notification(&self) -> Result<()> {
191 #[cfg(test)]
192 if self.fail_notification.load(Relaxed) {
193 return Err(std::io::Error::other("injected notification failure").into());
194 }
195 self.signal.post()
196 }
197
198 fn empty(&self) -> bool {
199 self.header().read.load(Acquire) == self.header().write.load(Acquire)
200 }
201}
202
203pub struct Publisher {
205 _lease: Lease,
206 shared: Shared,
207 id: i64,
208 slot: usize,
209}
210
211struct Active<'a>(&'a AtomicI32);
212impl Drop for Active<'_> {
213 fn drop(&mut self) {
214 self.0.fetch_sub(1, SeqCst);
215 }
216}
217
218impl Publisher {
219 pub fn open(options: &Options) -> Result<Self> {
225 let shared = Shared::open(options.clone())?;
226 let (id, lease) = shared.register()?;
227 let slot = shared.slot(id)?;
228 Ok(Self {
229 _lease: lease,
230 shared,
231 id,
232 slot,
233 })
234 }
235
236 pub fn try_send(&self, message: &[u8]) -> Result<()> {
238 let count = self.shared.active(self.slot);
239 count.fetch_add(1, SeqCst);
240 let _active = Active(count);
241 if self.shared.gate().load(Acquire) != 0 {
242 return Err(Error::Full);
243 }
244 self.send_admitted(message)
245 }
246
247 pub fn try_send_batch(&self, messages: &[&[u8]]) -> Result<usize> {
252 let count = self.shared.active(self.slot);
253 count.fetch_add(1, SeqCst);
254 let _active = Active(count);
255 if self.shared.gate().load(Acquire) != 0 {
256 return Ok(0);
257 }
258 let mut sent = 0;
259 for message in messages {
260 match self.send_admitted(message) {
261 Ok(()) => sent += 1,
262 Err(Error::Full) => break,
263 Err(_) if sent > 0 => break,
266 Err(error) => return Err(error),
267 }
268 }
269 Ok(sent)
270 }
271
272 fn send_admitted(&self, message: &[u8]) -> Result<()> {
273 if message.len() > i32::MAX as usize {
274 return Err(Error::Invalid("message exceeds the protocol length limit"));
275 }
276 let length = (message.len() + 15) & !7;
277 let Some(max_used) = self.shared.options.capacity.checked_sub(length) else {
278 return Err(Error::Full);
279 };
280 let header = self.shared.header();
281 loop {
282 let read = header.read.load(Acquire);
283 let write = header.write.load(Acquire);
284 let Some(used) = write.checked_sub(read) else {
285 return Err(Error::Corrupt);
286 };
287 if used < 0 || used > max_used as i64 {
288 return Err(Error::Full);
289 }
290 let next = write.checked_add(length as i64).ok_or(Error::Exhausted)?;
291 if header
292 .write
293 .compare_exchange(write, next, SeqCst, Acquire)
294 .is_err()
295 {
296 continue;
297 }
298 unsafe {
300 self.shared.write(write + 8, message);
301 self.shared
302 .pointer(write)
303 .add(4)
304 .cast::<i32>()
305 .write(message.len() as i32);
306 }
307 self.shared.state(write).store(2, Release);
308 self.shared.notify();
309 return Ok(());
310 }
311 }
312}
313
314impl Drop for Publisher {
315 fn drop(&mut self) {
316 if !self._lease.is_current_process() {
318 return;
319 }
320 let _ = self
322 .shared
323 .owner(self.slot)
324 .compare_exchange(self.id, 0, SeqCst, Acquire);
325 }
326}
327
328#[derive(Clone, Copy)]
329struct Pending {
330 started: u64,
331 read: i64,
332 tail: i64,
333}
334
335pub struct Subscriber {
337 _lease: Lease,
338 shared: Shared,
339 id: i64,
340 pending: UnsafeCell<Option<Pending>>,
341 next_check: AtomicU64,
342}
343
344unsafe impl Sync for Subscriber {}
347
348struct ReadGuard<'a> {
349 owner: &'a AtomicI64,
350 id: i64,
351}
352impl Drop for ReadGuard<'_> {
353 fn drop(&mut self) {
354 let _ = self.owner.compare_exchange(self.id, 0, SeqCst, Acquire);
355 }
356}
357struct GateGuard<'a>(&'a AtomicI32);
358impl Drop for GateGuard<'_> {
359 fn drop(&mut self) {
360 self.0.swap(0, SeqCst);
361 }
362}
363
364impl Subscriber {
365 pub fn open(options: &Options) -> Result<Self> {
371 let shared = Shared::open(options.clone())?;
372 let (id, lease) = shared.register()?;
373 Ok(Self {
374 _lease: lease,
375 shared,
376 id,
377 pending: UnsafeCell::new(None),
378 next_check: AtomicU64::new(now_ms() + RECOVERY_MS),
379 })
380 }
381
382 pub fn try_recv(&self) -> Result<Option<Vec<u8>>> {
384 self.receive_with(|shared, offset, length| {
385 let mut message = vec![0; length];
386 unsafe {
387 shared.read(offset, &mut message);
388 }
389 message
390 })
391 }
392
393 pub fn try_recv_into(&self, buffer: &mut [u8]) -> Result<Option<usize>> {
396 self.receive_with(|shared, offset, length| {
397 let length = length.min(buffer.len());
398 unsafe {
399 shared.read(offset, &mut buffer[..length]);
400 }
401 length
402 })
403 }
404
405 pub fn recv(&self) -> Result<Vec<u8>> {
407 self.receive_wait(None)
409 .map(|message| message.expect("unbounded wait timed out"))
410 }
411
412 pub fn recv_timeout(&self, timeout: Duration) -> Result<Option<Vec<u8>>> {
415 self.receive_wait(Some(timeout))
416 }
417
418 fn receive_wait(&self, timeout: Option<Duration>) -> Result<Option<Vec<u8>>> {
419 let started = Instant::now();
420 let mut relay = false;
421 let result = (|| loop {
422 if let Some(message) = self.try_recv()? {
423 return Ok(Some(message));
424 }
425 let wait = match timeout {
426 Some(limit) => {
427 let elapsed = started.elapsed();
428 if elapsed >= limit {
429 return Ok(None);
430 }
431 (limit - elapsed).min(Duration::from_millis(5))
432 }
433 None => Duration::from_millis(5),
434 };
435 if self.shared.signal.wait(wait)? {
436 self.shared.header().notification.swap(0, SeqCst);
437 relay = true;
438 }
439 })();
440 if relay && !self.shared.empty() {
441 self.shared.notify();
442 }
443 result
444 }
445
446 fn receive_with<T>(&self, copy: impl FnOnce(&Shared, i64, usize) -> T) -> Result<Option<T>> {
447 let shared = &self.shared;
448 let header = shared.header();
449 let owner = header.reader.load(Acquire);
450 if owner != 0 {
451 self.recover_reader(owner);
452 return Ok(None);
453 }
454 if shared.empty()
457 || header
458 .reader
459 .compare_exchange(0, self.id, SeqCst, Acquire)
460 .is_err()
461 {
462 return Ok(None);
463 }
464 let _read_guard = ReadGuard {
465 owner: &header.reader,
466 id: self.id,
467 };
468 let read = header.read.load(Acquire);
469 let write = header.write.load(Acquire);
470 if read == write {
471 return Ok(None);
472 }
473 if read < 0 {
474 return Err(Error::Corrupt);
475 }
476 let pending = unsafe { &mut *self.pending.get() };
477 if shared
478 .state(read)
479 .compare_exchange(2, 1, SeqCst, Acquire)
480 .is_err()
481 {
482 let now = now_ms();
483 let previous = match *pending {
484 Some(p) if p.read == read => p,
485 _ => {
486 *pending = Some(Pending {
487 started: now,
488 read,
489 tail: header.write.load(Acquire),
490 });
491 return Ok(None);
492 }
493 };
494 if now.saturating_sub(previous.started) >= RECOVERY_MS {
495 shared.gate().swap(1, SeqCst);
496 let _gate_guard = GateGuard(shared.gate());
497 if shared.any_active() {
498 *pending = Some(Pending {
499 started: now,
500 ..previous
501 });
502 return Ok(None);
503 }
504 let length = previous.tail.checked_sub(read).ok_or(Error::Corrupt)?;
505 if length < 0 || length as usize > shared.options.capacity {
506 return Err(Error::Corrupt);
507 }
508 unsafe {
509 shared.clear(read, length as usize);
510 }
511 header.read.swap(previous.tail, SeqCst);
512 *pending = None;
513 }
514 return Ok(None);
515 }
516 *pending = None;
517 let body = unsafe { shared.pointer(read).add(4).cast::<i32>().read() };
518 if body < 0 || body as usize > shared.options.capacity - 8 {
519 shared.state(read).store(2, Release);
520 return Err(Error::Corrupt);
521 }
522 let length = (body as usize + 15) & !7;
523 let Some(next) = read
524 .checked_add(length as i64)
525 .filter(|next| *next <= write)
526 else {
527 shared.state(read).store(2, Release);
528 return Err(Error::Corrupt);
529 };
530 let result = copy(shared, read + 8, body as usize);
531 unsafe {
532 shared.clear(read, length);
533 }
534 header.read.swap(next, SeqCst);
535 Ok(Some(result))
536 }
537
538 fn recover_reader(&self, owner: i64) {
539 if owner == self.id {
540 return;
541 }
542 let next = self.next_check.load(Acquire);
543 let now = now_ms();
544 if now < next
545 || self
546 .next_check
547 .compare_exchange(next, now + RECOVERY_MS, SeqCst, Acquire)
548 .is_err()
549 {
550 return;
551 }
552 let header = self.shared.header();
553 if !Lease::alive(&self.shared.options, owner)
554 && header
555 .reader
556 .compare_exchange(owner, self.id, SeqCst, Acquire)
557 .is_ok()
558 {
559 let _guard = ReadGuard {
560 owner: &header.reader,
561 id: self.id,
562 };
563 self.shared.gate().swap(0, SeqCst);
564 }
565 }
566}
567
568impl std::fmt::Debug for Publisher {
569 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570 f.debug_struct("Publisher")
571 .field("options", &self.shared.options)
572 .field("id", &self.id)
573 .finish_non_exhaustive()
574 }
575}
576impl std::fmt::Debug for Subscriber {
577 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
578 f.debug_struct("Subscriber")
579 .field("options", &self.shared.options)
580 .field("id", &self.id)
581 .finish_non_exhaustive()
582 }
583}
584
585#[cfg(test)]
586mod tests;