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, SIDE_CLAIMED, SIDE_FREE,
56 SIDE_RELEASED, SLOT_BITS, SLOT_EMPTY, SLOT_LIVE, SLOT_MASK, Slot
57};
58use orbit_core::NodeId;
59#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
60pub use orbit_core::readiness::Readiness;
61use table::Table;
62pub use table::{segment_size, segment_size_for};
63use wake::Interest;
64
65pub const STREAM_KIND: u8 = 246;
68pub const STREAM_LANE_CAPACITY: usize =
76 orbit_core::compile::usize_from_env(option_env!("ORBIT_STREAM_LANE_CAPACITY"), 256);
77pub const STREAM_BUFFER_BYTES: usize =
84 orbit_core::compile::usize_from_env(option_env!("ORBIT_STREAM_BUFFER_BYTES"), 64 * 1024);
85
86#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
98pub struct StreamSpec {
99 pub kind: u8,
101 pub lane_capacity: usize,
104 pub buffer_bytes: usize
106}
107
108impl StreamSpec {
109 pub const DEFAULT: Self = Self::new(STREAM_KIND, STREAM_LANE_CAPACITY, STREAM_BUFFER_BYTES);
110
111 pub const fn new(
112 kind: u8,
113 lane_capacity: usize,
114 buffer_bytes: usize
115 ) -> Self {
116 Self { kind, lane_capacity, buffer_bytes }
117 }
118
119 pub const fn lane_bytes(&self) -> usize {
123 self.lane_capacity * 2 * self.buffer_bytes
124 }
125
126 fn validate(self) -> Result<()> {
129 if self.lane_capacity == 0
130 || self.buffer_bytes == 0
131 || !self.lane_capacity.is_power_of_two()
132 || !self.buffer_bytes.is_power_of_two()
133 || self.lane_capacity > 1 << SLOT_BITS
134 || self.buffer_bytes > u32::MAX as usize
135 {
136 return Err(Error::Malformed(format!(
137 "stream spec kind={} lane_capacity={} buffer_bytes={}: both are powers of two, a lane holds at most {} streams and a ring at most {} bytes",
138 self.kind,
139 self.lane_capacity,
140 self.buffer_bytes,
141 1_usize << SLOT_BITS,
142 u32::MAX
143 )));
144 }
145 Ok(())
146 }
147}
148
149impl Default for StreamSpec {
150 fn default() -> Self {
151 Self::DEFAULT
152 }
153}
154
155pub type Result<T, E = Error> = std::result::Result<T, E>;
156
157#[non_exhaustive]
161#[derive(Debug)]
162pub enum Error {
163 Stale(StreamId),
165 AlreadyClaimed(Ticket),
167 Full {
169 capacity: usize
170 },
171 PayloadFull {
173 requested_slots: usize
174 },
175 PayloadTooLarge {
177 len: usize,
178 capacity: usize
179 },
180 Malformed(String),
182 Closed,
184 Reset,
186 PeerGone,
188 WouldBlock,
190 Io(io::Error)
191}
192
193impl fmt::Display for Error {
194 fn fmt(
195 &self,
196 f: &mut fmt::Formatter<'_>
197 ) -> fmt::Result {
198 match self {
199 Self::Stale(id) => write!(f, "stream {id} has ended"),
200 Self::AlreadyClaimed(ticket) => write!(f, "stream side {ticket} is already held"),
201 Self::Full { capacity } => write!(f, "stream lane is full: capacity={capacity}"),
202 Self::PayloadFull { requested_slots } => {
203 write!(f, "payload arena has no run of {requested_slots} free slots")
204 }
205 Self::PayloadTooLarge { len, capacity } => {
206 write!(f, "payload chunk is {len} bytes; arena lane capacity is {capacity}")
207 }
208 Self::Malformed(text) => write!(f, "not a stream address: {text:?}"),
209 Self::Closed => f.write_str("stream write half is shut down"),
210 Self::Reset => f.write_str("stream direction was reset by the peer"),
211 Self::PeerGone => f.write_str("stream peer reader is gone"),
212 Self::WouldBlock => f.write_str("stream operation would block"),
213 Self::Io(error) => write!(f, "Orbit stream io error: {error}")
214 }
215 }
216}
217
218impl std::error::Error for Error {
219 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
220 match self {
221 Self::Io(error) => Some(error),
222 _ => None
223 }
224 }
225}
226
227impl From<io::Error> for Error {
228 fn from(value: io::Error) -> Self {
229 Self::Io(value)
230 }
231}
232
233impl From<Error> for io::Error {
234 fn from(value: Error) -> Self {
235 let kind = match &value {
236 Error::Stale(_) | Error::AlreadyClaimed(_) => io::ErrorKind::NotConnected,
237 Error::Full { .. } | Error::PayloadFull { .. } | Error::PayloadTooLarge { .. } => {
238 io::ErrorKind::OutOfMemory
239 }
240 Error::Malformed(_) => io::ErrorKind::InvalidInput,
241 Error::Closed => io::ErrorKind::BrokenPipe,
242 Error::Reset => io::ErrorKind::ConnectionReset,
243 Error::PeerGone => io::ErrorKind::BrokenPipe,
244 Error::WouldBlock => io::ErrorKind::WouldBlock,
245 Error::Io(error) => error.kind()
246 };
247 io::Error::new(kind, value)
248 }
249}
250
251#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
254pub struct StreamId(NetId64);
255
256impl StreamId {
257 pub const fn from_net_id(id: NetId64) -> Self {
258 Self(id)
259 }
260
261 pub const fn net_id(self) -> NetId64 {
262 self.0
263 }
264
265 pub const fn kind(self) -> u8 {
266 self.0.kind()
267 }
268
269 pub const fn node(self) -> u16 {
271 self.0.node()
272 }
273
274 pub const fn slot(self) -> u32 {
275 (self.0.counter() & SLOT_MASK) as u32
276 }
277
278 pub const fn generation(self) -> u32 {
279 (self.0.counter() >> SLOT_BITS) as u32
280 }
281
282 fn make(
283 kind: u8,
284 node: u16,
285 slot: u32,
286 generation: u32
287 ) -> Self {
288 Self(NetId64::make(
289 kind,
290 node,
291 ((generation as u64) << SLOT_BITS) | (slot as u64 & SLOT_MASK)
292 ))
293 }
294}
295
296impl fmt::Display for StreamId {
297 fn fmt(
298 &self,
299 f: &mut fmt::Formatter<'_>
300 ) -> fmt::Result {
301 self.0.fmt(f)
302 }
303}
304
305impl FromStr for StreamId {
306 type Err = Error;
307
308 fn from_str(text: &str) -> Result<Self> {
309 text.parse::<NetId64>().map(Self).map_err(|_| Error::Malformed(text.to_owned()))
310 }
311}
312
313#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
315pub enum Side {
316 A,
317 B
318}
319
320impl Side {
321 const fn index(self) -> usize {
322 match self {
323 Self::A => 0,
324 Self::B => 1
325 }
326 }
327
328 const fn write_direction(self) -> usize {
330 self.index()
331 }
332
333 const fn read_direction(self) -> usize {
335 1 - self.index()
336 }
337
338 const fn letter(self) -> char {
339 match self {
340 Self::A => 'a',
341 Self::B => 'b'
342 }
343 }
344}
345
346#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
355pub struct Ticket {
356 pub id: StreamId,
357 pub side: Side,
358 pub epoch: u64
359}
360
361impl fmt::Display for Ticket {
362 fn fmt(
363 &self,
364 f: &mut fmt::Formatter<'_>
365 ) -> fmt::Result {
366 write!(f, "{}/{}/{}", self.id, self.side.letter(), self.epoch)
367 }
368}
369
370impl FromStr for Ticket {
371 type Err = Error;
372
373 fn from_str(text: &str) -> Result<Self> {
374 let malformed = || Error::Malformed(text.to_owned());
375 let (rest, epoch) = text.rsplit_once('/').ok_or_else(malformed)?;
376 let (id, side) = rest.rsplit_once('/').ok_or_else(malformed)?;
377 let side = match side {
378 "a" => Side::A,
379 "b" => Side::B,
380 _ => return Err(malformed())
381 };
382 Ok(Self {
383 id: id.parse().map_err(|_| malformed())?,
384 side,
385 epoch: epoch.parse().map_err(|_| malformed())?
386 })
387 }
388}
389
390#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
396pub struct Incarnation(u64);
397
398impl Incarnation {
399 pub const fn new(value: u64) -> Self {
400 Self(value)
401 }
402
403 pub const fn get(self) -> u64 {
404 self.0
405 }
406}
407
408#[derive(Clone)]
411pub struct Streams {
412 table: Arc<Table>
413}
414
415impl Streams {
416 pub fn new(
420 fleet: Arc<Fleet>,
421 incarnation: Incarnation
422 ) -> Result<Self> {
423 Self::with_spec(fleet, incarnation, StreamSpec::DEFAULT)
424 }
425
426 pub fn with_spec(
431 fleet: Arc<Fleet>,
432 incarnation: Incarnation,
433 spec: StreamSpec
434 ) -> Result<Self> {
435 spec.validate()?;
436 Ok(Self { table: table::open(&fleet, incarnation, spec)? })
437 }
438
439 pub fn kind(&self) -> u8 {
441 self.table.kind()
442 }
443
444 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
456 pub fn readiness(&self) -> Result<Readiness> {
457 self.table.take_readiness()
458 }
459
460 pub fn epoch(&self) -> u64 {
462 self.table.epoch()
463 }
464
465 pub fn create(&self) -> Result<(Endpoint, Ticket)> {
468 let (index, generation) = self.table.allocate()?;
469 let id = StreamId::make(
470 self.table.kind(),
471 self.table.node(),
472 (index % self.table.geometry().lane_capacity) as u32,
473 generation
474 );
475 let handle = Arc::new(Handle { table: Arc::clone(&self.table), index, id, side: Side::A });
476 Ok((Endpoint::new(handle), Ticket { id, side: Side::B, epoch: self.table.epoch() }))
477 }
478
479 pub fn open(
481 &self,
482 ticket: Ticket
483 ) -> Result<Endpoint> {
484 let index = self.locate(ticket.id)?;
485 if ticket.epoch != self.table.epoch() {
486 return Err(Error::Stale(ticket.id));
487 }
488 let slot = &self.table.slots()[index];
489 if !slot.is(ticket.id.generation()) {
490 return Err(Error::Stale(ticket.id));
491 }
492 let claimed = &slot.claimed[ticket.side.index()];
493 if claimed
494 .compare_exchange(SIDE_FREE, SIDE_CLAIMED, Ordering::SeqCst, Ordering::SeqCst)
495 .is_err()
496 {
497 return Err(Error::AlreadyClaimed(ticket));
498 }
499 if !slot.is(ticket.id.generation()) {
502 let _ = claimed.compare_exchange(
503 SIDE_CLAIMED,
504 SIDE_FREE,
505 Ordering::SeqCst,
506 Ordering::SeqCst
507 );
508 return Err(Error::Stale(ticket.id));
509 }
510 slot.incarnation[ticket.side.index()].store(self.table.incarnation(), Ordering::Release);
511 slot.node[ticket.side.index()].store(self.table.node(), Ordering::Release);
512 for direction in &slot.directions {
519 self.table.notify(index, slot, direction, table::Ring::Nobody);
520 }
521 Ok(Endpoint::new(Arc::new(Handle {
522 table: Arc::clone(&self.table),
523 index,
524 id: ticket.id,
525 side: ticket.side
526 })))
527 }
528
529 pub fn rearm(
565 &self,
566 id: StreamId
567 ) -> Result<()> {
568 let index = self.locate(id)?;
569 let slot = &self.table.slots()[index];
570 if !slot.is(id.generation()) {
571 return Err(Error::Stale(id));
572 }
573 let far = Side::B.index();
574 match slot.claimed[far].load(Ordering::SeqCst) {
575 SIDE_RELEASED => {}
576 SIDE_CLAIMED => return Err(Error::WouldBlock),
577 _ => return Err(Error::Stale(id))
578 }
579 for direction in &slot.directions {
580 direction.clear();
581 }
582 slot.node[far].store(u16::MAX, Ordering::Relaxed);
583 slot.incarnation[far].store(0, Ordering::Relaxed);
584 slot.claimed[far]
588 .compare_exchange(SIDE_RELEASED, SIDE_FREE, Ordering::SeqCst, Ordering::SeqCst)
589 .map_err(|_| Error::Stale(id))?;
590 Ok(())
591 }
592
593 pub fn is_live(
595 &self,
596 id: StreamId
597 ) -> bool {
598 self.locate(id).map(|index| self.table.slots()[index].is(id.generation())).unwrap_or(false)
599 }
600
601 pub fn offer(
607 &self,
608 ticket: Ticket,
609 to: NodeId
610 ) -> Result<()> {
611 let index = self.locate(ticket.id)?;
612 if ticket.epoch != self.table.epoch()
613 || !self.table.slots()[index].is(ticket.id.generation())
614 {
615 return Err(Error::Stale(ticket.id));
616 }
617 self.table.offer(index, to.get())
618 }
619
620 pub fn take_offer(&self) -> Option<Ticket> {
623 while let Some(index) = self.table.take_offer() {
624 if let Some(ticket) = self.ticket_for(index) {
625 return Some(ticket);
626 }
627 }
628 None
629 }
630
631 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
635 pub fn take_offer_timeout(
636 &self,
637 timeout: Duration
638 ) -> Result<Option<Ticket>> {
639 let deadline = Instant::now() + timeout;
640 loop {
641 if let Some(ticket) = self.take_offer() {
642 return Ok(Some(ticket));
643 }
644 let left = deadline.saturating_duration_since(Instant::now());
645 if left.is_zero() {
646 return Ok(self.take_offer());
647 }
648 if !self.table.wait_offer_timeout(left)? {
649 return Ok(self.take_offer());
650 }
651 }
652 }
653
654 pub fn blocking_take_offer(&self) -> Result<Ticket> {
656 loop {
657 if let Some(ticket) = self.take_offer() {
658 return Ok(ticket);
659 }
660 self.table.wait_offer()?;
661 }
662 }
663
664 pub fn poll_take_offer(
667 &self,
668 cx: &mut std::task::Context<'_>
669 ) -> std::task::Poll<Result<Ticket>> {
670 if let Some(ticket) = self.take_offer() {
671 return std::task::Poll::Ready(Ok(ticket));
672 }
673 if let Err(error) = self.table.register_offer(cx.waker()) {
674 return std::task::Poll::Ready(Err(error));
675 }
676 match self.take_offer() {
677 Some(ticket) => std::task::Poll::Ready(Ok(ticket)),
678 None => std::task::Poll::Pending
679 }
680 }
681
682 pub fn owners(&self) -> Vec<(NodeId, Incarnation)> {
688 let mut seen: Vec<(NodeId, Incarnation)> = Vec::new();
689 for slot in self.table.slots() {
690 if slot.state.load(Ordering::Acquire) != SLOT_LIVE {
691 continue;
692 }
693 for side in 0..2 {
694 if slot.claimed[side].load(Ordering::Acquire) != SIDE_CLAIMED {
695 continue;
696 }
697 let holder = (
698 NodeId::new(slot.node[side].load(Ordering::Acquire)),
699 Incarnation::new(slot.incarnation[side].load(Ordering::Acquire))
700 );
701 if !seen.contains(&holder) {
702 seen.push(holder);
703 }
704 }
705 }
706 seen.sort_by_key(|(node, incarnation)| (node.get(), incarnation.get()));
707 seen
708 }
709
710 pub fn node_dead(
717 &self,
718 node: NodeId,
719 incarnation: Incarnation
720 ) {
721 self.table.node_dead(node.get(), incarnation.get());
722 }
723
724 pub fn supersede(&self) {
736 self.table.supersede();
737 }
738
739 pub fn reset_all(&self) {
742 self.table.reset_all();
743 }
744
745 fn ticket_for(
746 &self,
747 index: usize
748 ) -> Option<Ticket> {
749 let slot = &self.table.slots()[index];
750 let generation = slot.generation.load(Ordering::Acquire);
751 if !slot.is(generation) {
752 return None;
753 }
754 let lane_capacity = self.table.geometry().lane_capacity;
755 let id = StreamId::make(
756 self.table.kind(),
757 (index / lane_capacity) as u16,
758 (index % lane_capacity) as u32,
759 generation
760 );
761 Some(Ticket { id, side: Side::B, epoch: self.table.epoch() })
762 }
763
764 #[cfg(unix)]
767 pub fn unlink(&self) -> Result<()> {
768 self.table.unlink()
769 }
770
771 fn locate(
772 &self,
773 id: StreamId
774 ) -> Result<usize> {
775 let geometry = self.table.geometry();
776 if id.kind() != self.table.kind()
777 || usize::from(id.node()) >= geometry.fleet_capacity
778 || id.slot() as usize >= geometry.lane_capacity
779 || id.generation() == 0
780 || id.generation() > GENERATION_MASK
781 {
782 return Err(Error::Malformed(id.to_string()));
783 }
784 Ok(usize::from(id.node()) * geometry.lane_capacity + id.slot() as usize)
785 }
786}
787
788struct Handle {
790 table: Arc<Table>,
791 index: usize,
792 id: StreamId,
793 side: Side
794}
795
796impl Handle {
797 fn peer_if(
801 &self,
802 ring: bool
803 ) -> table::Ring {
804 if ring { table::Ring::Side(1 - self.side.index()) } else { table::Ring::Nobody }
805 }
806
807 fn slot(&self) -> Result<&Slot> {
808 let slot = &self.table.slots()[self.index];
809 if slot.is(self.id.generation()) { Ok(slot) } else { Err(Error::Stale(self.id)) }
810 }
811
812 fn direction(
813 slot: &Slot,
814 direction: usize
815 ) -> &Direction {
816 &slot.directions[direction]
817 }
818
819 fn try_write(
821 &self,
822 buf: &[u8]
823 ) -> Result<usize> {
824 let slot = self.slot()?;
825 let direction = Self::direction(slot, self.side.write_direction());
826 let flags = direction.flags();
827 if flags & FLAG_RESET != 0 {
828 return Err(Error::Reset);
829 }
830 if flags & FLAG_FIN != 0 {
831 return Err(Error::Closed);
832 }
833 if flags & FLAG_READER_GONE != 0 {
834 return Err(Error::PeerGone);
835 }
836 let buffer_bytes = self.table.geometry().buffer_bytes;
837 let head = direction.head.load(Ordering::Relaxed);
838 let tail = direction.tail.load(Ordering::Acquire);
839 let free = buffer_bytes - (head - tail) as usize;
840 if buf.is_empty() {
841 return Ok(0);
842 }
843 if free == 0 {
844 return Err(Error::WouldBlock);
845 }
846 let len = buf.len().min(free);
847 let base = self.table.buffer(self.index, self.side.write_direction());
848 let start = (head as usize) & (buffer_bytes - 1);
849 let first = len.min(buffer_bytes - start);
850 unsafe {
853 std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
854 std::ptr::copy_nonoverlapping(buf.as_ptr().add(first), base, len - first);
855 }
856 direction.head.store(head + len as u64, Ordering::SeqCst);
857 std::sync::atomic::fence(Ordering::SeqCst);
867 let drained = direction.tail.load(Ordering::SeqCst) >= head;
868 self.table.notify(self.index, slot, direction, self.peer_if(drained));
869 Ok(len)
870 }
871
872 fn try_write_exact(
874 &self,
875 buf: &[u8]
876 ) -> Result<()> {
877 let slot = self.slot()?;
878 let direction = Self::direction(slot, self.side.write_direction());
879 let flags = direction.flags();
880 if flags & FLAG_RESET != 0 {
881 return Err(Error::Reset);
882 }
883 if flags & FLAG_FIN != 0 {
884 return Err(Error::Closed);
885 }
886 if flags & FLAG_READER_GONE != 0 {
887 return Err(Error::PeerGone);
888 }
889 let buffer_bytes = self.table.geometry().buffer_bytes;
890 if buf.len() > buffer_bytes {
891 return Err(Error::Malformed(format!(
892 "atomic stream write is {} bytes; ring capacity is {buffer_bytes}",
893 buf.len()
894 )));
895 }
896 if buf.is_empty() {
897 return Ok(());
898 }
899 let head = direction.head.load(Ordering::Relaxed);
900 let tail = direction.tail.load(Ordering::Acquire);
901 if buffer_bytes - ((head - tail) as usize) < buf.len() {
902 return Err(Error::WouldBlock);
903 }
904 let base = self.table.buffer(self.index, self.side.write_direction());
905 let start = (head as usize) & (buffer_bytes - 1);
906 let first = buf.len().min(buffer_bytes - start);
907 unsafe {
909 std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
910 std::ptr::copy_nonoverlapping(buf.as_ptr().add(first), base, buf.len() - first);
911 }
912 direction.head.store(head + buf.len() as u64, Ordering::SeqCst);
913 std::sync::atomic::fence(Ordering::SeqCst);
914 let drained = direction.tail.load(Ordering::SeqCst) >= head;
915 self.table.notify(self.index, slot, direction, self.peer_if(drained));
916 Ok(())
917 }
918
919 fn try_read(
922 &self,
923 buf: &mut [u8]
924 ) -> Result<usize> {
925 let slot = self.slot()?;
926 let direction = Self::direction(slot, self.side.read_direction());
927 let flags = direction.flags();
928 if flags & FLAG_RESET != 0 {
929 return Err(Error::Reset);
930 }
931 let head = direction.head.load(Ordering::Acquire);
932 let tail = direction.tail.load(Ordering::Relaxed);
933 let available = (head - tail) as usize;
934 if buf.is_empty() {
935 return Ok(0);
936 }
937 if available == 0 {
938 if flags & FLAG_FIN != 0 {
939 return Ok(0);
940 }
941 return Err(Error::WouldBlock);
942 }
943 let len = buf.len().min(available);
944 let base = self.table.buffer(self.index, self.side.read_direction());
945 let buffer_bytes = self.table.geometry().buffer_bytes;
946 let start = (tail as usize) & (buffer_bytes - 1);
947 let first = len.min(buffer_bytes - start);
948 unsafe {
952 std::ptr::copy_nonoverlapping(base.add(start), buf.as_mut_ptr(), first);
953 std::ptr::copy_nonoverlapping(base, buf.as_mut_ptr().add(first), len - first);
954 }
955 direction.tail.store(tail + len as u64, Ordering::SeqCst);
956 std::sync::atomic::fence(Ordering::SeqCst);
960 let head_now = direction.head.load(Ordering::SeqCst);
961 let was_full = head_now - tail >= buffer_bytes as u64;
962 self.table.notify(self.index, slot, direction, self.peer_if(was_full));
963 Ok(len)
964 }
965
966 fn try_read_exact(
968 &self,
969 buf: &mut [u8]
970 ) -> Result<()> {
971 let slot = self.slot()?;
972 let direction = Self::direction(slot, self.side.read_direction());
973 let flags = direction.flags();
974 if flags & FLAG_RESET != 0 {
975 return Err(Error::Reset);
976 }
977 let buffer_bytes = self.table.geometry().buffer_bytes;
978 if buf.len() > buffer_bytes {
979 return Err(Error::Malformed(format!(
980 "atomic stream read is {} bytes; ring capacity is {buffer_bytes}",
981 buf.len()
982 )));
983 }
984 if buf.is_empty() {
985 return Ok(());
986 }
987 let head = direction.head.load(Ordering::Acquire);
988 let tail = direction.tail.load(Ordering::Relaxed);
989 let available = (head - tail) as usize;
990 if available < buf.len() {
991 if flags & FLAG_FIN != 0 {
992 return Err(Error::Malformed(format!(
993 "stream ended with {available} bytes of a {} byte frame",
994 buf.len()
995 )));
996 }
997 return Err(Error::WouldBlock);
998 }
999 let base = self.table.buffer(self.index, self.side.read_direction());
1000 let start = (tail as usize) & (buffer_bytes - 1);
1001 let first = buf.len().min(buffer_bytes - start);
1002 unsafe {
1004 std::ptr::copy_nonoverlapping(base.add(start), buf.as_mut_ptr(), first);
1005 std::ptr::copy_nonoverlapping(base, buf.as_mut_ptr().add(first), buf.len() - first);
1006 }
1007 direction.tail.store(tail + buf.len() as u64, Ordering::SeqCst);
1008 std::sync::atomic::fence(Ordering::SeqCst);
1009 let head_now = direction.head.load(Ordering::SeqCst);
1010 let was_full = head_now - tail >= buffer_bytes as u64;
1011 self.table.notify(self.index, slot, direction, self.peer_if(was_full));
1012 Ok(())
1013 }
1014
1015 fn set_flag(
1016 &self,
1017 direction_index: usize,
1018 flag: u8
1019 ) {
1020 if let Ok(slot) = self.slot() {
1021 let direction = Self::direction(slot, direction_index);
1022 let before = direction.flags.fetch_or(flag, Ordering::SeqCst);
1023 if before & flag == 0 {
1024 let ring = flag != FLAG_READER_GONE || writer_may_park(direction, before);
1025 self.table.notify(self.index, slot, direction, self.peer_if(ring));
1026 }
1027 }
1028 }
1029
1030 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1033 fn wait_until_timeout(
1034 &self,
1035 direction_index: usize,
1036 ready: impl Fn(&Direction) -> bool,
1037 timeout: Duration
1038 ) -> Result<bool> {
1039 let deadline = Instant::now() + timeout;
1040 loop {
1041 let slot = self.slot()?;
1042 let direction = Self::direction(slot, direction_index);
1043 if ready(direction) {
1044 return Ok(true);
1045 }
1046 let left = deadline.saturating_duration_since(Instant::now());
1047 if left.is_zero() {
1048 return Ok(false);
1049 }
1050 direction.waiters.fetch_add(1, Ordering::SeqCst);
1051 let since = direction.changes.load(Ordering::SeqCst);
1052 let outcome = if ready(direction) {
1053 Ok(true)
1054 } else {
1055 wait_on_timeout(&direction.changes, since, left)
1056 };
1057 direction.waiters.fetch_sub(1, Ordering::SeqCst);
1058 if !outcome? {
1059 let slot = self.slot()?;
1062 return Ok(ready(Self::direction(slot, direction_index)));
1063 }
1064 }
1065 }
1066
1067 fn wait_until(
1068 &self,
1069 direction_index: usize,
1070 ready: impl Fn(&Direction) -> bool
1071 ) -> Result<()> {
1072 loop {
1073 let slot = self.slot()?;
1074 let direction = Self::direction(slot, direction_index);
1075 if ready(direction) {
1076 return Ok(());
1077 }
1078 direction.waiters.fetch_add(1, Ordering::SeqCst);
1079 let since = direction.changes.load(Ordering::SeqCst);
1080 let outcome =
1081 if ready(direction) { Ok(()) } else { wait_on(&direction.changes, since) };
1082 direction.waiters.fetch_sub(1, Ordering::SeqCst);
1083 outcome?;
1084 }
1085 }
1086
1087 fn poll_ready(
1090 &self,
1091 interest: Interest,
1092 ready: impl Fn(&Direction) -> bool,
1093 cx: &mut std::task::Context<'_>
1094 ) -> std::task::Poll<Result<()>> {
1095 let slot = match self.slot() {
1096 Ok(slot) => slot,
1097 Err(error) => return std::task::Poll::Ready(Err(error))
1098 };
1099 let direction = Self::direction(slot, interest.direction);
1100 if ready(direction) {
1101 return std::task::Poll::Ready(Ok(()));
1102 }
1103 if let Err(error) = self.table.register(self.index, interest, cx.waker()) {
1104 return std::task::Poll::Ready(Err(error));
1105 }
1106 std::sync::atomic::fence(Ordering::SeqCst);
1109 if ready(direction) { std::task::Poll::Ready(Ok(())) } else { std::task::Poll::Pending }
1110 }
1111}
1112
1113fn writer_may_park(
1127 direction: &Direction,
1128 before: u8
1129) -> bool {
1130 if before & (FLAG_FIN | FLAG_RESET) != 0 {
1131 return false;
1132 }
1133 std::sync::atomic::fence(Ordering::SeqCst);
1134 direction.head.load(Ordering::SeqCst) != direction.tail.load(Ordering::SeqCst)
1135}
1136
1137fn readable(direction: &Direction) -> bool {
1138 direction.head.load(Ordering::Acquire) != direction.tail.load(Ordering::Relaxed)
1139 || direction.flags() & (FLAG_FIN | FLAG_RESET) != 0
1140}
1141
1142fn writable(
1143 direction: &Direction,
1144 buffer_bytes: usize
1145) -> bool {
1146 let queued =
1147 (direction.head.load(Ordering::Relaxed) - direction.tail.load(Ordering::Acquire)) as usize;
1148 queued < buffer_bytes || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1149}
1150
1151fn writable_exact(
1152 direction: &Direction,
1153 buffer_bytes: usize,
1154 needed: usize
1155) -> bool {
1156 let queued =
1157 (direction.head.load(Ordering::Relaxed) - direction.tail.load(Ordering::Acquire)) as usize;
1158 buffer_bytes.saturating_sub(queued) >= needed
1159 || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1160}
1161
1162impl Drop for Handle {
1163 fn drop(&mut self) {
1167 let slot = &self.table.slots()[self.index];
1168 if !slot.is(self.id.generation()) {
1169 return;
1170 }
1171 if slot.claimed[self.side.index()]
1174 .compare_exchange(SIDE_CLAIMED, SIDE_RELEASED, Ordering::SeqCst, Ordering::SeqCst)
1175 .is_err()
1176 {
1177 return;
1178 }
1179 let other = slot.claimed[1 - self.side.index()].load(Ordering::SeqCst);
1183 if other != SIDE_CLAIMED {
1184 let _ = slot.state.compare_exchange(
1185 SLOT_LIVE,
1186 SLOT_EMPTY,
1187 Ordering::SeqCst,
1188 Ordering::SeqCst
1189 );
1190 for direction in &slot.directions {
1191 direction.changes.fetch_add(1, Ordering::SeqCst);
1194 if direction.waiters.load(Ordering::SeqCst) > 0 {
1195 wake_on(&direction.changes);
1196 }
1197 }
1198 self.table.registry.wake(self.index);
1199 }
1200 }
1201}
1202
1203pub struct Endpoint {
1207 read: ReadHalf,
1208 write: WriteHalf
1209}
1210
1211impl Endpoint {
1212 fn new(handle: Arc<Handle>) -> Self {
1213 Self {
1214 read: ReadHalf { handle: Arc::clone(&handle) },
1215 write: WriteHalf { handle, shut: false }
1216 }
1217 }
1218
1219 pub fn id(&self) -> StreamId {
1220 self.read.handle.id
1221 }
1222
1223 pub fn side(&self) -> Side {
1224 self.read.handle.side
1225 }
1226
1227 pub fn split(self) -> (ReadHalf, WriteHalf) {
1228 (self.read, self.write)
1229 }
1230
1231 pub fn rejoin(
1242 read: ReadHalf,
1243 write: WriteHalf
1244 ) -> Result<Self> {
1245 if read.id() != write.id() {
1246 return Err(Error::Stale(read.id()));
1247 }
1248 Ok(Self { read, write })
1249 }
1250
1251 pub fn rearm(
1264 &mut self,
1265 streams: &Streams
1266 ) -> Result<()> {
1267 if self.side() != Side::A {
1268 return Err(Error::Malformed(
1269 "only the creator side of a stream can be rearmed".to_owned()
1270 ));
1271 }
1272 streams.rearm(self.id())?;
1273 self.write.shut = false;
1274 Ok(())
1275 }
1276
1277 pub fn try_read(
1278 &self,
1279 buf: &mut [u8]
1280 ) -> Result<usize> {
1281 self.read.try_read(buf)
1282 }
1283
1284 pub fn try_read_exact(
1285 &self,
1286 buf: &mut [u8]
1287 ) -> Result<()> {
1288 self.read.try_read_exact(buf)
1289 }
1290
1291 pub fn blocking_read(
1292 &self,
1293 buf: &mut [u8]
1294 ) -> Result<usize> {
1295 self.read.blocking_read(buf)
1296 }
1297
1298 pub fn blocking_read_chunk(
1299 &self,
1300 max: usize
1301 ) -> Result<Bytes> {
1302 self.read.blocking_read_chunk(max)
1303 }
1304
1305 pub fn try_write(
1306 &self,
1307 buf: &[u8]
1308 ) -> Result<usize> {
1309 self.write.try_write(buf)
1310 }
1311
1312 pub fn try_write_exact(
1313 &self,
1314 buf: &[u8]
1315 ) -> Result<()> {
1316 self.write.try_write_exact(buf)
1317 }
1318
1319 pub fn blocking_write(
1320 &self,
1321 buf: &[u8]
1322 ) -> Result<usize> {
1323 self.write.blocking_write(buf)
1324 }
1325
1326 pub fn blocking_write_all(
1327 &self,
1328 buf: &[u8]
1329 ) -> Result<()> {
1330 self.write.blocking_write_all(buf)
1331 }
1332
1333 pub fn finish(&mut self) -> Result<()> {
1334 self.write.finish()
1335 }
1336
1337 pub fn reset(&mut self) {
1338 self.write.reset()
1339 }
1340}
1341
1342pub struct ReadHalf {
1345 handle: Arc<Handle>
1346}
1347
1348impl ReadHalf {
1349 pub fn id(&self) -> StreamId {
1350 self.handle.id
1351 }
1352
1353 pub fn try_read(
1356 &self,
1357 buf: &mut [u8]
1358 ) -> Result<usize> {
1359 self.handle.try_read(buf)
1360 }
1361
1362 pub fn try_read_exact(
1365 &self,
1366 buf: &mut [u8]
1367 ) -> Result<()> {
1368 self.handle.try_read_exact(buf)
1369 }
1370
1371 pub fn blocking_read(
1374 &self,
1375 buf: &mut [u8]
1376 ) -> Result<usize> {
1377 loop {
1378 match self.handle.try_read(buf) {
1379 Err(Error::WouldBlock) => self.wait_readable()?,
1380 other => return other
1381 }
1382 }
1383 }
1384
1385 pub fn stop_reading(&self) {
1393 self.handle.set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1394 }
1395
1396 pub fn blocking_read_chunk(
1398 &self,
1399 max: usize
1400 ) -> Result<Bytes> {
1401 let mut chunk = BytesMut::zeroed(max);
1402 let len = self.blocking_read(&mut chunk)?;
1403 chunk.truncate(len);
1404 Ok(chunk.freeze())
1405 }
1406
1407 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1409 pub fn wait_readable_timeout(
1410 &self,
1411 timeout: Duration
1412 ) -> Result<bool> {
1413 self.handle.wait_until_timeout(self.handle.side.read_direction(), readable, timeout)
1414 }
1415
1416 pub fn wait_readable(&self) -> Result<()> {
1418 self.handle.wait_until(self.handle.side.read_direction(), readable)
1419 }
1420
1421 pub fn poll_readable(
1426 &self,
1427 cx: &mut std::task::Context<'_>
1428 ) -> std::task::Poll<Result<()>> {
1429 self.handle.poll_ready(
1430 Interest { direction: self.handle.side.read_direction(), writer: false },
1431 readable,
1432 cx
1433 )
1434 }
1435}
1436
1437impl Drop for ReadHalf {
1438 fn drop(&mut self) {
1439 self.handle.set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1440 }
1441}
1442
1443pub struct WriteHalf {
1446 handle: Arc<Handle>,
1447 shut: bool
1448}
1449
1450impl WriteHalf {
1451 pub fn id(&self) -> StreamId {
1452 self.handle.id
1453 }
1454
1455 pub fn try_write(
1457 &self,
1458 buf: &[u8]
1459 ) -> Result<usize> {
1460 self.handle.try_write(buf)
1461 }
1462
1463 pub fn try_write_exact(
1466 &self,
1467 buf: &[u8]
1468 ) -> Result<()> {
1469 self.handle.try_write_exact(buf)
1470 }
1471
1472 pub fn blocking_write(
1474 &self,
1475 buf: &[u8]
1476 ) -> Result<usize> {
1477 loop {
1478 match self.handle.try_write(buf) {
1479 Err(Error::WouldBlock) => self.wait_writable()?,
1480 other => return other
1481 }
1482 }
1483 }
1484
1485 pub fn blocking_write_all(
1488 &self,
1489 mut buf: &[u8]
1490 ) -> Result<()> {
1491 while !buf.is_empty() {
1492 let written = self.blocking_write(buf)?;
1493 buf = &buf[written..];
1494 }
1495 Ok(())
1496 }
1497
1498 pub fn finish(&mut self) -> Result<()> {
1502 self.shut = true;
1503 self.handle.set_flag(self.handle.side.write_direction(), FLAG_FIN);
1504 Ok(())
1505 }
1506
1507 pub fn reset(&mut self) {
1510 self.shut = true;
1511 self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1512 }
1513
1514 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1518 pub fn wait_writable_timeout(
1519 &self,
1520 timeout: Duration
1521 ) -> Result<bool> {
1522 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1523 self.handle.wait_until_timeout(
1524 self.handle.side.write_direction(),
1525 |direction| writable(direction, buffer_bytes),
1526 timeout
1527 )
1528 }
1529
1530 pub fn wait_writable(&self) -> Result<()> {
1532 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1533 self.handle.wait_until(self.handle.side.write_direction(), |direction| {
1534 writable(direction, buffer_bytes)
1535 })
1536 }
1537
1538 pub fn wait_writable_exact(
1540 &self,
1541 needed: usize
1542 ) -> Result<()> {
1543 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1544 if needed > buffer_bytes {
1545 return Err(Error::Malformed(format!(
1546 "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1547 )));
1548 }
1549 self.handle.wait_until(self.handle.side.write_direction(), |direction| {
1550 writable_exact(direction, buffer_bytes, needed)
1551 })
1552 }
1553
1554 pub fn poll_writable(
1559 &self,
1560 cx: &mut std::task::Context<'_>
1561 ) -> std::task::Poll<Result<()>> {
1562 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1563 self.handle.poll_ready(
1564 Interest { direction: self.handle.side.write_direction(), writer: true },
1565 |direction| writable(direction, buffer_bytes),
1566 cx
1567 )
1568 }
1569
1570 pub fn poll_writable_exact(
1572 &self,
1573 needed: usize,
1574 cx: &mut std::task::Context<'_>
1575 ) -> std::task::Poll<Result<()>> {
1576 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1577 if needed > buffer_bytes {
1578 return std::task::Poll::Ready(Err(Error::Malformed(format!(
1579 "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1580 ))));
1581 }
1582 self.handle.poll_ready(
1583 Interest { direction: self.handle.side.write_direction(), writer: true },
1584 |direction| writable_exact(direction, buffer_bytes, needed),
1585 cx
1586 )
1587 }
1588}
1589
1590impl Drop for WriteHalf {
1591 fn drop(&mut self) {
1592 if !self.shut {
1593 self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1594 }
1595 }
1596}
1597
1598#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1602pub(crate) fn waits_supported() -> bool {
1603 orbit_core::sync::supported()
1604}
1605
1606#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1607pub(crate) fn waits_supported() -> bool {
1608 false
1609}
1610
1611#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1615#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1618pub(crate) fn wait_on_timeout(
1619 word: &AtomicU32,
1620 expected: u32,
1621 timeout: Duration
1622) -> Result<bool> {
1623 orbit_core::sync::wait_word_timeout(word, expected, timeout).map_err(Error::Io)
1624}
1625
1626pub(crate) fn wait_on(
1627 word: &AtomicU32,
1628 expected: u32
1629) -> Result<()> {
1630 orbit_core::sync::wait_word(word, expected).map_err(Error::Io)
1631}
1632
1633#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1634pub(crate) fn wait_on(
1635 _word: &AtomicU32,
1636 _expected: u32
1637) -> Result<()> {
1638 Err(Error::Io(std::io::Error::new(
1639 std::io::ErrorKind::Unsupported,
1640 "orbit-stream needs a platform that can wait on a shared word"
1641 )))
1642}
1643
1644pub(crate) fn wake_on(word: &AtomicU32) {
1646 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1647 let _ = orbit_core::sync::wake_word(word);
1648 #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1649 let _ = word;
1650}
1651
1652pub(crate) fn lock_unpoisoned<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
1653 mutex.lock().unwrap_or_else(|error| error.into_inner())
1654}
1655
1656#[cfg(test)]
1657mod tests {
1658 use std::sync::Arc;
1659 use std::time::Duration;
1660
1661 use orbit_core::{Fleet, NodeId};
1662
1663 use super::{
1664 Error, Incarnation, STREAM_BUFFER_BYTES, STREAM_LANE_CAPACITY, Side, Streams, Ticket
1665 };
1666
1667 fn streams(name: &'static str) -> Streams {
1668 Streams::new(Arc::new(Fleet::join(name, 2).unwrap()), Incarnation::new(1)).unwrap()
1669 }
1670
1671 #[test]
1672 fn bytes_cross_in_order_and_end_cleanly() {
1673 let streams = streams("stream-order");
1674 let (mut a, ticket) = streams.create().unwrap();
1675 assert_eq!(ticket.side, Side::B);
1676 assert_eq!(ticket.id.node(), 0);
1677 assert_eq!(ticket.epoch, streams.epoch());
1678 let b = streams.open(ticket).unwrap();
1679
1680 a.blocking_write_all(b"hello, ").unwrap();
1681 a.blocking_write_all(b"world").unwrap();
1682 a.finish().unwrap();
1683
1684 let mut out = Vec::new();
1685 let mut buf = [0_u8; 4];
1686 loop {
1687 let n = b.blocking_read(&mut buf).unwrap();
1688 if n == 0 {
1689 break;
1690 }
1691 out.extend_from_slice(&buf[..n]);
1692 }
1693 assert_eq!(out, b"hello, world");
1694 b.blocking_write_all(b"back").unwrap();
1696 assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"back");
1697 }
1698
1699 #[test]
1700 fn a_ticket_prints_and_parses() {
1701 let streams = streams("stream-ticket");
1702 let (_a, ticket) = streams.create().unwrap();
1703 let text = ticket.to_string();
1704 assert!(text.contains("/b/"), "{text}");
1705 assert_eq!(text.parse::<Ticket>().unwrap(), ticket);
1706 assert!(matches!("nope".parse::<Ticket>(), Err(Error::Malformed(_))));
1707 assert!(matches!("x/b".parse::<Ticket>(), Err(Error::Malformed(_))));
1708 }
1709
1710 #[test]
1711 fn a_side_is_held_once_and_a_released_stream_goes_stale() {
1712 let streams = streams("stream-claim");
1713 let (a, ticket) = streams.create().unwrap();
1714 let b = streams.open(ticket).unwrap();
1715 assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_))));
1716 assert!(streams.is_live(ticket.id));
1717
1718 drop(a);
1719 let mut buf = [0_u8; 1];
1721 assert!(matches!(b.blocking_read(&mut buf), Err(Error::Reset)));
1722 drop(b);
1723 assert!(!streams.is_live(ticket.id));
1724 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1725
1726 for _ in 0..STREAM_LANE_CAPACITY - 1 {
1729 drop(streams.create().unwrap());
1730 }
1731 let (_a2, ticket2) = streams.create().unwrap();
1732 assert_eq!(ticket2.id.slot(), ticket.id.slot());
1733 assert_ne!(ticket2.id.generation(), ticket.id.generation());
1734 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1735 }
1736
1737 #[test]
1738 fn a_reset_starts_a_new_epoch_and_refuses_old_tickets() {
1739 let streams = streams("stream-epoch");
1740 let (_a, ticket) = streams.create().unwrap();
1741 let before = streams.epoch();
1742 streams.reset_all();
1743 assert!(streams.epoch() > before);
1744 let (_a2, ticket2) = streams.create().unwrap();
1747 assert_eq!(ticket2.id, ticket.id);
1748 assert_ne!(ticket2.epoch, ticket.epoch);
1749 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1750 assert!(streams.open(ticket2).is_ok());
1751 }
1752
1753 #[test]
1754 fn a_slot_whose_generation_runs_out_is_not_reused() {
1755 let streams = streams("stream-exhaust");
1758 let limit = super::layout::GENERATION_LIMIT as usize;
1759 for _ in 0..STREAM_LANE_CAPACITY * limit {
1760 drop(streams.create().unwrap());
1761 }
1762 assert!(matches!(streams.create(), Err(Error::Full { .. })));
1763 streams.reset_all();
1764 let (_a, ticket) = streams.create().unwrap();
1765 assert_eq!(ticket.id.generation(), 1);
1766 }
1767
1768 #[test]
1769 fn a_full_ring_blocks_the_writer_and_wraps_correctly() {
1770 let streams = streams("stream-wrap");
1771 let (a, ticket) = streams.create().unwrap();
1772 let b = streams.open(ticket).unwrap();
1773
1774 let filler = vec![7_u8; STREAM_BUFFER_BYTES];
1775 assert_eq!(a.try_write(&filler).unwrap(), STREAM_BUFFER_BYTES);
1776 assert!(matches!(a.try_write(b"x"), Err(Error::WouldBlock)));
1777
1778 let mut buf = vec![0_u8; STREAM_BUFFER_BYTES - 5];
1779 assert_eq!(b.blocking_read(&mut buf).unwrap(), STREAM_BUFFER_BYTES - 5);
1780 let tail = (0..STREAM_BUFFER_BYTES).map(|i| i as u8).collect::<Vec<_>>();
1782 assert_eq!(a.try_write(&tail).unwrap(), STREAM_BUFFER_BYTES - 5);
1783
1784 let mut rest = vec![0_u8; 5];
1785 assert_eq!(b.blocking_read(&mut rest).unwrap(), 5);
1786 assert_eq!(rest, vec![7_u8; 5]);
1787 let mut wrapped = vec![0_u8; STREAM_BUFFER_BYTES];
1788 let mut got = 0;
1789 while got < STREAM_BUFFER_BYTES - 5 {
1790 got += b.blocking_read(&mut wrapped[got..]).unwrap();
1791 }
1792 assert_eq!(&wrapped[..got], &tail[..got]);
1793 }
1794
1795 #[test]
1796 fn exact_frames_never_publish_or_consume_a_prefix() {
1797 let streams = streams("stream-exact-frame");
1798 let (a, ticket) = streams.create().unwrap();
1799 let b = streams.open(ticket).unwrap();
1800
1801 let filler = vec![7_u8; STREAM_BUFFER_BYTES - 32];
1802 a.try_write_exact(&filler).unwrap();
1803 assert!(matches!(a.try_write_exact(&[9_u8; 64]), Err(Error::WouldBlock)));
1804 let mut consumed = vec![0_u8; filler.len()];
1805 b.try_read_exact(&mut consumed).unwrap();
1806 assert_eq!(consumed, filler);
1807 assert!(matches!(b.try_read(&mut [0_u8; 1]), Err(Error::WouldBlock)));
1808
1809 a.try_write_exact(&[3_u8; 32]).unwrap();
1810 assert!(matches!(b.try_read_exact(&mut [0_u8; 64]), Err(Error::WouldBlock)));
1811 let mut frame = [0_u8; 32];
1812 b.try_read_exact(&mut frame).unwrap();
1813 assert_eq!(frame, [3_u8; 32]);
1814 }
1815
1816 #[test]
1817 fn a_blocked_reader_is_woken_by_a_write_in_another_thread() {
1818 let streams = streams("stream-wake");
1819 let (a, ticket) = streams.create().unwrap();
1820 let b = streams.open(ticket).unwrap();
1821 let reader = std::thread::spawn(move || {
1822 let mut buf = [0_u8; 8];
1823 let n = b.blocking_read(&mut buf).unwrap();
1824 buf[..n].to_vec()
1825 });
1826 std::thread::sleep(Duration::from_millis(30));
1827 a.blocking_write_all(b"ping").unwrap();
1828 assert_eq!(reader.join().unwrap(), b"ping");
1829 }
1830
1831 #[test]
1832 fn a_reader_gone_fails_the_writer_and_a_lane_fills_up() {
1833 let streams = streams("stream-gone");
1834 let (a, ticket) = streams.create().unwrap();
1835 let b = streams.open(ticket).unwrap();
1836 let (b_read, _b_write) = b.split();
1837 drop(b_read);
1838 assert!(matches!(a.blocking_write(b"nobody"), Err(Error::PeerGone)));
1839
1840 let mut held = vec![a];
1841 for _ in 1..STREAM_LANE_CAPACITY {
1842 held.push(streams.create().unwrap().0);
1843 }
1844 assert!(matches!(streams.create(), Err(Error::Full { .. })));
1845 }
1846
1847 #[test]
1848 fn a_death_report_ends_that_incarnations_sides_only() {
1849 let streams = streams("stream-dead");
1850 let (a, ticket) = streams.create().unwrap();
1851 let b = streams.open(ticket).unwrap();
1852 b.blocking_write_all(b"partial").unwrap();
1853
1854 streams.node_dead(NodeId::ZERO, Incarnation::new(2));
1856 assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"partial");
1857
1858 let reader = std::thread::spawn(move || {
1864 let mut buf = [0_u8; 8];
1865 a.blocking_read(&mut buf).map(|_| ())
1866 });
1867 std::thread::sleep(Duration::from_millis(30));
1868 streams.node_dead(NodeId::ZERO, Incarnation::new(1));
1869 assert!(matches!(reader.join().unwrap(), Err(Error::Reset | Error::Stale(_))));
1870 assert!(matches!(b.try_write(b"x"), Err(Error::Reset | Error::Stale(_))));
1871 assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_) | Error::Stale(_))));
1872 drop(b);
1873 assert!(!streams.is_live(ticket.id));
1874 }
1875
1876 #[test]
1877 fn an_offer_is_taken_once_and_a_stale_offer_is_skipped() {
1878 let streams = streams("stream-offer");
1879 assert!(streams.take_offer().is_none());
1880 let (a, ticket) = streams.create().unwrap();
1881 streams.offer(ticket, NodeId::ZERO).unwrap();
1882 assert_eq!(streams.take_offer(), Some(ticket));
1883 assert!(streams.take_offer().is_none());
1884
1885 let (gone, stale) = streams.create().unwrap();
1886 streams.offer(stale, NodeId::ZERO).unwrap();
1887 drop(gone);
1888 assert!(streams.take_offer().is_none());
1889
1890 let taker = streams.clone();
1891 let waiter = std::thread::spawn(move || taker.blocking_take_offer());
1892 std::thread::sleep(Duration::from_millis(30));
1893 streams.offer(ticket, NodeId::ZERO).unwrap();
1894 assert_eq!(waiter.join().unwrap().unwrap(), ticket);
1895 drop(a);
1896 }
1897
1898 #[test]
1899 fn a_contested_claim_goes_to_exactly_one_opener() {
1900 let streams = streams("stream-race");
1901 for _ in 0..50 {
1902 let (_a, ticket) = streams.create().unwrap();
1903 let openers = (0..4)
1904 .map(|_| {
1905 let streams = streams.clone();
1906 std::thread::spawn(move || streams.open(ticket).is_ok())
1907 })
1908 .collect::<Vec<_>>();
1909 let won =
1910 openers.into_iter().map(|opener| opener.join().unwrap()).filter(|won| *won).count();
1911 assert_eq!(won, 1);
1912 }
1913 }
1914
1915 #[test]
1916 fn random_sizes_concatenate_exactly() {
1917 let streams = streams("stream-random");
1918 let (mut a, ticket) = streams.create().unwrap();
1919 let b = streams.open(ticket).unwrap();
1920 let payload =
1921 (0..300_000_u32).map(|i| (i.wrapping_mul(2654435761) >> 13) as u8).collect::<Vec<_>>();
1922 let expected = payload.clone();
1923 let writer = std::thread::spawn(move || {
1924 let mut rest = &payload[..];
1925 let mut step = 1_usize;
1926 while !rest.is_empty() {
1927 let len = step.min(rest.len());
1928 a.blocking_write_all(&rest[..len]).unwrap();
1929 rest = &rest[len..];
1930 step = (step * 7 + 13) % 9_001 + 1;
1931 }
1932 a.finish().unwrap();
1933 });
1934 let mut out = Vec::new();
1935 let mut step = 3_usize;
1936 loop {
1937 let chunk = b.blocking_read_chunk(step).unwrap();
1938 if chunk.is_empty() {
1939 break;
1940 }
1941 out.extend_from_slice(&chunk);
1942 step = (step * 5 + 11) % 4_099 + 1;
1943 }
1944 writer.join().unwrap();
1945 assert_eq!(out, expected);
1946 }
1947}