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
45pub mod exchange;
46mod layout;
47#[cfg(feature = "tokio")]
48mod poll;
49mod table;
50mod wake;
51
52use layout::{
53 Direction, FLAG_FIN, FLAG_READER_GONE, FLAG_RESET, GENERATION_MASK, SIDE_CLAIMED, SIDE_FREE,
54 SIDE_RELEASED, SLOT_BITS, SLOT_EMPTY, SLOT_LIVE, SLOT_MASK, Slot
55};
56use orbit_core::NodeId;
57#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
58pub use orbit_core::readiness::Readiness;
59use table::Table;
60pub use table::{segment_size, segment_size_for};
61use wake::Interest;
62
63pub const STREAM_KIND: u8 = 246;
66pub const STREAM_LANE_CAPACITY: usize =
74 orbit_core::compile::usize_from_env(option_env!("ORBIT_STREAM_LANE_CAPACITY"), 256);
75pub const STREAM_BUFFER_BYTES: usize =
82 orbit_core::compile::usize_from_env(option_env!("ORBIT_STREAM_BUFFER_BYTES"), 64 * 1024);
83
84#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
96pub struct StreamSpec {
97 pub kind: u8,
99 pub lane_capacity: usize,
102 pub buffer_bytes: usize
104}
105
106impl StreamSpec {
107 pub const DEFAULT: Self = Self::new(STREAM_KIND, STREAM_LANE_CAPACITY, STREAM_BUFFER_BYTES);
108
109 pub const fn new(
110 kind: u8,
111 lane_capacity: usize,
112 buffer_bytes: usize
113 ) -> Self {
114 Self { kind, lane_capacity, buffer_bytes }
115 }
116
117 pub const fn lane_bytes(&self) -> usize {
121 self.lane_capacity * 2 * self.buffer_bytes
122 }
123
124 fn validate(self) -> Result<()> {
127 if self.lane_capacity == 0
128 || self.buffer_bytes == 0
129 || !self.lane_capacity.is_power_of_two()
130 || !self.buffer_bytes.is_power_of_two()
131 || self.lane_capacity > 1 << SLOT_BITS
132 || self.buffer_bytes > u32::MAX as usize
133 {
134 return Err(Error::Malformed(format!(
135 "stream spec kind={} lane_capacity={} buffer_bytes={}: both are powers of two, a lane holds at most {} streams and a ring at most {} bytes",
136 self.kind,
137 self.lane_capacity,
138 self.buffer_bytes,
139 1_usize << SLOT_BITS,
140 u32::MAX
141 )));
142 }
143 Ok(())
144 }
145}
146
147impl Default for StreamSpec {
148 fn default() -> Self {
149 Self::DEFAULT
150 }
151}
152
153pub type Result<T, E = Error> = std::result::Result<T, E>;
154
155#[non_exhaustive]
159#[derive(Debug)]
160pub enum Error {
161 Stale(StreamId),
163 AlreadyClaimed(Ticket),
165 Full {
167 capacity: usize
168 },
169 PayloadFull {
171 requested_slots: usize
172 },
173 PayloadTooLarge {
175 len: usize,
176 capacity: usize
177 },
178 Malformed(String),
180 Closed,
182 Reset,
184 PeerGone,
186 WouldBlock,
188 Io(io::Error)
189}
190
191impl fmt::Display for Error {
192 fn fmt(
193 &self,
194 f: &mut fmt::Formatter<'_>
195 ) -> fmt::Result {
196 match self {
197 Self::Stale(id) => write!(f, "stream {id} has ended"),
198 Self::AlreadyClaimed(ticket) => write!(f, "stream side {ticket} is already held"),
199 Self::Full { capacity } => write!(f, "stream lane is full: capacity={capacity}"),
200 Self::PayloadFull { requested_slots } => {
201 write!(f, "payload arena has no run of {requested_slots} free slots")
202 }
203 Self::PayloadTooLarge { len, capacity } => {
204 write!(f, "payload chunk is {len} bytes; arena lane capacity is {capacity}")
205 }
206 Self::Malformed(text) => write!(f, "not a stream address: {text:?}"),
207 Self::Closed => f.write_str("stream write half is shut down"),
208 Self::Reset => f.write_str("stream direction was reset by the peer"),
209 Self::PeerGone => f.write_str("stream peer reader is gone"),
210 Self::WouldBlock => f.write_str("stream operation would block"),
211 Self::Io(error) => write!(f, "Orbit stream io error: {error}")
212 }
213 }
214}
215
216impl std::error::Error for Error {
217 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
218 match self {
219 Self::Io(error) => Some(error),
220 _ => None
221 }
222 }
223}
224
225impl From<io::Error> for Error {
226 fn from(value: io::Error) -> Self {
227 Self::Io(value)
228 }
229}
230
231impl From<Error> for io::Error {
232 fn from(value: Error) -> Self {
233 let kind = match &value {
234 Error::Stale(_) | Error::AlreadyClaimed(_) => io::ErrorKind::NotConnected,
235 Error::Full { .. } | Error::PayloadFull { .. } | Error::PayloadTooLarge { .. } => {
236 io::ErrorKind::OutOfMemory
237 }
238 Error::Malformed(_) => io::ErrorKind::InvalidInput,
239 Error::Closed => io::ErrorKind::BrokenPipe,
240 Error::Reset => io::ErrorKind::ConnectionReset,
241 Error::PeerGone => io::ErrorKind::BrokenPipe,
242 Error::WouldBlock => io::ErrorKind::WouldBlock,
243 Error::Io(error) => error.kind()
244 };
245 io::Error::new(kind, value)
246 }
247}
248
249#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
252pub struct StreamId(NetId64);
253
254impl StreamId {
255 pub const fn from_net_id(id: NetId64) -> Self {
256 Self(id)
257 }
258
259 pub const fn net_id(self) -> NetId64 {
260 self.0
261 }
262
263 pub const fn kind(self) -> u8 {
264 self.0.kind()
265 }
266
267 pub const fn node(self) -> u16 {
269 self.0.node()
270 }
271
272 pub const fn slot(self) -> u32 {
273 (self.0.counter() & SLOT_MASK) as u32
274 }
275
276 pub const fn generation(self) -> u32 {
277 (self.0.counter() >> SLOT_BITS) as u32
278 }
279
280 fn make(
281 kind: u8,
282 node: u16,
283 slot: u32,
284 generation: u32
285 ) -> Self {
286 Self(NetId64::make(
287 kind,
288 node,
289 ((generation as u64) << SLOT_BITS) | (slot as u64 & SLOT_MASK)
290 ))
291 }
292}
293
294impl fmt::Display for StreamId {
295 fn fmt(
296 &self,
297 f: &mut fmt::Formatter<'_>
298 ) -> fmt::Result {
299 self.0.fmt(f)
300 }
301}
302
303impl FromStr for StreamId {
304 type Err = Error;
305
306 fn from_str(text: &str) -> Result<Self> {
307 text.parse::<NetId64>().map(Self).map_err(|_| Error::Malformed(text.to_owned()))
308 }
309}
310
311#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
313pub enum Side {
314 A,
315 B
316}
317
318impl Side {
319 const fn index(self) -> usize {
320 match self {
321 Self::A => 0,
322 Self::B => 1
323 }
324 }
325
326 const fn write_direction(self) -> usize {
328 self.index()
329 }
330
331 const fn read_direction(self) -> usize {
333 1 - self.index()
334 }
335
336 const fn letter(self) -> char {
337 match self {
338 Self::A => 'a',
339 Self::B => 'b'
340 }
341 }
342}
343
344#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
353pub struct Ticket {
354 pub id: StreamId,
355 pub side: Side,
356 pub epoch: u64
357}
358
359impl fmt::Display for Ticket {
360 fn fmt(
361 &self,
362 f: &mut fmt::Formatter<'_>
363 ) -> fmt::Result {
364 write!(f, "{}/{}/{}", self.id, self.side.letter(), self.epoch)
365 }
366}
367
368impl FromStr for Ticket {
369 type Err = Error;
370
371 fn from_str(text: &str) -> Result<Self> {
372 let malformed = || Error::Malformed(text.to_owned());
373 let (rest, epoch) = text.rsplit_once('/').ok_or_else(malformed)?;
374 let (id, side) = rest.rsplit_once('/').ok_or_else(malformed)?;
375 let side = match side {
376 "a" => Side::A,
377 "b" => Side::B,
378 _ => return Err(malformed())
379 };
380 Ok(Self {
381 id: id.parse().map_err(|_| malformed())?,
382 side,
383 epoch: epoch.parse().map_err(|_| malformed())?
384 })
385 }
386}
387
388#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
394pub struct Incarnation(u64);
395
396impl Incarnation {
397 pub const fn new(value: u64) -> Self {
398 Self(value)
399 }
400
401 pub const fn get(self) -> u64 {
402 self.0
403 }
404}
405
406#[derive(Clone)]
409pub struct Streams {
410 table: Arc<Table>
411}
412
413impl Streams {
414 pub fn new(
418 fleet: Arc<Fleet>,
419 incarnation: Incarnation
420 ) -> Result<Self> {
421 Self::with_spec(fleet, incarnation, StreamSpec::DEFAULT)
422 }
423
424 pub fn with_spec(
429 fleet: Arc<Fleet>,
430 incarnation: Incarnation,
431 spec: StreamSpec
432 ) -> Result<Self> {
433 spec.validate()?;
434 Ok(Self { table: table::open(&fleet, incarnation, spec)? })
435 }
436
437 pub fn kind(&self) -> u8 {
439 self.table.kind()
440 }
441
442 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
454 pub fn readiness(&self) -> Result<Readiness> {
455 self.table.take_readiness()
456 }
457
458 pub fn epoch(&self) -> u64 {
460 self.table.epoch()
461 }
462
463 pub fn create(&self) -> Result<(Endpoint, Ticket)> {
466 let (index, generation) = self.table.allocate()?;
467 let id = StreamId::make(
468 self.table.kind(),
469 self.table.node(),
470 (index % self.table.geometry().lane_capacity) as u32,
471 generation
472 );
473 let handle = Arc::new(Handle { table: Arc::clone(&self.table), index, id, side: Side::A });
474 Ok((Endpoint::new(handle), Ticket { id, side: Side::B, epoch: self.table.epoch() }))
475 }
476
477 pub fn open(
479 &self,
480 ticket: Ticket
481 ) -> Result<Endpoint> {
482 let index = self.locate(ticket.id)?;
483 if ticket.epoch != self.table.epoch() {
484 return Err(Error::Stale(ticket.id));
485 }
486 let slot = &self.table.slots()[index];
487 if !slot.is(ticket.id.generation()) {
488 return Err(Error::Stale(ticket.id));
489 }
490 let claimed = &slot.claimed[ticket.side.index()];
491 if claimed
492 .compare_exchange(SIDE_FREE, SIDE_CLAIMED, Ordering::SeqCst, Ordering::SeqCst)
493 .is_err()
494 {
495 return Err(Error::AlreadyClaimed(ticket));
496 }
497 if !slot.is(ticket.id.generation()) {
500 let _ = claimed.compare_exchange(
501 SIDE_CLAIMED,
502 SIDE_FREE,
503 Ordering::SeqCst,
504 Ordering::SeqCst
505 );
506 return Err(Error::Stale(ticket.id));
507 }
508 slot.incarnation[ticket.side.index()].store(self.table.incarnation(), Ordering::Release);
509 slot.node[ticket.side.index()].store(self.table.node(), Ordering::Release);
510 for direction in &slot.directions {
512 self.table.notify(index, slot, direction, true);
513 }
514 Ok(Endpoint::new(Arc::new(Handle {
515 table: Arc::clone(&self.table),
516 index,
517 id: ticket.id,
518 side: ticket.side
519 })))
520 }
521
522 pub fn is_live(
524 &self,
525 id: StreamId
526 ) -> bool {
527 self.locate(id).map(|index| self.table.slots()[index].is(id.generation())).unwrap_or(false)
528 }
529
530 pub fn offer(
536 &self,
537 ticket: Ticket,
538 to: NodeId
539 ) -> Result<()> {
540 let index = self.locate(ticket.id)?;
541 if ticket.epoch != self.table.epoch()
542 || !self.table.slots()[index].is(ticket.id.generation())
543 {
544 return Err(Error::Stale(ticket.id));
545 }
546 self.table.offer(index, to.get())
547 }
548
549 pub fn take_offer(&self) -> Option<Ticket> {
552 while let Some(index) = self.table.take_offer() {
553 if let Some(ticket) = self.ticket_for(index) {
554 return Some(ticket);
555 }
556 }
557 None
558 }
559
560 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
564 pub fn take_offer_timeout(
565 &self,
566 timeout: Duration
567 ) -> Result<Option<Ticket>> {
568 let deadline = Instant::now() + timeout;
569 loop {
570 if let Some(ticket) = self.take_offer() {
571 return Ok(Some(ticket));
572 }
573 let left = deadline.saturating_duration_since(Instant::now());
574 if left.is_zero() {
575 return Ok(self.take_offer());
576 }
577 if !self.table.wait_offer_timeout(left)? {
578 return Ok(self.take_offer());
579 }
580 }
581 }
582
583 pub fn blocking_take_offer(&self) -> Result<Ticket> {
585 loop {
586 if let Some(ticket) = self.take_offer() {
587 return Ok(ticket);
588 }
589 self.table.wait_offer()?;
590 }
591 }
592
593 pub fn poll_take_offer(
596 &self,
597 cx: &mut std::task::Context<'_>
598 ) -> std::task::Poll<Result<Ticket>> {
599 if let Some(ticket) = self.take_offer() {
600 return std::task::Poll::Ready(Ok(ticket));
601 }
602 if let Err(error) = self.table.register_offer(cx.waker()) {
603 return std::task::Poll::Ready(Err(error));
604 }
605 match self.take_offer() {
606 Some(ticket) => std::task::Poll::Ready(Ok(ticket)),
607 None => std::task::Poll::Pending
608 }
609 }
610
611 pub fn owners(&self) -> Vec<(NodeId, Incarnation)> {
617 let mut seen: Vec<(NodeId, Incarnation)> = Vec::new();
618 for slot in self.table.slots() {
619 if slot.state.load(Ordering::Acquire) != SLOT_LIVE {
620 continue;
621 }
622 for side in 0..2 {
623 if slot.claimed[side].load(Ordering::Acquire) != SIDE_CLAIMED {
624 continue;
625 }
626 let holder = (
627 NodeId::new(slot.node[side].load(Ordering::Acquire)),
628 Incarnation::new(slot.incarnation[side].load(Ordering::Acquire))
629 );
630 if !seen.contains(&holder) {
631 seen.push(holder);
632 }
633 }
634 }
635 seen.sort_by_key(|(node, incarnation)| (node.get(), incarnation.get()));
636 seen
637 }
638
639 pub fn node_dead(
646 &self,
647 node: NodeId,
648 incarnation: Incarnation
649 ) {
650 self.table.node_dead(node.get(), incarnation.get());
651 }
652
653 pub fn reset_all(&self) {
656 self.table.reset_all();
657 }
658
659 fn ticket_for(
660 &self,
661 index: usize
662 ) -> Option<Ticket> {
663 let slot = &self.table.slots()[index];
664 let generation = slot.generation.load(Ordering::Acquire);
665 if !slot.is(generation) {
666 return None;
667 }
668 let lane_capacity = self.table.geometry().lane_capacity;
669 let id = StreamId::make(
670 self.table.kind(),
671 (index / lane_capacity) as u16,
672 (index % lane_capacity) as u32,
673 generation
674 );
675 Some(Ticket { id, side: Side::B, epoch: self.table.epoch() })
676 }
677
678 #[cfg(unix)]
681 pub fn unlink(&self) -> Result<()> {
682 self.table.unlink()
683 }
684
685 fn locate(
686 &self,
687 id: StreamId
688 ) -> Result<usize> {
689 let geometry = self.table.geometry();
690 if id.kind() != self.table.kind()
691 || usize::from(id.node()) >= geometry.fleet_capacity
692 || id.slot() as usize >= geometry.lane_capacity
693 || id.generation() == 0
694 || id.generation() > GENERATION_MASK
695 {
696 return Err(Error::Malformed(id.to_string()));
697 }
698 Ok(usize::from(id.node()) * geometry.lane_capacity + id.slot() as usize)
699 }
700}
701
702struct Handle {
704 table: Arc<Table>,
705 index: usize,
706 id: StreamId,
707 side: Side
708}
709
710impl Handle {
711 fn slot(&self) -> Result<&Slot> {
712 let slot = &self.table.slots()[self.index];
713 if slot.is(self.id.generation()) { Ok(slot) } else { Err(Error::Stale(self.id)) }
714 }
715
716 fn direction(
717 slot: &Slot,
718 direction: usize
719 ) -> &Direction {
720 &slot.directions[direction]
721 }
722
723 fn try_write(
725 &self,
726 buf: &[u8]
727 ) -> Result<usize> {
728 let slot = self.slot()?;
729 let direction = Self::direction(slot, self.side.write_direction());
730 let flags = direction.flags();
731 if flags & FLAG_RESET != 0 {
732 return Err(Error::Reset);
733 }
734 if flags & FLAG_FIN != 0 {
735 return Err(Error::Closed);
736 }
737 if flags & FLAG_READER_GONE != 0 {
738 return Err(Error::PeerGone);
739 }
740 let buffer_bytes = self.table.geometry().buffer_bytes;
741 let head = direction.head.load(Ordering::Relaxed);
742 let tail = direction.tail.load(Ordering::Acquire);
743 let free = buffer_bytes - (head - tail) as usize;
744 if buf.is_empty() {
745 return Ok(0);
746 }
747 if free == 0 {
748 return Err(Error::WouldBlock);
749 }
750 let len = buf.len().min(free);
751 let base = self.table.buffer(self.index, self.side.write_direction());
752 let start = (head as usize) & (buffer_bytes - 1);
753 let first = len.min(buffer_bytes - start);
754 unsafe {
757 std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
758 std::ptr::copy_nonoverlapping(buf.as_ptr().add(first), base, len - first);
759 }
760 direction.head.store(head + len as u64, Ordering::SeqCst);
761 std::sync::atomic::fence(Ordering::SeqCst);
771 let drained = direction.tail.load(Ordering::SeqCst) >= head;
772 self.table.notify(self.index, slot, direction, drained);
773 Ok(len)
774 }
775
776 fn try_write_exact(
778 &self,
779 buf: &[u8]
780 ) -> Result<()> {
781 let slot = self.slot()?;
782 let direction = Self::direction(slot, self.side.write_direction());
783 let flags = direction.flags();
784 if flags & FLAG_RESET != 0 {
785 return Err(Error::Reset);
786 }
787 if flags & FLAG_FIN != 0 {
788 return Err(Error::Closed);
789 }
790 if flags & FLAG_READER_GONE != 0 {
791 return Err(Error::PeerGone);
792 }
793 let buffer_bytes = self.table.geometry().buffer_bytes;
794 if buf.len() > buffer_bytes {
795 return Err(Error::Malformed(format!(
796 "atomic stream write is {} bytes; ring capacity is {buffer_bytes}",
797 buf.len()
798 )));
799 }
800 if buf.is_empty() {
801 return Ok(());
802 }
803 let head = direction.head.load(Ordering::Relaxed);
804 let tail = direction.tail.load(Ordering::Acquire);
805 if buffer_bytes - ((head - tail) as usize) < buf.len() {
806 return Err(Error::WouldBlock);
807 }
808 let base = self.table.buffer(self.index, self.side.write_direction());
809 let start = (head as usize) & (buffer_bytes - 1);
810 let first = buf.len().min(buffer_bytes - start);
811 unsafe {
813 std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
814 std::ptr::copy_nonoverlapping(buf.as_ptr().add(first), base, buf.len() - first);
815 }
816 direction.head.store(head + buf.len() as u64, Ordering::SeqCst);
817 std::sync::atomic::fence(Ordering::SeqCst);
818 let drained = direction.tail.load(Ordering::SeqCst) >= head;
819 self.table.notify(self.index, slot, direction, drained);
820 Ok(())
821 }
822
823 fn try_read(
826 &self,
827 buf: &mut [u8]
828 ) -> Result<usize> {
829 let slot = self.slot()?;
830 let direction = Self::direction(slot, self.side.read_direction());
831 let flags = direction.flags();
832 if flags & FLAG_RESET != 0 {
833 return Err(Error::Reset);
834 }
835 let head = direction.head.load(Ordering::Acquire);
836 let tail = direction.tail.load(Ordering::Relaxed);
837 let available = (head - tail) as usize;
838 if buf.is_empty() {
839 return Ok(0);
840 }
841 if available == 0 {
842 if flags & FLAG_FIN != 0 {
843 return Ok(0);
844 }
845 return Err(Error::WouldBlock);
846 }
847 let len = buf.len().min(available);
848 let base = self.table.buffer(self.index, self.side.read_direction());
849 let buffer_bytes = self.table.geometry().buffer_bytes;
850 let start = (tail as usize) & (buffer_bytes - 1);
851 let first = len.min(buffer_bytes - start);
852 unsafe {
856 std::ptr::copy_nonoverlapping(base.add(start), buf.as_mut_ptr(), first);
857 std::ptr::copy_nonoverlapping(base, buf.as_mut_ptr().add(first), len - first);
858 }
859 direction.tail.store(tail + len as u64, Ordering::SeqCst);
860 std::sync::atomic::fence(Ordering::SeqCst);
864 let head_now = direction.head.load(Ordering::SeqCst);
865 let was_full = head_now - tail >= buffer_bytes as u64;
866 self.table.notify(self.index, slot, direction, was_full);
867 Ok(len)
868 }
869
870 fn try_read_exact(
872 &self,
873 buf: &mut [u8]
874 ) -> Result<()> {
875 let slot = self.slot()?;
876 let direction = Self::direction(slot, self.side.read_direction());
877 let flags = direction.flags();
878 if flags & FLAG_RESET != 0 {
879 return Err(Error::Reset);
880 }
881 let buffer_bytes = self.table.geometry().buffer_bytes;
882 if buf.len() > buffer_bytes {
883 return Err(Error::Malformed(format!(
884 "atomic stream read is {} bytes; ring capacity is {buffer_bytes}",
885 buf.len()
886 )));
887 }
888 if buf.is_empty() {
889 return Ok(());
890 }
891 let head = direction.head.load(Ordering::Acquire);
892 let tail = direction.tail.load(Ordering::Relaxed);
893 let available = (head - tail) as usize;
894 if available < buf.len() {
895 if flags & FLAG_FIN != 0 {
896 return Err(Error::Malformed(format!(
897 "stream ended with {available} bytes of a {} byte frame",
898 buf.len()
899 )));
900 }
901 return Err(Error::WouldBlock);
902 }
903 let base = self.table.buffer(self.index, self.side.read_direction());
904 let start = (tail as usize) & (buffer_bytes - 1);
905 let first = buf.len().min(buffer_bytes - start);
906 unsafe {
908 std::ptr::copy_nonoverlapping(base.add(start), buf.as_mut_ptr(), first);
909 std::ptr::copy_nonoverlapping(base, buf.as_mut_ptr().add(first), buf.len() - first);
910 }
911 direction.tail.store(tail + buf.len() as u64, Ordering::SeqCst);
912 std::sync::atomic::fence(Ordering::SeqCst);
913 let head_now = direction.head.load(Ordering::SeqCst);
914 let was_full = head_now - tail >= buffer_bytes as u64;
915 self.table.notify(self.index, slot, direction, was_full);
916 Ok(())
917 }
918
919 fn set_flag(
920 &self,
921 direction_index: usize,
922 flag: u8
923 ) {
924 if let Ok(slot) = self.slot() {
925 let direction = Self::direction(slot, direction_index);
926 if direction.flags.fetch_or(flag, Ordering::SeqCst) & flag == 0 {
927 self.table.notify(self.index, slot, direction, true);
928 }
929 }
930 }
931
932 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
935 fn wait_until_timeout(
936 &self,
937 direction_index: usize,
938 ready: impl Fn(&Direction) -> bool,
939 timeout: Duration
940 ) -> Result<bool> {
941 let deadline = Instant::now() + timeout;
942 loop {
943 let slot = self.slot()?;
944 let direction = Self::direction(slot, direction_index);
945 if ready(direction) {
946 return Ok(true);
947 }
948 let left = deadline.saturating_duration_since(Instant::now());
949 if left.is_zero() {
950 return Ok(false);
951 }
952 direction.waiters.fetch_add(1, Ordering::SeqCst);
953 let since = direction.changes.load(Ordering::SeqCst);
954 let outcome = if ready(direction) {
955 Ok(true)
956 } else {
957 wait_on_timeout(&direction.changes, since, left)
958 };
959 direction.waiters.fetch_sub(1, Ordering::SeqCst);
960 if !outcome? {
961 let slot = self.slot()?;
964 return Ok(ready(Self::direction(slot, direction_index)));
965 }
966 }
967 }
968
969 fn wait_until(
970 &self,
971 direction_index: usize,
972 ready: impl Fn(&Direction) -> bool
973 ) -> Result<()> {
974 loop {
975 let slot = self.slot()?;
976 let direction = Self::direction(slot, direction_index);
977 if ready(direction) {
978 return Ok(());
979 }
980 direction.waiters.fetch_add(1, Ordering::SeqCst);
981 let since = direction.changes.load(Ordering::SeqCst);
982 let outcome =
983 if ready(direction) { Ok(()) } else { wait_on(&direction.changes, since) };
984 direction.waiters.fetch_sub(1, Ordering::SeqCst);
985 outcome?;
986 }
987 }
988
989 fn poll_ready(
992 &self,
993 interest: Interest,
994 ready: impl Fn(&Direction) -> bool,
995 cx: &mut std::task::Context<'_>
996 ) -> std::task::Poll<Result<()>> {
997 let slot = match self.slot() {
998 Ok(slot) => slot,
999 Err(error) => return std::task::Poll::Ready(Err(error))
1000 };
1001 let direction = Self::direction(slot, interest.direction);
1002 if ready(direction) {
1003 return std::task::Poll::Ready(Ok(()));
1004 }
1005 if let Err(error) = self.table.register(self.index, interest, cx.waker()) {
1006 return std::task::Poll::Ready(Err(error));
1007 }
1008 if ready(direction) { std::task::Poll::Ready(Ok(())) } else { std::task::Poll::Pending }
1009 }
1010}
1011
1012fn readable(direction: &Direction) -> bool {
1013 direction.head.load(Ordering::Acquire) != direction.tail.load(Ordering::Relaxed)
1014 || direction.flags() & (FLAG_FIN | FLAG_RESET) != 0
1015}
1016
1017fn writable(
1018 direction: &Direction,
1019 buffer_bytes: usize
1020) -> bool {
1021 let queued =
1022 (direction.head.load(Ordering::Relaxed) - direction.tail.load(Ordering::Acquire)) as usize;
1023 queued < buffer_bytes || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1024}
1025
1026fn writable_exact(
1027 direction: &Direction,
1028 buffer_bytes: usize,
1029 needed: usize
1030) -> bool {
1031 let queued =
1032 (direction.head.load(Ordering::Relaxed) - direction.tail.load(Ordering::Acquire)) as usize;
1033 buffer_bytes.saturating_sub(queued) >= needed
1034 || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1035}
1036
1037impl Drop for Handle {
1038 fn drop(&mut self) {
1042 let slot = &self.table.slots()[self.index];
1043 if !slot.is(self.id.generation()) {
1044 return;
1045 }
1046 if slot.claimed[self.side.index()]
1049 .compare_exchange(SIDE_CLAIMED, SIDE_RELEASED, Ordering::SeqCst, Ordering::SeqCst)
1050 .is_err()
1051 {
1052 return;
1053 }
1054 let other = slot.claimed[1 - self.side.index()].load(Ordering::SeqCst);
1058 if other != SIDE_CLAIMED {
1059 let _ = slot.state.compare_exchange(
1060 SLOT_LIVE,
1061 SLOT_EMPTY,
1062 Ordering::SeqCst,
1063 Ordering::SeqCst
1064 );
1065 for direction in &slot.directions {
1066 direction.changes.fetch_add(1, Ordering::SeqCst);
1069 if direction.waiters.load(Ordering::SeqCst) > 0 {
1070 wake_on(&direction.changes);
1071 }
1072 }
1073 self.table.registry.wake(self.index);
1074 }
1075 }
1076}
1077
1078pub struct Endpoint {
1082 read: ReadHalf,
1083 write: WriteHalf
1084}
1085
1086impl Endpoint {
1087 fn new(handle: Arc<Handle>) -> Self {
1088 Self {
1089 read: ReadHalf { handle: Arc::clone(&handle) },
1090 write: WriteHalf { handle, shut: false }
1091 }
1092 }
1093
1094 pub fn id(&self) -> StreamId {
1095 self.read.handle.id
1096 }
1097
1098 pub fn side(&self) -> Side {
1099 self.read.handle.side
1100 }
1101
1102 pub fn split(self) -> (ReadHalf, WriteHalf) {
1103 (self.read, self.write)
1104 }
1105
1106 pub fn try_read(
1107 &self,
1108 buf: &mut [u8]
1109 ) -> Result<usize> {
1110 self.read.try_read(buf)
1111 }
1112
1113 pub fn try_read_exact(
1114 &self,
1115 buf: &mut [u8]
1116 ) -> Result<()> {
1117 self.read.try_read_exact(buf)
1118 }
1119
1120 pub fn blocking_read(
1121 &self,
1122 buf: &mut [u8]
1123 ) -> Result<usize> {
1124 self.read.blocking_read(buf)
1125 }
1126
1127 pub fn blocking_read_chunk(
1128 &self,
1129 max: usize
1130 ) -> Result<Bytes> {
1131 self.read.blocking_read_chunk(max)
1132 }
1133
1134 pub fn try_write(
1135 &self,
1136 buf: &[u8]
1137 ) -> Result<usize> {
1138 self.write.try_write(buf)
1139 }
1140
1141 pub fn try_write_exact(
1142 &self,
1143 buf: &[u8]
1144 ) -> Result<()> {
1145 self.write.try_write_exact(buf)
1146 }
1147
1148 pub fn blocking_write(
1149 &self,
1150 buf: &[u8]
1151 ) -> Result<usize> {
1152 self.write.blocking_write(buf)
1153 }
1154
1155 pub fn blocking_write_all(
1156 &self,
1157 buf: &[u8]
1158 ) -> Result<()> {
1159 self.write.blocking_write_all(buf)
1160 }
1161
1162 pub fn finish(&mut self) -> Result<()> {
1163 self.write.finish()
1164 }
1165
1166 pub fn reset(&mut self) {
1167 self.write.reset()
1168 }
1169}
1170
1171pub struct ReadHalf {
1174 handle: Arc<Handle>
1175}
1176
1177impl ReadHalf {
1178 pub fn id(&self) -> StreamId {
1179 self.handle.id
1180 }
1181
1182 pub fn try_read(
1185 &self,
1186 buf: &mut [u8]
1187 ) -> Result<usize> {
1188 self.handle.try_read(buf)
1189 }
1190
1191 pub fn try_read_exact(
1194 &self,
1195 buf: &mut [u8]
1196 ) -> Result<()> {
1197 self.handle.try_read_exact(buf)
1198 }
1199
1200 pub fn blocking_read(
1203 &self,
1204 buf: &mut [u8]
1205 ) -> Result<usize> {
1206 loop {
1207 match self.handle.try_read(buf) {
1208 Err(Error::WouldBlock) => self.wait_readable()?,
1209 other => return other
1210 }
1211 }
1212 }
1213
1214 pub fn blocking_read_chunk(
1216 &self,
1217 max: usize
1218 ) -> Result<Bytes> {
1219 let mut chunk = BytesMut::zeroed(max);
1220 let len = self.blocking_read(&mut chunk)?;
1221 chunk.truncate(len);
1222 Ok(chunk.freeze())
1223 }
1224
1225 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1227 pub fn wait_readable_timeout(
1228 &self,
1229 timeout: Duration
1230 ) -> Result<bool> {
1231 self.handle.wait_until_timeout(self.handle.side.read_direction(), readable, timeout)
1232 }
1233
1234 pub fn wait_readable(&self) -> Result<()> {
1236 self.handle.wait_until(self.handle.side.read_direction(), readable)
1237 }
1238
1239 pub fn poll_readable(
1244 &self,
1245 cx: &mut std::task::Context<'_>
1246 ) -> std::task::Poll<Result<()>> {
1247 self.handle.poll_ready(
1248 Interest { direction: self.handle.side.read_direction(), writer: false },
1249 readable,
1250 cx
1251 )
1252 }
1253}
1254
1255impl Drop for ReadHalf {
1256 fn drop(&mut self) {
1257 self.handle.set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1258 }
1259}
1260
1261pub struct WriteHalf {
1264 handle: Arc<Handle>,
1265 shut: bool
1266}
1267
1268impl WriteHalf {
1269 pub fn id(&self) -> StreamId {
1270 self.handle.id
1271 }
1272
1273 pub fn try_write(
1275 &self,
1276 buf: &[u8]
1277 ) -> Result<usize> {
1278 self.handle.try_write(buf)
1279 }
1280
1281 pub fn try_write_exact(
1284 &self,
1285 buf: &[u8]
1286 ) -> Result<()> {
1287 self.handle.try_write_exact(buf)
1288 }
1289
1290 pub fn blocking_write(
1292 &self,
1293 buf: &[u8]
1294 ) -> Result<usize> {
1295 loop {
1296 match self.handle.try_write(buf) {
1297 Err(Error::WouldBlock) => self.wait_writable()?,
1298 other => return other
1299 }
1300 }
1301 }
1302
1303 pub fn blocking_write_all(
1306 &self,
1307 mut buf: &[u8]
1308 ) -> Result<()> {
1309 while !buf.is_empty() {
1310 let written = self.blocking_write(buf)?;
1311 buf = &buf[written..];
1312 }
1313 Ok(())
1314 }
1315
1316 pub fn finish(&mut self) -> Result<()> {
1320 self.shut = true;
1321 self.handle.set_flag(self.handle.side.write_direction(), FLAG_FIN);
1322 Ok(())
1323 }
1324
1325 pub fn reset(&mut self) {
1328 self.shut = true;
1329 self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1330 }
1331
1332 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1336 pub fn wait_writable_timeout(
1337 &self,
1338 timeout: Duration
1339 ) -> Result<bool> {
1340 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1341 self.handle.wait_until_timeout(
1342 self.handle.side.write_direction(),
1343 |direction| writable(direction, buffer_bytes),
1344 timeout
1345 )
1346 }
1347
1348 pub fn wait_writable(&self) -> Result<()> {
1350 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1351 self.handle.wait_until(self.handle.side.write_direction(), |direction| {
1352 writable(direction, buffer_bytes)
1353 })
1354 }
1355
1356 pub fn wait_writable_exact(
1358 &self,
1359 needed: usize
1360 ) -> Result<()> {
1361 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1362 if needed > buffer_bytes {
1363 return Err(Error::Malformed(format!(
1364 "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1365 )));
1366 }
1367 self.handle.wait_until(self.handle.side.write_direction(), |direction| {
1368 writable_exact(direction, buffer_bytes, needed)
1369 })
1370 }
1371
1372 pub fn poll_writable(
1377 &self,
1378 cx: &mut std::task::Context<'_>
1379 ) -> std::task::Poll<Result<()>> {
1380 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1381 self.handle.poll_ready(
1382 Interest { direction: self.handle.side.write_direction(), writer: true },
1383 |direction| writable(direction, buffer_bytes),
1384 cx
1385 )
1386 }
1387
1388 pub fn poll_writable_exact(
1390 &self,
1391 needed: usize,
1392 cx: &mut std::task::Context<'_>
1393 ) -> std::task::Poll<Result<()>> {
1394 let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1395 if needed > buffer_bytes {
1396 return std::task::Poll::Ready(Err(Error::Malformed(format!(
1397 "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1398 ))));
1399 }
1400 self.handle.poll_ready(
1401 Interest { direction: self.handle.side.write_direction(), writer: true },
1402 |direction| writable_exact(direction, buffer_bytes, needed),
1403 cx
1404 )
1405 }
1406}
1407
1408impl Drop for WriteHalf {
1409 fn drop(&mut self) {
1410 if !self.shut {
1411 self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1412 }
1413 }
1414}
1415
1416#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1420pub(crate) fn waits_supported() -> bool {
1421 orbit_core::sync::supported()
1422}
1423
1424#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1425pub(crate) fn waits_supported() -> bool {
1426 false
1427}
1428
1429#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1433#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1436pub(crate) fn wait_on_timeout(
1437 word: &AtomicU32,
1438 expected: u32,
1439 timeout: Duration
1440) -> Result<bool> {
1441 orbit_core::sync::wait_word_timeout(word, expected, timeout).map_err(Error::Io)
1442}
1443
1444pub(crate) fn wait_on(
1445 word: &AtomicU32,
1446 expected: u32
1447) -> Result<()> {
1448 orbit_core::sync::wait_word(word, expected).map_err(Error::Io)
1449}
1450
1451#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1452pub(crate) fn wait_on(
1453 _word: &AtomicU32,
1454 _expected: u32
1455) -> Result<()> {
1456 Err(Error::Io(std::io::Error::new(
1457 std::io::ErrorKind::Unsupported,
1458 "orbit-stream needs a platform that can wait on a shared word"
1459 )))
1460}
1461
1462pub(crate) fn wake_on(word: &AtomicU32) {
1464 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1465 let _ = orbit_core::sync::wake_word(word);
1466 #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1467 let _ = word;
1468}
1469
1470pub(crate) fn lock_unpoisoned<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
1471 mutex.lock().unwrap_or_else(|error| error.into_inner())
1472}
1473
1474#[cfg(test)]
1475mod tests {
1476 use std::sync::Arc;
1477 use std::time::Duration;
1478
1479 use orbit_core::{Fleet, NodeId};
1480
1481 use super::{
1482 Error, Incarnation, STREAM_BUFFER_BYTES, STREAM_LANE_CAPACITY, Side, Streams, Ticket
1483 };
1484
1485 fn streams(name: &'static str) -> Streams {
1486 Streams::new(Arc::new(Fleet::join(name, 2).unwrap()), Incarnation::new(1)).unwrap()
1487 }
1488
1489 #[test]
1490 fn bytes_cross_in_order_and_end_cleanly() {
1491 let streams = streams("stream-order");
1492 let (mut a, ticket) = streams.create().unwrap();
1493 assert_eq!(ticket.side, Side::B);
1494 assert_eq!(ticket.id.node(), 0);
1495 assert_eq!(ticket.epoch, streams.epoch());
1496 let b = streams.open(ticket).unwrap();
1497
1498 a.blocking_write_all(b"hello, ").unwrap();
1499 a.blocking_write_all(b"world").unwrap();
1500 a.finish().unwrap();
1501
1502 let mut out = Vec::new();
1503 let mut buf = [0_u8; 4];
1504 loop {
1505 let n = b.blocking_read(&mut buf).unwrap();
1506 if n == 0 {
1507 break;
1508 }
1509 out.extend_from_slice(&buf[..n]);
1510 }
1511 assert_eq!(out, b"hello, world");
1512 b.blocking_write_all(b"back").unwrap();
1514 assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"back");
1515 }
1516
1517 #[test]
1518 fn a_ticket_prints_and_parses() {
1519 let streams = streams("stream-ticket");
1520 let (_a, ticket) = streams.create().unwrap();
1521 let text = ticket.to_string();
1522 assert!(text.contains("/b/"), "{text}");
1523 assert_eq!(text.parse::<Ticket>().unwrap(), ticket);
1524 assert!(matches!("nope".parse::<Ticket>(), Err(Error::Malformed(_))));
1525 assert!(matches!("x/b".parse::<Ticket>(), Err(Error::Malformed(_))));
1526 }
1527
1528 #[test]
1529 fn a_side_is_held_once_and_a_released_stream_goes_stale() {
1530 let streams = streams("stream-claim");
1531 let (a, ticket) = streams.create().unwrap();
1532 let b = streams.open(ticket).unwrap();
1533 assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_))));
1534 assert!(streams.is_live(ticket.id));
1535
1536 drop(a);
1537 let mut buf = [0_u8; 1];
1539 assert!(matches!(b.blocking_read(&mut buf), Err(Error::Reset)));
1540 drop(b);
1541 assert!(!streams.is_live(ticket.id));
1542 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1543
1544 for _ in 0..STREAM_LANE_CAPACITY - 1 {
1547 drop(streams.create().unwrap());
1548 }
1549 let (_a2, ticket2) = streams.create().unwrap();
1550 assert_eq!(ticket2.id.slot(), ticket.id.slot());
1551 assert_ne!(ticket2.id.generation(), ticket.id.generation());
1552 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1553 }
1554
1555 #[test]
1556 fn a_reset_starts_a_new_epoch_and_refuses_old_tickets() {
1557 let streams = streams("stream-epoch");
1558 let (_a, ticket) = streams.create().unwrap();
1559 let before = streams.epoch();
1560 streams.reset_all();
1561 assert!(streams.epoch() > before);
1562 let (_a2, ticket2) = streams.create().unwrap();
1565 assert_eq!(ticket2.id, ticket.id);
1566 assert_ne!(ticket2.epoch, ticket.epoch);
1567 assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1568 assert!(streams.open(ticket2).is_ok());
1569 }
1570
1571 #[test]
1572 fn a_slot_whose_generation_runs_out_is_not_reused() {
1573 let streams = streams("stream-exhaust");
1576 let limit = super::layout::GENERATION_LIMIT as usize;
1577 for _ in 0..STREAM_LANE_CAPACITY * limit {
1578 drop(streams.create().unwrap());
1579 }
1580 assert!(matches!(streams.create(), Err(Error::Full { .. })));
1581 streams.reset_all();
1582 let (_a, ticket) = streams.create().unwrap();
1583 assert_eq!(ticket.id.generation(), 1);
1584 }
1585
1586 #[test]
1587 fn a_full_ring_blocks_the_writer_and_wraps_correctly() {
1588 let streams = streams("stream-wrap");
1589 let (a, ticket) = streams.create().unwrap();
1590 let b = streams.open(ticket).unwrap();
1591
1592 let filler = vec![7_u8; STREAM_BUFFER_BYTES];
1593 assert_eq!(a.try_write(&filler).unwrap(), STREAM_BUFFER_BYTES);
1594 assert!(matches!(a.try_write(b"x"), Err(Error::WouldBlock)));
1595
1596 let mut buf = vec![0_u8; STREAM_BUFFER_BYTES - 5];
1597 assert_eq!(b.blocking_read(&mut buf).unwrap(), STREAM_BUFFER_BYTES - 5);
1598 let tail = (0..STREAM_BUFFER_BYTES).map(|i| i as u8).collect::<Vec<_>>();
1600 assert_eq!(a.try_write(&tail).unwrap(), STREAM_BUFFER_BYTES - 5);
1601
1602 let mut rest = vec![0_u8; 5];
1603 assert_eq!(b.blocking_read(&mut rest).unwrap(), 5);
1604 assert_eq!(rest, vec![7_u8; 5]);
1605 let mut wrapped = vec![0_u8; STREAM_BUFFER_BYTES];
1606 let mut got = 0;
1607 while got < STREAM_BUFFER_BYTES - 5 {
1608 got += b.blocking_read(&mut wrapped[got..]).unwrap();
1609 }
1610 assert_eq!(&wrapped[..got], &tail[..got]);
1611 }
1612
1613 #[test]
1614 fn exact_frames_never_publish_or_consume_a_prefix() {
1615 let streams = streams("stream-exact-frame");
1616 let (a, ticket) = streams.create().unwrap();
1617 let b = streams.open(ticket).unwrap();
1618
1619 let filler = vec![7_u8; STREAM_BUFFER_BYTES - 32];
1620 a.try_write_exact(&filler).unwrap();
1621 assert!(matches!(a.try_write_exact(&[9_u8; 64]), Err(Error::WouldBlock)));
1622 let mut consumed = vec![0_u8; filler.len()];
1623 b.try_read_exact(&mut consumed).unwrap();
1624 assert_eq!(consumed, filler);
1625 assert!(matches!(b.try_read(&mut [0_u8; 1]), Err(Error::WouldBlock)));
1626
1627 a.try_write_exact(&[3_u8; 32]).unwrap();
1628 assert!(matches!(b.try_read_exact(&mut [0_u8; 64]), Err(Error::WouldBlock)));
1629 let mut frame = [0_u8; 32];
1630 b.try_read_exact(&mut frame).unwrap();
1631 assert_eq!(frame, [3_u8; 32]);
1632 }
1633
1634 #[test]
1635 fn a_blocked_reader_is_woken_by_a_write_in_another_thread() {
1636 let streams = streams("stream-wake");
1637 let (a, ticket) = streams.create().unwrap();
1638 let b = streams.open(ticket).unwrap();
1639 let reader = std::thread::spawn(move || {
1640 let mut buf = [0_u8; 8];
1641 let n = b.blocking_read(&mut buf).unwrap();
1642 buf[..n].to_vec()
1643 });
1644 std::thread::sleep(Duration::from_millis(30));
1645 a.blocking_write_all(b"ping").unwrap();
1646 assert_eq!(reader.join().unwrap(), b"ping");
1647 }
1648
1649 #[test]
1650 fn a_reader_gone_fails_the_writer_and_a_lane_fills_up() {
1651 let streams = streams("stream-gone");
1652 let (a, ticket) = streams.create().unwrap();
1653 let b = streams.open(ticket).unwrap();
1654 let (b_read, _b_write) = b.split();
1655 drop(b_read);
1656 assert!(matches!(a.blocking_write(b"nobody"), Err(Error::PeerGone)));
1657
1658 let mut held = vec![a];
1659 for _ in 1..STREAM_LANE_CAPACITY {
1660 held.push(streams.create().unwrap().0);
1661 }
1662 assert!(matches!(streams.create(), Err(Error::Full { .. })));
1663 }
1664
1665 #[test]
1666 fn a_death_report_ends_that_incarnations_sides_only() {
1667 let streams = streams("stream-dead");
1668 let (a, ticket) = streams.create().unwrap();
1669 let b = streams.open(ticket).unwrap();
1670 b.blocking_write_all(b"partial").unwrap();
1671
1672 streams.node_dead(NodeId::ZERO, Incarnation::new(2));
1674 assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"partial");
1675
1676 let reader = std::thread::spawn(move || {
1682 let mut buf = [0_u8; 8];
1683 a.blocking_read(&mut buf).map(|_| ())
1684 });
1685 std::thread::sleep(Duration::from_millis(30));
1686 streams.node_dead(NodeId::ZERO, Incarnation::new(1));
1687 assert!(matches!(reader.join().unwrap(), Err(Error::Reset | Error::Stale(_))));
1688 assert!(matches!(b.try_write(b"x"), Err(Error::Reset | Error::Stale(_))));
1689 assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_) | Error::Stale(_))));
1690 drop(b);
1691 assert!(!streams.is_live(ticket.id));
1692 }
1693
1694 #[test]
1695 fn an_offer_is_taken_once_and_a_stale_offer_is_skipped() {
1696 let streams = streams("stream-offer");
1697 assert!(streams.take_offer().is_none());
1698 let (a, ticket) = streams.create().unwrap();
1699 streams.offer(ticket, NodeId::ZERO).unwrap();
1700 assert_eq!(streams.take_offer(), Some(ticket));
1701 assert!(streams.take_offer().is_none());
1702
1703 let (gone, stale) = streams.create().unwrap();
1704 streams.offer(stale, NodeId::ZERO).unwrap();
1705 drop(gone);
1706 assert!(streams.take_offer().is_none());
1707
1708 let taker = streams.clone();
1709 let waiter = std::thread::spawn(move || taker.blocking_take_offer());
1710 std::thread::sleep(Duration::from_millis(30));
1711 streams.offer(ticket, NodeId::ZERO).unwrap();
1712 assert_eq!(waiter.join().unwrap().unwrap(), ticket);
1713 drop(a);
1714 }
1715
1716 #[test]
1717 fn a_contested_claim_goes_to_exactly_one_opener() {
1718 let streams = streams("stream-race");
1719 for _ in 0..50 {
1720 let (_a, ticket) = streams.create().unwrap();
1721 let openers = (0..4)
1722 .map(|_| {
1723 let streams = streams.clone();
1724 std::thread::spawn(move || streams.open(ticket).is_ok())
1725 })
1726 .collect::<Vec<_>>();
1727 let won =
1728 openers.into_iter().map(|opener| opener.join().unwrap()).filter(|won| *won).count();
1729 assert_eq!(won, 1);
1730 }
1731 }
1732
1733 #[test]
1734 fn random_sizes_concatenate_exactly() {
1735 let streams = streams("stream-random");
1736 let (mut a, ticket) = streams.create().unwrap();
1737 let b = streams.open(ticket).unwrap();
1738 let payload =
1739 (0..300_000_u32).map(|i| (i.wrapping_mul(2654435761) >> 13) as u8).collect::<Vec<_>>();
1740 let expected = payload.clone();
1741 let writer = std::thread::spawn(move || {
1742 let mut rest = &payload[..];
1743 let mut step = 1_usize;
1744 while !rest.is_empty() {
1745 let len = step.min(rest.len());
1746 a.blocking_write_all(&rest[..len]).unwrap();
1747 rest = &rest[len..];
1748 step = (step * 7 + 13) % 9_001 + 1;
1749 }
1750 a.finish().unwrap();
1751 });
1752 let mut out = Vec::new();
1753 let mut step = 3_usize;
1754 loop {
1755 let chunk = b.blocking_read_chunk(step).unwrap();
1756 if chunk.is_empty() {
1757 break;
1758 }
1759 out.extend_from_slice(&chunk);
1760 step = (step * 5 + 11) % 4_099 + 1;
1761 }
1762 writer.join().unwrap();
1763 assert_eq!(out, expected);
1764 }
1765}