1use std::str::FromStr;
37use std::sync::atomic::{AtomicU32, Ordering};
38use std::sync::{Arc, Mutex, MutexGuard};
39use std::time::{Duration, Instant};
40use std::{fmt, io};
41
42use bytes::{Bytes, BytesMut};
43use orbit_core::{Fleet, NetId64};
44
45#[cfg(unix)]
46mod bell;
47pub mod exchange;
48mod layout;
49#[cfg(feature = "tokio")]
50mod poll;
51mod table;
52mod wake;
53
54use layout::{
55 Direction, FLAG_FIN, FLAG_READER_GONE, FLAG_RESET, GENERATION_MASK,
56 SIDE_CLAIMED, SIDE_DEAD, SIDE_FREE, SIDE_RELEASED, SLOT_BITS, SLOT_EMPTY,
57 SLOT_LIVE, SLOT_MASK, Slot,
58};
59use orbit_core::NodeId;
60#[cfg(any(
61 target_os = "linux",
62 target_os = "freebsd",
63 target_os = "macos"
64))]
65pub use orbit_core::readiness::Readiness;
66use table::Table;
67pub use table::{segment_size, segment_size_for};
68use wake::Interest;
69
70pub const STREAM_KIND: u8 = 246;
73pub const STREAM_LANE_CAPACITY: usize = orbit_core::compile::usize_from_env(
81 option_env!("ORBIT_STREAM_LANE_CAPACITY"),
82 256,
83);
84pub const STREAM_BUFFER_BYTES: usize = orbit_core::compile::usize_from_env(
91 option_env!("ORBIT_STREAM_BUFFER_BYTES"),
92 64 * 1024,
93);
94
95#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
107pub struct StreamSpec {
108 pub kind: u8,
110 pub lane_capacity: usize,
113 pub buffer_bytes: usize,
115}
116
117impl StreamSpec {
118 pub const DEFAULT: Self =
119 Self::new(STREAM_KIND, STREAM_LANE_CAPACITY, STREAM_BUFFER_BYTES);
120
121 pub const fn new(
122 kind: u8,
123 lane_capacity: usize,
124 buffer_bytes: usize,
125 ) -> Self {
126 Self { kind, lane_capacity, buffer_bytes }
127 }
128
129 pub const fn lane_bytes(&self) -> usize {
133 self.lane_capacity * 2 * self.buffer_bytes
134 }
135
136 fn validate(self) -> Result<()> {
139 if self.lane_capacity == 0
140 || self.buffer_bytes == 0
141 || !self.lane_capacity.is_power_of_two()
142 || !self.buffer_bytes.is_power_of_two()
143 || self.lane_capacity > 1 << SLOT_BITS
144 || self.buffer_bytes > u32::MAX as usize
145 {
146 return Err(Error::Malformed(format!(
147 "stream spec kind={} lane_capacity={} buffer_bytes={}: both are powers of two, a lane holds at most {} streams and a ring at most {} bytes",
148 self.kind,
149 self.lane_capacity,
150 self.buffer_bytes,
151 1_usize << SLOT_BITS,
152 u32::MAX
153 )));
154 }
155 Ok(())
156 }
157}
158
159impl Default for StreamSpec {
160 fn default() -> Self {
161 Self::DEFAULT
162 }
163}
164
165pub type Result<T, E = Error> = std::result::Result<T, E>;
166
167#[non_exhaustive]
171#[derive(Debug)]
172pub enum Error {
173 Stale(StreamId),
175 AlreadyClaimed(Ticket),
177 Full {
179 capacity: usize,
180 },
181 PayloadFull {
183 requested_slots: usize,
184 },
185 PayloadTooLarge {
187 len: usize,
188 capacity: usize,
189 },
190 Malformed(String),
192 Closed,
194 Reset,
196 PeerGone,
198 WouldBlock,
200 Io(io::Error),
201}
202
203impl fmt::Display for Error {
204 fn fmt(
205 &self,
206 f: &mut fmt::Formatter<'_>,
207 ) -> fmt::Result {
208 match self {
209 Self::Stale(id) => write!(f, "stream {id} has ended"),
210 Self::AlreadyClaimed(ticket) => {
211 write!(f, "stream side {ticket} is already held")
212 }
213 Self::Full { capacity } => {
214 write!(f, "stream lane is full: capacity={capacity}")
215 }
216 Self::PayloadFull { requested_slots } => {
217 write!(
218 f,
219 "payload arena has no run of {requested_slots} free slots"
220 )
221 }
222 Self::PayloadTooLarge { len, capacity } => {
223 write!(
224 f,
225 "payload chunk is {len} bytes; arena lane capacity is {capacity}"
226 )
227 }
228 Self::Malformed(text) => {
229 write!(f, "not a stream address: {text:?}")
230 }
231 Self::Closed => f.write_str("stream write half is shut down"),
232 Self::Reset => {
233 f.write_str("stream direction was reset by the peer")
234 }
235 Self::PeerGone => f.write_str("stream peer reader is gone"),
236 Self::WouldBlock => f.write_str("stream operation would block"),
237 Self::Io(error) => write!(f, "Orbit stream io error: {error}"),
238 }
239 }
240}
241
242impl std::error::Error for Error {
243 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
244 match self {
245 Self::Io(error) => Some(error),
246 _ => None,
247 }
248 }
249}
250
251impl From<io::Error> for Error {
252 fn from(value: io::Error) -> Self {
253 Self::Io(value)
254 }
255}
256
257impl From<Error> for io::Error {
258 fn from(value: Error) -> Self {
259 let kind = match &value {
260 Error::Stale(_) | Error::AlreadyClaimed(_) => {
261 io::ErrorKind::NotConnected
262 }
263 Error::Full { .. }
264 | Error::PayloadFull { .. }
265 | Error::PayloadTooLarge { .. } => io::ErrorKind::OutOfMemory,
266 Error::Malformed(_) => io::ErrorKind::InvalidInput,
267 Error::Closed => io::ErrorKind::BrokenPipe,
268 Error::Reset => io::ErrorKind::ConnectionReset,
269 Error::PeerGone => io::ErrorKind::BrokenPipe,
270 Error::WouldBlock => io::ErrorKind::WouldBlock,
271 Error::Io(error) => error.kind(),
272 };
273 io::Error::new(kind, value)
274 }
275}
276
277#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
280pub struct StreamId(NetId64);
281
282impl StreamId {
283 pub const fn from_net_id(id: NetId64) -> Self {
284 Self(id)
285 }
286
287 pub const fn net_id(self) -> NetId64 {
288 self.0
289 }
290
291 pub const fn kind(self) -> u8 {
292 self.0.kind()
293 }
294
295 pub const fn node(self) -> u16 {
297 self.0.node()
298 }
299
300 pub const fn slot(self) -> u32 {
301 (self.0.counter() & SLOT_MASK) as u32
302 }
303
304 pub const fn generation(self) -> u32 {
305 (self.0.counter() >> SLOT_BITS) as u32
306 }
307
308 fn make(
309 kind: u8,
310 node: u16,
311 slot: u32,
312 generation: u32,
313 ) -> Self {
314 Self(NetId64::make(
315 kind,
316 node,
317 ((generation as u64) << SLOT_BITS) | (slot as u64 & SLOT_MASK),
318 ))
319 }
320}
321
322impl fmt::Display for StreamId {
323 fn fmt(
324 &self,
325 f: &mut fmt::Formatter<'_>,
326 ) -> fmt::Result {
327 self.0.fmt(f)
328 }
329}
330
331impl FromStr for StreamId {
332 type Err = Error;
333
334 fn from_str(text: &str) -> Result<Self> {
335 text.parse::<NetId64>()
336 .map(Self)
337 .map_err(|_| Error::Malformed(text.to_owned()))
338 }
339}
340
341#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
343pub enum Side {
344 A,
345 B,
346}
347
348impl Side {
349 const fn index(self) -> usize {
350 match self {
351 Self::A => 0,
352 Self::B => 1,
353 }
354 }
355
356 const fn write_direction(self) -> usize {
358 self.index()
359 }
360
361 const fn read_direction(self) -> usize {
363 1 - self.index()
364 }
365
366 const fn letter(self) -> char {
367 match self {
368 Self::A => 'a',
369 Self::B => 'b',
370 }
371 }
372}
373
374#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
383pub struct Ticket {
384 pub id: StreamId,
385 pub side: Side,
386 pub epoch: u64,
387}
388
389impl fmt::Display for Ticket {
390 fn fmt(
391 &self,
392 f: &mut fmt::Formatter<'_>,
393 ) -> fmt::Result {
394 write!(f, "{}/{}/{}", self.id, self.side.letter(), self.epoch)
395 }
396}
397
398impl FromStr for Ticket {
399 type Err = Error;
400
401 fn from_str(text: &str) -> Result<Self> {
402 let malformed = || Error::Malformed(text.to_owned());
403 let (rest, epoch) = text.rsplit_once('/').ok_or_else(malformed)?;
404 let (id, side) = rest.rsplit_once('/').ok_or_else(malformed)?;
405 let side = match side {
406 "a" => Side::A,
407 "b" => Side::B,
408 _ => return Err(malformed()),
409 };
410 Ok(Self {
411 id: id.parse().map_err(|_| malformed())?,
412 side,
413 epoch: epoch.parse().map_err(|_| malformed())?,
414 })
415 }
416}
417
418#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
424pub struct Incarnation(u64);
425
426impl Incarnation {
427 pub const fn new(value: u64) -> Self {
428 Self(value)
429 }
430
431 pub const fn get(self) -> u64 {
432 self.0
433 }
434}
435
436#[derive(Clone)]
439pub struct Streams {
440 table: Arc<Table>,
441}
442
443impl Streams {
444 #[cfg(unix)]
448 pub fn node_dead_in_shm(
449 name: &str,
450 fleet_capacity: u16,
451 spec: StreamSpec,
452 node: NodeId,
453 incarnation: Incarnation,
454 ) -> Result<bool> {
455 spec.validate()?;
456 if node.get() >= fleet_capacity {
457 return Err(Error::Malformed(
458 "recovery node is outside the fleet".into(),
459 ));
460 }
461 let segment = orbit_core::shm::ring_segment_name(name, spec.kind);
462 match orbit_core::shm::ShmRegion::validate_existing(
463 &segment,
464 segment_size_for(fleet_capacity, spec),
465 )? {
466 orbit_core::shm::ShmValidation::Missing => Ok(false),
467 orbit_core::shm::ShmValidation::Valid { .. } => {
468 table::recover_shm(
469 name,
470 fleet_capacity,
471 spec,
472 node.get(),
473 incarnation.get(),
474 )?;
475 Ok(true)
476 }
477 }
478 }
479
480 pub fn new(
484 fleet: Arc<Fleet>,
485 incarnation: Incarnation,
486 ) -> Result<Self> {
487 Self::with_spec(fleet, incarnation, StreamSpec::DEFAULT)
488 }
489
490 pub fn with_spec(
495 fleet: Arc<Fleet>,
496 incarnation: Incarnation,
497 spec: StreamSpec,
498 ) -> Result<Self> {
499 spec.validate()?;
500 Ok(Self { table: table::open(&fleet, incarnation, spec)? })
501 }
502
503 pub fn kind(&self) -> u8 {
505 self.table.kind()
506 }
507
508 #[cfg(any(
520 target_os = "linux",
521 target_os = "freebsd",
522 target_os = "macos"
523 ))]
524 pub fn readiness(&self) -> Result<Readiness> {
525 self.table.take_readiness()
526 }
527
528 pub fn epoch(&self) -> u64 {
530 self.table.epoch()
531 }
532
533 pub fn create(&self) -> Result<(Endpoint, Ticket)> {
536 let (index, generation) = self.table.allocate()?;
537 let id = StreamId::make(
538 self.table.kind(),
539 self.table.node(),
540 (index % self.table.geometry().lane_capacity) as u32,
541 generation,
542 );
543 let handle = Arc::new(Handle {
544 table: Arc::clone(&self.table),
545 index,
546 id,
547 side: Side::A,
548 });
549 Ok((
550 Endpoint::new(handle),
551 Ticket { id, side: Side::B, epoch: self.table.epoch() },
552 ))
553 }
554
555 pub fn open(
557 &self,
558 ticket: Ticket,
559 ) -> Result<Endpoint> {
560 let index = self.locate(ticket.id)?;
561 if ticket.epoch != self.table.epoch() {
562 return Err(Error::Stale(ticket.id));
563 }
564 let slot = &self.table.slots()[index];
565 if !slot.is(ticket.id.generation()) {
566 return Err(Error::Stale(ticket.id));
567 }
568 let claimed = &slot.claimed[ticket.side.index()];
569 if claimed
570 .compare_exchange(
571 SIDE_FREE,
572 SIDE_CLAIMED,
573 Ordering::SeqCst,
574 Ordering::SeqCst,
575 )
576 .is_err()
577 {
578 return Err(Error::AlreadyClaimed(ticket));
579 }
580 if !slot.is(ticket.id.generation()) {
583 let _ = claimed.compare_exchange(
584 SIDE_CLAIMED,
585 SIDE_FREE,
586 Ordering::SeqCst,
587 Ordering::SeqCst,
588 );
589 return Err(Error::Stale(ticket.id));
590 }
591 slot.incarnation[ticket.side.index()]
592 .store(self.table.incarnation(), Ordering::Release);
593 slot.node[ticket.side.index()]
594 .store(self.table.node(), Ordering::Release);
595 for direction in &slot.directions {
602 self.table.notify(index, slot, direction, table::Ring::Nobody);
603 }
604 Ok(Endpoint::new(Arc::new(Handle {
605 table: Arc::clone(&self.table),
606 index,
607 id: ticket.id,
608 side: ticket.side,
609 })))
610 }
611
612 pub fn rearm(
648 &self,
649 id: StreamId,
650 ) -> Result<()> {
651 let index = self.locate(id)?;
652 let slot = &self.table.slots()[index];
653 if !slot.is(id.generation()) {
654 return Err(Error::Stale(id));
655 }
656 let far = Side::B.index();
657 match slot.claimed[far].load(Ordering::SeqCst) {
658 SIDE_RELEASED => {}
659 SIDE_CLAIMED => return Err(Error::WouldBlock),
660 _ => return Err(Error::Stale(id)),
661 }
662 for direction in &slot.directions {
663 direction.clear();
664 }
665 slot.node[far].store(u16::MAX, Ordering::Relaxed);
666 slot.incarnation[far].store(0, Ordering::Relaxed);
667 slot.claimed[far]
671 .compare_exchange(
672 SIDE_RELEASED,
673 SIDE_FREE,
674 Ordering::SeqCst,
675 Ordering::SeqCst,
676 )
677 .map_err(|_| Error::Stale(id))?;
678 Ok(())
679 }
680
681 pub fn is_live(
683 &self,
684 id: StreamId,
685 ) -> bool {
686 self.locate(id)
687 .map(|index| self.table.slots()[index].is(id.generation()))
688 .unwrap_or(false)
689 }
690
691 pub fn offer(
697 &self,
698 ticket: Ticket,
699 to: NodeId,
700 ) -> Result<()> {
701 let index = self.locate(ticket.id)?;
702 if ticket.epoch != self.table.epoch()
703 || !self.table.slots()[index].is(ticket.id.generation())
704 {
705 return Err(Error::Stale(ticket.id));
706 }
707 self.table.offer(index, to.get())
708 }
709
710 pub fn take_offer(&self) -> Option<Ticket> {
713 while let Some(index) = self.table.take_offer() {
714 if let Some(ticket) = self.ticket_for(index) {
715 return Some(ticket);
716 }
717 }
718 None
719 }
720
721 #[cfg(any(
725 target_os = "linux",
726 target_os = "freebsd",
727 target_os = "macos"
728 ))]
729 pub fn take_offer_timeout(
730 &self,
731 timeout: Duration,
732 ) -> Result<Option<Ticket>> {
733 let deadline = Instant::now() + timeout;
734 loop {
735 if let Some(ticket) = self.take_offer() {
736 return Ok(Some(ticket));
737 }
738 let left = deadline.saturating_duration_since(Instant::now());
739 if left.is_zero() {
740 return Ok(self.take_offer());
741 }
742 if !self.table.wait_offer_timeout(left)? {
743 return Ok(self.take_offer());
744 }
745 }
746 }
747
748 pub fn blocking_take_offer(&self) -> Result<Ticket> {
750 loop {
751 if let Some(ticket) = self.take_offer() {
752 return Ok(ticket);
753 }
754 self.table.wait_offer()?;
755 }
756 }
757
758 pub fn poll_take_offer(
761 &self,
762 cx: &mut std::task::Context<'_>,
763 ) -> std::task::Poll<Result<Ticket>> {
764 if let Some(ticket) = self.take_offer() {
765 return std::task::Poll::Ready(Ok(ticket));
766 }
767 if let Err(error) = self.table.register_offer(cx.waker()) {
768 return std::task::Poll::Ready(Err(error));
769 }
770 match self.take_offer() {
771 Some(ticket) => std::task::Poll::Ready(Ok(ticket)),
772 None => std::task::Poll::Pending,
773 }
774 }
775
776 pub fn owners(&self) -> Vec<(NodeId, Incarnation)> {
782 let mut seen: Vec<(NodeId, Incarnation)> = Vec::new();
783 for slot in self.table.slots() {
784 if slot.state.load(Ordering::Acquire) != SLOT_LIVE {
785 continue;
786 }
787 for side in 0..2 {
788 if slot.claimed[side].load(Ordering::Acquire) != SIDE_CLAIMED {
789 continue;
790 }
791 let holder = (
792 NodeId::new(slot.node[side].load(Ordering::Acquire)),
793 Incarnation::new(
794 slot.incarnation[side].load(Ordering::Acquire),
795 ),
796 );
797 if !seen.contains(&holder) {
798 seen.push(holder);
799 }
800 }
801 }
802 seen.sort_by_key(|(node, incarnation)| (node.get(), incarnation.get()));
803 seen
804 }
805
806 pub fn node_dead(
813 &self,
814 node: NodeId,
815 incarnation: Incarnation,
816 ) {
817 self.table.node_dead(node.get(), incarnation.get());
818 }
819
820 pub fn supersede(&self) {
832 self.table.supersede();
833 }
834
835 pub fn reset_all(&self) {
838 self.table.reset_all();
839 }
840
841 fn ticket_for(
842 &self,
843 index: usize,
844 ) -> Option<Ticket> {
845 let slot = &self.table.slots()[index];
846 let generation = slot.generation.load(Ordering::Acquire);
847 if !slot.is(generation) {
848 return None;
849 }
850 let lane_capacity = self.table.geometry().lane_capacity;
851 let id = StreamId::make(
852 self.table.kind(),
853 (index / lane_capacity) as u16,
854 (index % lane_capacity) as u32,
855 generation,
856 );
857 Some(Ticket { id, side: Side::B, epoch: self.table.epoch() })
858 }
859
860 #[cfg(unix)]
863 pub fn unlink(&self) -> Result<()> {
864 self.table.unlink()
865 }
866
867 fn locate(
868 &self,
869 id: StreamId,
870 ) -> Result<usize> {
871 let geometry = self.table.geometry();
872 if id.kind() != self.table.kind()
873 || usize::from(id.node()) >= geometry.fleet_capacity
874 || id.slot() as usize >= geometry.lane_capacity
875 || id.generation() == 0
876 || id.generation() > GENERATION_MASK
877 {
878 return Err(Error::Malformed(id.to_string()));
879 }
880 Ok(usize::from(id.node()) * geometry.lane_capacity + id.slot() as usize)
881 }
882}
883
884struct Handle {
886 table: Arc<Table>,
887 index: usize,
888 id: StreamId,
889 side: Side,
890}
891
892impl Handle {
893 fn peer_if(
897 &self,
898 ring: bool,
899 ) -> table::Ring {
900 if ring {
901 table::Ring::Side(1 - self.side.index())
902 } else {
903 table::Ring::Nobody
904 }
905 }
906
907 fn slot(&self) -> Result<&Slot> {
908 let slot = &self.table.slots()[self.index];
909 if slot.is(self.id.generation()) {
910 Ok(slot)
911 } else {
912 Err(Error::Stale(self.id))
913 }
914 }
915
916 fn direction(
917 slot: &Slot,
918 direction: usize,
919 ) -> &Direction {
920 &slot.directions[direction]
921 }
922
923 fn try_write(
925 &self,
926 buf: &[u8],
927 ) -> Result<usize> {
928 let slot = self.slot()?;
929 let direction = Self::direction(slot, self.side.write_direction());
930 let flags = direction.flags();
931 if flags & FLAG_RESET != 0 {
932 return Err(Error::Reset);
933 }
934 if flags & FLAG_FIN != 0 {
935 return Err(Error::Closed);
936 }
937 if flags & FLAG_READER_GONE != 0 {
938 return Err(Error::PeerGone);
939 }
940 let buffer_bytes = self.table.geometry().buffer_bytes;
941 let head = direction.head.load(Ordering::Relaxed);
942 let tail = direction.tail.load(Ordering::Acquire);
943 let free = buffer_bytes - (head - tail) as usize;
944 if buf.is_empty() {
945 return Ok(0);
946 }
947 if free == 0 {
948 return Err(Error::WouldBlock);
949 }
950 let len = buf.len().min(free);
951 let base = self.table.buffer(self.index, self.side.write_direction());
952 let start = (head as usize) & (buffer_bytes - 1);
953 let first = len.min(buffer_bytes - start);
954 unsafe {
957 std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
958 std::ptr::copy_nonoverlapping(
959 buf.as_ptr().add(first),
960 base,
961 len - first,
962 );
963 }
964 direction.head.store(head + len as u64, Ordering::SeqCst);
965 std::sync::atomic::fence(Ordering::SeqCst);
975 let drained = direction.tail.load(Ordering::SeqCst) >= head;
976 self.table.notify(self.index, slot, direction, self.peer_if(drained));
977 Ok(len)
978 }
979
980 fn try_write_exact(
982 &self,
983 buf: &[u8],
984 ) -> Result<()> {
985 let slot = self.slot()?;
986 let direction = Self::direction(slot, self.side.write_direction());
987 let flags = direction.flags();
988 if flags & FLAG_RESET != 0 {
989 return Err(Error::Reset);
990 }
991 if flags & FLAG_FIN != 0 {
992 return Err(Error::Closed);
993 }
994 if flags & FLAG_READER_GONE != 0 {
995 return Err(Error::PeerGone);
996 }
997 let buffer_bytes = self.table.geometry().buffer_bytes;
998 if buf.len() > buffer_bytes {
999 return Err(Error::Malformed(format!(
1000 "atomic stream write is {} bytes; ring capacity is {buffer_bytes}",
1001 buf.len()
1002 )));
1003 }
1004 if buf.is_empty() {
1005 return Ok(());
1006 }
1007 let head = direction.head.load(Ordering::Relaxed);
1008 let tail = direction.tail.load(Ordering::Acquire);
1009 if buffer_bytes - ((head - tail) as usize) < buf.len() {
1010 return Err(Error::WouldBlock);
1011 }
1012 let base = self.table.buffer(self.index, self.side.write_direction());
1013 let start = (head as usize) & (buffer_bytes - 1);
1014 let first = buf.len().min(buffer_bytes - start);
1015 unsafe {
1017 std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
1018 std::ptr::copy_nonoverlapping(
1019 buf.as_ptr().add(first),
1020 base,
1021 buf.len() - first,
1022 );
1023 }
1024 direction.head.store(head + buf.len() as u64, Ordering::SeqCst);
1025 std::sync::atomic::fence(Ordering::SeqCst);
1026 let drained = direction.tail.load(Ordering::SeqCst) >= head;
1027 self.table.notify(self.index, slot, direction, self.peer_if(drained));
1028 Ok(())
1029 }
1030
1031 fn try_read(
1034 &self,
1035 buf: &mut [u8],
1036 ) -> Result<usize> {
1037 let slot = self.slot()?;
1038 let direction = Self::direction(slot, self.side.read_direction());
1039 let flags = direction.flags();
1040 if flags & FLAG_RESET != 0 {
1041 return Err(Error::Reset);
1042 }
1043 let head = direction.head.load(Ordering::Acquire);
1044 let tail = direction.tail.load(Ordering::Relaxed);
1045 let available = (head - tail) as usize;
1046 if buf.is_empty() {
1047 return Ok(0);
1048 }
1049 if available == 0 {
1050 if flags & FLAG_FIN != 0 {
1051 return Ok(0);
1052 }
1053 return Err(Error::WouldBlock);
1054 }
1055 let len = buf.len().min(available);
1056 let base = self.table.buffer(self.index, self.side.read_direction());
1057 let buffer_bytes = self.table.geometry().buffer_bytes;
1058 let start = (tail as usize) & (buffer_bytes - 1);
1059 let first = len.min(buffer_bytes - start);
1060 unsafe {
1064 std::ptr::copy_nonoverlapping(
1065 base.add(start),
1066 buf.as_mut_ptr(),
1067 first,
1068 );
1069 std::ptr::copy_nonoverlapping(
1070 base,
1071 buf.as_mut_ptr().add(first),
1072 len - first,
1073 );
1074 }
1075 direction.tail.store(tail + len as u64, Ordering::SeqCst);
1076 std::sync::atomic::fence(Ordering::SeqCst);
1080 let head_now = direction.head.load(Ordering::SeqCst);
1081 let was_full = head_now - tail >= buffer_bytes as u64;
1082 self.table.notify(self.index, slot, direction, self.peer_if(was_full));
1083 Ok(len)
1084 }
1085
1086 fn try_read_exact(
1088 &self,
1089 buf: &mut [u8],
1090 ) -> Result<()> {
1091 let slot = self.slot()?;
1092 let direction = Self::direction(slot, self.side.read_direction());
1093 let flags = direction.flags();
1094 if flags & FLAG_RESET != 0 {
1095 return Err(Error::Reset);
1096 }
1097 let buffer_bytes = self.table.geometry().buffer_bytes;
1098 if buf.len() > buffer_bytes {
1099 return Err(Error::Malformed(format!(
1100 "atomic stream read is {} bytes; ring capacity is {buffer_bytes}",
1101 buf.len()
1102 )));
1103 }
1104 if buf.is_empty() {
1105 return Ok(());
1106 }
1107 let head = direction.head.load(Ordering::Acquire);
1108 let tail = direction.tail.load(Ordering::Relaxed);
1109 let available = (head - tail) as usize;
1110 if available < buf.len() {
1111 if flags & FLAG_FIN != 0 {
1112 return Err(Error::Malformed(format!(
1113 "stream ended with {available} bytes of a {} byte frame",
1114 buf.len()
1115 )));
1116 }
1117 return Err(Error::WouldBlock);
1118 }
1119 let base = self.table.buffer(self.index, self.side.read_direction());
1120 let start = (tail as usize) & (buffer_bytes - 1);
1121 let first = buf.len().min(buffer_bytes - start);
1122 unsafe {
1124 std::ptr::copy_nonoverlapping(
1125 base.add(start),
1126 buf.as_mut_ptr(),
1127 first,
1128 );
1129 std::ptr::copy_nonoverlapping(
1130 base,
1131 buf.as_mut_ptr().add(first),
1132 buf.len() - first,
1133 );
1134 }
1135 direction.tail.store(tail + buf.len() as u64, Ordering::SeqCst);
1136 std::sync::atomic::fence(Ordering::SeqCst);
1137 let head_now = direction.head.load(Ordering::SeqCst);
1138 let was_full = head_now - tail >= buffer_bytes as u64;
1139 self.table.notify(self.index, slot, direction, self.peer_if(was_full));
1140 Ok(())
1141 }
1142
1143 fn set_flag(
1144 &self,
1145 direction_index: usize,
1146 flag: u8,
1147 ) {
1148 if let Ok(slot) = self.slot() {
1149 let direction = Self::direction(slot, direction_index);
1150 let before = direction.flags.fetch_or(flag, Ordering::SeqCst);
1151 if before & flag == 0 {
1152 let ring = flag != FLAG_READER_GONE
1153 || writer_may_park(direction, before);
1154 self.table.notify(
1155 self.index,
1156 slot,
1157 direction,
1158 self.peer_if(ring),
1159 );
1160 }
1161 }
1162 }
1163
1164 #[cfg(any(
1167 target_os = "linux",
1168 target_os = "freebsd",
1169 target_os = "macos"
1170 ))]
1171 fn wait_until_timeout(
1172 &self,
1173 direction_index: usize,
1174 ready: impl Fn(&Direction) -> bool,
1175 timeout: Duration,
1176 ) -> Result<bool> {
1177 let deadline = Instant::now() + timeout;
1178 loop {
1179 let slot = self.slot()?;
1180 let direction = Self::direction(slot, direction_index);
1181 if ready(direction) {
1182 return Ok(true);
1183 }
1184 let left = deadline.saturating_duration_since(Instant::now());
1185 if left.is_zero() {
1186 return Ok(false);
1187 }
1188 direction.waiters.fetch_add(1, Ordering::SeqCst);
1189 let since = direction.changes.load(Ordering::SeqCst);
1190 let outcome = if ready(direction) {
1191 Ok(true)
1192 } else {
1193 wait_on_timeout(&direction.changes, since, left)
1194 };
1195 direction.waiters.fetch_sub(1, Ordering::SeqCst);
1196 if !outcome? {
1197 let slot = self.slot()?;
1200 return Ok(ready(Self::direction(slot, direction_index)));
1201 }
1202 }
1203 }
1204
1205 fn wait_until(
1206 &self,
1207 direction_index: usize,
1208 ready: impl Fn(&Direction) -> bool,
1209 ) -> Result<()> {
1210 loop {
1211 let slot = self.slot()?;
1212 let direction = Self::direction(slot, direction_index);
1213 if ready(direction) {
1214 return Ok(());
1215 }
1216 direction.waiters.fetch_add(1, Ordering::SeqCst);
1217 let since = direction.changes.load(Ordering::SeqCst);
1218 let outcome = if ready(direction) {
1219 Ok(())
1220 } else {
1221 wait_on(&direction.changes, since)
1222 };
1223 direction.waiters.fetch_sub(1, Ordering::SeqCst);
1224 outcome?;
1225 }
1226 }
1227
1228 fn poll_ready(
1231 &self,
1232 interest: Interest,
1233 ready: impl Fn(&Direction) -> bool,
1234 cx: &mut std::task::Context<'_>,
1235 ) -> std::task::Poll<Result<()>> {
1236 let slot = match self.slot() {
1237 Ok(slot) => slot,
1238 Err(error) => return std::task::Poll::Ready(Err(error)),
1239 };
1240 let direction = Self::direction(slot, interest.direction);
1241 if ready(direction) {
1242 return std::task::Poll::Ready(Ok(()));
1243 }
1244 if let Err(error) =
1245 self.table.register(self.index, interest, cx.waker())
1246 {
1247 return std::task::Poll::Ready(Err(error));
1248 }
1249 std::sync::atomic::fence(Ordering::SeqCst);
1252 if ready(direction) {
1253 std::task::Poll::Ready(Ok(()))
1254 } else {
1255 std::task::Poll::Pending
1256 }
1257 }
1258}
1259
1260fn writer_may_park(
1274 direction: &Direction,
1275 before: u8,
1276) -> bool {
1277 if before & (FLAG_FIN | FLAG_RESET) != 0 {
1278 return false;
1279 }
1280 std::sync::atomic::fence(Ordering::SeqCst);
1281 direction.head.load(Ordering::SeqCst)
1282 != direction.tail.load(Ordering::SeqCst)
1283}
1284
1285fn readable(direction: &Direction) -> bool {
1286 direction.head.load(Ordering::Acquire)
1287 != direction.tail.load(Ordering::Relaxed)
1288 || direction.flags() & (FLAG_FIN | FLAG_RESET) != 0
1289}
1290
1291fn writable(
1292 direction: &Direction,
1293 buffer_bytes: usize,
1294) -> bool {
1295 let queued = (direction.head.load(Ordering::Relaxed)
1296 - direction.tail.load(Ordering::Acquire)) as usize;
1297 queued < buffer_bytes
1298 || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1299}
1300
1301fn writable_exact(
1302 direction: &Direction,
1303 buffer_bytes: usize,
1304 needed: usize,
1305) -> bool {
1306 let queued = (direction.head.load(Ordering::Relaxed)
1307 - direction.tail.load(Ordering::Acquire)) as usize;
1308 buffer_bytes.saturating_sub(queued) >= needed
1309 || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1310}
1311
1312impl Drop for Handle {
1313 fn drop(&mut self) {
1317 let slot = &self.table.slots()[self.index];
1318 if !slot.is(self.id.generation()) {
1319 return;
1320 }
1321 if slot.claimed[self.side.index()]
1324 .compare_exchange(
1325 SIDE_CLAIMED,
1326 SIDE_RELEASED,
1327 Ordering::SeqCst,
1328 Ordering::SeqCst,
1329 )
1330 .is_err()
1331 {
1332 return;
1333 }
1334 let other = slot.claimed[1 - self.side.index()].load(Ordering::SeqCst);
1338 if other != SIDE_CLAIMED {
1339 let _ = slot.state.compare_exchange(
1340 SLOT_LIVE,
1341 SLOT_EMPTY,
1342 Ordering::SeqCst,
1343 Ordering::SeqCst,
1344 );
1345 for direction in &slot.directions {
1346 direction.changes.fetch_add(1, Ordering::SeqCst);
1349 if direction.waiters.load(Ordering::SeqCst) > 0 {
1350 wake_on(&direction.changes);
1351 }
1352 }
1353 self.table.registry.wake(self.index);
1354 }
1355 }
1356}
1357
1358pub struct Endpoint {
1362 read: ReadHalf,
1363 write: WriteHalf,
1364}
1365
1366impl Endpoint {
1367 fn new(handle: Arc<Handle>) -> Self {
1368 Self {
1369 read: ReadHalf { handle: Arc::clone(&handle) },
1370 write: WriteHalf { handle, shut: false },
1371 }
1372 }
1373
1374 pub fn id(&self) -> StreamId {
1375 self.read.handle.id
1376 }
1377
1378 pub fn side(&self) -> Side {
1379 self.read.handle.side
1380 }
1381
1382 pub fn split(self) -> (ReadHalf, WriteHalf) {
1383 (self.read, self.write)
1384 }
1385
1386 pub fn rejoin(
1397 read: ReadHalf,
1398 write: WriteHalf,
1399 ) -> Result<Self> {
1400 if read.id() != write.id() {
1401 return Err(Error::Stale(read.id()));
1402 }
1403 Ok(Self { read, write })
1404 }
1405
1406 pub fn rearm(
1419 &mut self,
1420 streams: &Streams,
1421 ) -> Result<()> {
1422 if self.side() != Side::A {
1423 return Err(Error::Malformed(
1424 "only the creator side of a stream can be rearmed".to_owned(),
1425 ));
1426 }
1427 streams.rearm(self.id())?;
1428 self.write.shut = false;
1429 Ok(())
1430 }
1431
1432 pub fn try_read(
1433 &self,
1434 buf: &mut [u8],
1435 ) -> Result<usize> {
1436 self.read.try_read(buf)
1437 }
1438
1439 pub fn try_read_exact(
1440 &self,
1441 buf: &mut [u8],
1442 ) -> Result<()> {
1443 self.read.try_read_exact(buf)
1444 }
1445
1446 pub fn blocking_read(
1447 &self,
1448 buf: &mut [u8],
1449 ) -> Result<usize> {
1450 self.read.blocking_read(buf)
1451 }
1452
1453 pub fn blocking_read_chunk(
1454 &self,
1455 max: usize,
1456 ) -> Result<Bytes> {
1457 self.read.blocking_read_chunk(max)
1458 }
1459
1460 pub fn try_write(
1461 &self,
1462 buf: &[u8],
1463 ) -> Result<usize> {
1464 self.write.try_write(buf)
1465 }
1466
1467 pub fn try_write_exact(
1468 &self,
1469 buf: &[u8],
1470 ) -> Result<()> {
1471 self.write.try_write_exact(buf)
1472 }
1473
1474 pub fn blocking_write(
1475 &self,
1476 buf: &[u8],
1477 ) -> Result<usize> {
1478 self.write.blocking_write(buf)
1479 }
1480
1481 pub fn blocking_write_all(
1482 &self,
1483 buf: &[u8],
1484 ) -> Result<()> {
1485 self.write.blocking_write_all(buf)
1486 }
1487
1488 pub fn finish(&mut self) -> Result<()> {
1489 self.write.finish()
1490 }
1491
1492 pub fn reset(&mut self) {
1493 self.write.reset()
1494 }
1495}
1496
1497pub struct ReadHalf {
1500 handle: Arc<Handle>,
1501}
1502
1503impl ReadHalf {
1504 pub fn id(&self) -> StreamId {
1505 self.handle.id
1506 }
1507
1508 pub fn try_read(
1511 &self,
1512 buf: &mut [u8],
1513 ) -> Result<usize> {
1514 self.handle.try_read(buf)
1515 }
1516
1517 pub fn try_read_exact(
1520 &self,
1521 buf: &mut [u8],
1522 ) -> Result<()> {
1523 self.handle.try_read_exact(buf)
1524 }
1525
1526 pub fn blocking_read(
1529 &self,
1530 buf: &mut [u8],
1531 ) -> Result<usize> {
1532 loop {
1533 match self.handle.try_read(buf) {
1534 Err(Error::WouldBlock) => self.wait_readable()?,
1535 other => return other,
1536 }
1537 }
1538 }
1539
1540 pub fn stop_reading(&self) {
1548 self.handle
1549 .set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1550 }
1551
1552 pub fn blocking_read_chunk(
1554 &self,
1555 max: usize,
1556 ) -> Result<Bytes> {
1557 let mut chunk = BytesMut::zeroed(max);
1558 let len = self.blocking_read(&mut chunk)?;
1559 chunk.truncate(len);
1560 Ok(chunk.freeze())
1561 }
1562
1563 #[cfg(any(
1565 target_os = "linux",
1566 target_os = "freebsd",
1567 target_os = "macos"
1568 ))]
1569 pub fn wait_readable_timeout(
1570 &self,
1571 timeout: Duration,
1572 ) -> Result<bool> {
1573 self.handle.wait_until_timeout(
1574 self.handle.side.read_direction(),
1575 readable,
1576 timeout,
1577 )
1578 }
1579
1580 pub fn wait_readable(&self) -> Result<()> {
1582 self.handle.wait_until(self.handle.side.read_direction(), readable)
1583 }
1584
1585 pub fn poll_readable(
1590 &self,
1591 cx: &mut std::task::Context<'_>,
1592 ) -> std::task::Poll<Result<()>> {
1593 self.handle.poll_ready(
1594 Interest {
1595 direction: self.handle.side.read_direction(),
1596 writer: false,
1597 },
1598 readable,
1599 cx,
1600 )
1601 }
1602}
1603
1604impl Drop for ReadHalf {
1605 fn drop(&mut self) {
1606 self.handle
1607 .set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1608 }
1609}
1610
1611pub struct WriteHalf {
1614 handle: Arc<Handle>,
1615 shut: bool,
1616}
1617
1618impl WriteHalf {
1619 pub fn poll_peer_dead(
1625 &self,
1626 cx: &mut std::task::Context<'_>,
1627 ) -> std::task::Poll<Result<()>> {
1628 let slot = match self.handle.slot() {
1629 Ok(slot) => slot,
1630 Err(error) => return std::task::Poll::Ready(Err(error)),
1631 };
1632 let peer = &slot.claimed[1 - self.handle.side.index()];
1633 if peer.load(Ordering::SeqCst) == SIDE_DEAD {
1634 return std::task::Poll::Ready(Ok(()));
1635 }
1636 let interest = Interest {
1637 direction: self.handle.side.write_direction(),
1638 writer: true,
1639 };
1640 if let Err(error) =
1641 self.handle.table.register(self.handle.index, interest, cx.waker())
1642 {
1643 return std::task::Poll::Ready(Err(error));
1644 }
1645 if peer.load(Ordering::SeqCst) == SIDE_DEAD {
1648 std::task::Poll::Ready(Ok(()))
1649 } else {
1650 std::task::Poll::Pending
1651 }
1652 }
1653
1654 pub fn id(&self) -> StreamId {
1655 self.handle.id
1656 }
1657
1658 pub fn try_write(
1660 &self,
1661 buf: &[u8],
1662 ) -> Result<usize> {
1663 self.handle.try_write(buf)
1664 }
1665
1666 pub fn try_write_exact(
1669 &self,
1670 buf: &[u8],
1671 ) -> Result<()> {
1672 self.handle.try_write_exact(buf)
1673 }
1674
1675 pub fn blocking_write(
1677 &self,
1678 buf: &[u8],
1679 ) -> Result<usize> {
1680 loop {
1681 match self.handle.try_write(buf) {
1682 Err(Error::WouldBlock) => self.wait_writable()?,
1683 other => return other,
1684 }
1685 }
1686 }
1687
1688 pub fn blocking_write_all(
1691 &self,
1692 mut buf: &[u8],
1693 ) -> Result<()> {
1694 while !buf.is_empty() {
1695 let written = self.blocking_write(buf)?;
1696 buf = &buf[written..];
1697 }
1698 Ok(())
1699 }
1700
1701 pub fn finish(&mut self) -> Result<()> {
1705 self.shut = true;
1706 self.handle.set_flag(self.handle.side.write_direction(), FLAG_FIN);
1707 Ok(())
1708 }
1709
1710 pub fn reset(&mut self) {
1713 self.shut = true;
1714 self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1715 }
1716
1717 #[cfg(any(
1721 target_os = "linux",
1722 target_os = "freebsd",
1723 target_os = "macos"
1724 ))]
1725 pub fn wait_writable_timeout(
1726 &self,
1727 timeout: Duration,
1728 ) -> Result<bool> {
1729 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1730 self.handle.wait_until_timeout(
1731 self.handle.side.write_direction(),
1732 |direction| writable(direction, buffer_bytes),
1733 timeout,
1734 )
1735 }
1736
1737 pub fn wait_writable(&self) -> Result<()> {
1739 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1740 self.handle
1741 .wait_until(self.handle.side.write_direction(), |direction| {
1742 writable(direction, buffer_bytes)
1743 })
1744 }
1745
1746 pub fn wait_writable_exact(
1748 &self,
1749 needed: usize,
1750 ) -> Result<()> {
1751 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1752 if needed > buffer_bytes {
1753 return Err(Error::Malformed(format!(
1754 "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1755 )));
1756 }
1757 self.handle
1758 .wait_until(self.handle.side.write_direction(), |direction| {
1759 writable_exact(direction, buffer_bytes, needed)
1760 })
1761 }
1762
1763 pub fn poll_writable(
1768 &self,
1769 cx: &mut std::task::Context<'_>,
1770 ) -> std::task::Poll<Result<()>> {
1771 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1772 self.handle.poll_ready(
1773 Interest {
1774 direction: self.handle.side.write_direction(),
1775 writer: true,
1776 },
1777 |direction| writable(direction, buffer_bytes),
1778 cx,
1779 )
1780 }
1781
1782 pub fn poll_writable_exact(
1784 &self,
1785 needed: usize,
1786 cx: &mut std::task::Context<'_>,
1787 ) -> std::task::Poll<Result<()>> {
1788 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1789 if needed > buffer_bytes {
1790 return std::task::Poll::Ready(Err(Error::Malformed(format!(
1791 "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1792 ))));
1793 }
1794 self.handle.poll_ready(
1795 Interest {
1796 direction: self.handle.side.write_direction(),
1797 writer: true,
1798 },
1799 |direction| writable_exact(direction, buffer_bytes, needed),
1800 cx,
1801 )
1802 }
1803}
1804
1805impl Drop for WriteHalf {
1806 fn drop(&mut self) {
1807 if !self.shut {
1808 self.handle
1809 .set_flag(self.handle.side.write_direction(), FLAG_RESET);
1810 }
1811 }
1812}
1813
1814#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1818pub(crate) fn waits_supported() -> bool {
1819 orbit_core::sync::supported()
1820}
1821
1822#[cfg(not(any(
1823 target_os = "linux",
1824 target_os = "freebsd",
1825 target_os = "macos"
1826)))]
1827pub(crate) fn waits_supported() -> bool {
1828 false
1829}
1830
1831#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1835#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1838pub(crate) fn wait_on_timeout(
1839 word: &AtomicU32,
1840 expected: u32,
1841 timeout: Duration,
1842) -> Result<bool> {
1843 orbit_core::sync::wait_word_timeout(word, expected, timeout)
1844 .map_err(Error::Io)
1845}
1846
1847pub(crate) fn wait_on(
1848 word: &AtomicU32,
1849 expected: u32,
1850) -> Result<()> {
1851 orbit_core::sync::wait_word(word, expected).map_err(Error::Io)
1852}
1853
1854#[cfg(not(any(
1855 target_os = "linux",
1856 target_os = "freebsd",
1857 target_os = "macos"
1858)))]
1859pub(crate) fn wait_on(
1860 _word: &AtomicU32,
1861 _expected: u32,
1862) -> Result<()> {
1863 Err(Error::Io(std::io::Error::new(
1864 std::io::ErrorKind::Unsupported,
1865 "orbit-stream needs a platform that can wait on a shared word",
1866 )))
1867}
1868
1869pub(crate) fn wake_on(word: &AtomicU32) {
1871 #[cfg(any(
1872 target_os = "linux",
1873 target_os = "freebsd",
1874 target_os = "macos"
1875 ))]
1876 let _ = orbit_core::sync::wake_word(word);
1877 #[cfg(not(any(
1878 target_os = "linux",
1879 target_os = "freebsd",
1880 target_os = "macos"
1881 )))]
1882 let _ = word;
1883}
1884
1885pub(crate) fn lock_unpoisoned<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
1886 mutex.lock().unwrap_or_else(|error| error.into_inner())
1887}
1888
1889#[cfg(test)]
1890mod tests {
1891 use std::sync::Arc;
1892 use std::time::Duration;
1893
1894 use orbit_core::{Fleet, NodeId};
1895
1896 use super::{
1897 Error, Incarnation, STREAM_BUFFER_BYTES, STREAM_LANE_CAPACITY, Side,
1898 Streams, Ticket,
1899 };
1900
1901 fn streams(name: &'static str) -> Streams {
1902 Streams::new(
1903 Arc::new(Fleet::join(name, 2).unwrap()),
1904 Incarnation::new(1),
1905 )
1906 .unwrap()
1907 }
1908
1909 #[test]
1910 fn bytes_cross_in_order_and_end_cleanly() {
1911 let streams = streams("stream-order");
1912 let (mut a, ticket) = streams.create().unwrap();
1913 assert_eq!(ticket.side, Side::B);
1914 assert_eq!(ticket.id.node(), 0);
1915 assert_eq!(ticket.epoch, streams.epoch());
1916 let b = streams.open(ticket).unwrap();
1917
1918 a.blocking_write_all(b"hello, ").unwrap();
1919 a.blocking_write_all(b"world").unwrap();
1920 a.finish().unwrap();
1921
1922 let mut out = Vec::new();
1923 let mut buf = [0_u8; 4];
1924 loop {
1925 let n = b.blocking_read(&mut buf).unwrap();
1926 if n == 0 {
1927 break;
1928 }
1929 out.extend_from_slice(&buf[..n]);
1930 }
1931 assert_eq!(out, b"hello, world");
1932 b.blocking_write_all(b"back").unwrap();
1934 assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"back");
1935 }
1936
1937 #[test]
1938 fn a_ticket_prints_and_parses() {
1939 let streams = streams("stream-ticket");
1940 let (_a, ticket) = streams.create().unwrap();
1941 let text = ticket.to_string();
1942 assert!(text.contains("/b/"), "{text}");
1943 assert_eq!(text.parse::<Ticket>().unwrap(), ticket);
1944 assert!(matches!("nope".parse::<Ticket>(), Err(Error::Malformed(_))));
1945 assert!(matches!("x/b".parse::<Ticket>(), Err(Error::Malformed(_))));
1946 }
1947
1948 #[test]
1949 fn a_side_is_held_once_and_a_released_stream_goes_stale() {
1950 let streams = streams("stream-claim");
1951 let (a, ticket) = streams.create().unwrap();
1952 let b = streams.open(ticket).unwrap();
1953 assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_))));
1954 assert!(streams.is_live(ticket.id));
1955
1956 drop(a);
1957 let mut buf = [0_u8; 1];
1959 assert!(matches!(b.blocking_read(&mut buf), Err(Error::Reset)));
1960 drop(b);
1961 assert!(!streams.is_live(ticket.id));
1962 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1963
1964 for _ in 0..STREAM_LANE_CAPACITY - 1 {
1967 drop(streams.create().unwrap());
1968 }
1969 let (_a2, ticket2) = streams.create().unwrap();
1970 assert_eq!(ticket2.id.slot(), ticket.id.slot());
1971 assert_ne!(ticket2.id.generation(), ticket.id.generation());
1972 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1973 }
1974
1975 #[test]
1976 fn a_reset_starts_a_new_epoch_and_refuses_old_tickets() {
1977 let streams = streams("stream-epoch");
1978 let (_a, ticket) = streams.create().unwrap();
1979 let before = streams.epoch();
1980 streams.reset_all();
1981 assert!(streams.epoch() > before);
1982 let (_a2, ticket2) = streams.create().unwrap();
1985 assert_eq!(ticket2.id, ticket.id);
1986 assert_ne!(ticket2.epoch, ticket.epoch);
1987 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1988 assert!(streams.open(ticket2).is_ok());
1989 }
1990
1991 #[test]
1992 fn a_slot_whose_generation_runs_out_is_not_reused() {
1993 let streams = streams("stream-exhaust");
1996 let limit = super::layout::GENERATION_LIMIT as usize;
1997 for _ in 0..STREAM_LANE_CAPACITY * limit {
1998 drop(streams.create().unwrap());
1999 }
2000 assert!(matches!(streams.create(), Err(Error::Full { .. })));
2001 streams.reset_all();
2002 let (_a, ticket) = streams.create().unwrap();
2003 assert_eq!(ticket.id.generation(), 1);
2004 }
2005
2006 #[test]
2007 fn a_full_ring_blocks_the_writer_and_wraps_correctly() {
2008 let streams = streams("stream-wrap");
2009 let (a, ticket) = streams.create().unwrap();
2010 let b = streams.open(ticket).unwrap();
2011
2012 let filler = vec![7_u8; STREAM_BUFFER_BYTES];
2013 assert_eq!(a.try_write(&filler).unwrap(), STREAM_BUFFER_BYTES);
2014 assert!(matches!(a.try_write(b"x"), Err(Error::WouldBlock)));
2015
2016 let mut buf = vec![0_u8; STREAM_BUFFER_BYTES - 5];
2017 assert_eq!(b.blocking_read(&mut buf).unwrap(), STREAM_BUFFER_BYTES - 5);
2018 let tail =
2020 (0..STREAM_BUFFER_BYTES).map(|i| i as u8).collect::<Vec<_>>();
2021 assert_eq!(a.try_write(&tail).unwrap(), STREAM_BUFFER_BYTES - 5);
2022
2023 let mut rest = vec![0_u8; 5];
2024 assert_eq!(b.blocking_read(&mut rest).unwrap(), 5);
2025 assert_eq!(rest, vec![7_u8; 5]);
2026 let mut wrapped = vec![0_u8; STREAM_BUFFER_BYTES];
2027 let mut got = 0;
2028 while got < STREAM_BUFFER_BYTES - 5 {
2029 got += b.blocking_read(&mut wrapped[got..]).unwrap();
2030 }
2031 assert_eq!(&wrapped[..got], &tail[..got]);
2032 }
2033
2034 #[test]
2035 fn exact_frames_never_publish_or_consume_a_prefix() {
2036 let streams = streams("stream-exact-frame");
2037 let (a, ticket) = streams.create().unwrap();
2038 let b = streams.open(ticket).unwrap();
2039
2040 let filler = vec![7_u8; STREAM_BUFFER_BYTES - 32];
2041 a.try_write_exact(&filler).unwrap();
2042 assert!(matches!(
2043 a.try_write_exact(&[9_u8; 64]),
2044 Err(Error::WouldBlock)
2045 ));
2046 let mut consumed = vec![0_u8; filler.len()];
2047 b.try_read_exact(&mut consumed).unwrap();
2048 assert_eq!(consumed, filler);
2049 assert!(matches!(b.try_read(&mut [0_u8; 1]), Err(Error::WouldBlock)));
2050
2051 a.try_write_exact(&[3_u8; 32]).unwrap();
2052 assert!(matches!(
2053 b.try_read_exact(&mut [0_u8; 64]),
2054 Err(Error::WouldBlock)
2055 ));
2056 let mut frame = [0_u8; 32];
2057 b.try_read_exact(&mut frame).unwrap();
2058 assert_eq!(frame, [3_u8; 32]);
2059 }
2060
2061 #[test]
2062 fn a_blocked_reader_is_woken_by_a_write_in_another_thread() {
2063 let streams = streams("stream-wake");
2064 let (a, ticket) = streams.create().unwrap();
2065 let b = streams.open(ticket).unwrap();
2066 let reader = std::thread::spawn(move || {
2067 let mut buf = [0_u8; 8];
2068 let n = b.blocking_read(&mut buf).unwrap();
2069 buf[..n].to_vec()
2070 });
2071 std::thread::sleep(Duration::from_millis(30));
2072 a.blocking_write_all(b"ping").unwrap();
2073 assert_eq!(reader.join().unwrap(), b"ping");
2074 }
2075
2076 #[test]
2077 fn a_reader_gone_fails_the_writer_and_a_lane_fills_up() {
2078 let streams = streams("stream-gone");
2079 let (a, ticket) = streams.create().unwrap();
2080 let b = streams.open(ticket).unwrap();
2081 let (b_read, _b_write) = b.split();
2082 drop(b_read);
2083 assert!(matches!(a.blocking_write(b"nobody"), Err(Error::PeerGone)));
2084
2085 let mut held = vec![a];
2086 for _ in 1..STREAM_LANE_CAPACITY {
2087 held.push(streams.create().unwrap().0);
2088 }
2089 assert!(matches!(streams.create(), Err(Error::Full { .. })));
2090 }
2091
2092 #[test]
2093 fn a_death_report_ends_that_incarnations_sides_only() {
2094 let streams = streams("stream-dead");
2095 let (a, ticket) = streams.create().unwrap();
2096 let b = streams.open(ticket).unwrap();
2097 b.blocking_write_all(b"partial").unwrap();
2098
2099 streams.node_dead(NodeId::ZERO, Incarnation::new(2));
2101 assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"partial");
2102
2103 let reader = std::thread::spawn(move || {
2109 let mut buf = [0_u8; 8];
2110 a.blocking_read(&mut buf).map(|_| ())
2111 });
2112 std::thread::sleep(Duration::from_millis(30));
2113 streams.node_dead(NodeId::ZERO, Incarnation::new(1));
2114 assert!(matches!(
2115 reader.join().unwrap(),
2116 Err(Error::Reset | Error::Stale(_))
2117 ));
2118 assert!(matches!(
2119 b.try_write(b"x"),
2120 Err(Error::Reset | Error::Stale(_))
2121 ));
2122 assert!(matches!(
2123 streams.open(ticket),
2124 Err(Error::AlreadyClaimed(_) | Error::Stale(_))
2125 ));
2126 drop(b);
2127 assert!(!streams.is_live(ticket.id));
2128 }
2129
2130 #[test]
2131 fn an_offer_is_taken_once_and_a_stale_offer_is_skipped() {
2132 let streams = streams("stream-offer");
2133 assert!(streams.take_offer().is_none());
2134 let (a, ticket) = streams.create().unwrap();
2135 streams.offer(ticket, NodeId::ZERO).unwrap();
2136 assert_eq!(streams.take_offer(), Some(ticket));
2137 assert!(streams.take_offer().is_none());
2138
2139 let (gone, stale) = streams.create().unwrap();
2140 streams.offer(stale, NodeId::ZERO).unwrap();
2141 drop(gone);
2142 assert!(streams.take_offer().is_none());
2143
2144 let taker = streams.clone();
2145 let waiter = std::thread::spawn(move || taker.blocking_take_offer());
2146 std::thread::sleep(Duration::from_millis(30));
2147 streams.offer(ticket, NodeId::ZERO).unwrap();
2148 assert_eq!(waiter.join().unwrap().unwrap(), ticket);
2149 drop(a);
2150 }
2151
2152 #[test]
2153 fn a_contested_claim_goes_to_exactly_one_opener() {
2154 let streams = streams("stream-race");
2155 for _ in 0..50 {
2156 let (_a, ticket) = streams.create().unwrap();
2157 let openers = (0..4)
2158 .map(|_| {
2159 let streams = streams.clone();
2160 std::thread::spawn(move || streams.open(ticket).is_ok())
2161 })
2162 .collect::<Vec<_>>();
2163 let won = openers
2164 .into_iter()
2165 .map(|opener| opener.join().unwrap())
2166 .filter(|won| *won)
2167 .count();
2168 assert_eq!(won, 1);
2169 }
2170 }
2171
2172 #[test]
2173 fn random_sizes_concatenate_exactly() {
2174 let streams = streams("stream-random");
2175 let (mut a, ticket) = streams.create().unwrap();
2176 let b = streams.open(ticket).unwrap();
2177 let payload = (0..300_000_u32)
2178 .map(|i| (i.wrapping_mul(2654435761) >> 13) as u8)
2179 .collect::<Vec<_>>();
2180 let expected = payload.clone();
2181 let writer = std::thread::spawn(move || {
2182 let mut rest = &payload[..];
2183 let mut step = 1_usize;
2184 while !rest.is_empty() {
2185 let len = step.min(rest.len());
2186 a.blocking_write_all(&rest[..len]).unwrap();
2187 rest = &rest[len..];
2188 step = (step * 7 + 13) % 9_001 + 1;
2189 }
2190 a.finish().unwrap();
2191 });
2192 let mut out = Vec::new();
2193 let mut step = 3_usize;
2194 loop {
2195 let chunk = b.blocking_read_chunk(step).unwrap();
2196 if chunk.is_empty() {
2197 break;
2198 }
2199 out.extend_from_slice(&chunk);
2200 step = (step * 5 + 11) % 4_099 + 1;
2201 }
2202 writer.join().unwrap();
2203 assert_eq!(out, expected);
2204 }
2205}