1use crate::batch::{ActiveRegionSet, BatchError, ExpectedBatch, TransferBatch};
4use crate::control::{ControlError, ControlFrame};
5use core::cell::Cell;
6use core::marker::PhantomData;
7#[cfg(any(target_os = "macos", target_os = "windows"))]
8use core::sync::atomic::{AtomicBool, Ordering};
9#[cfg(target_os = "linux")]
10use core::sync::atomic::{AtomicI32, Ordering};
11use std::ffi::OsString;
12use std::num::NonZeroU32;
13#[cfg(target_os = "linux")]
14use std::os::fd::{FromRawFd, OwnedFd};
15#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
16use std::path::Path;
17use std::path::PathBuf;
18use std::time::{Duration, Instant};
19
20pub use crate::liveness::{ActiveLeaseFacts, LeaseFactsConsistency};
21
22#[cfg(target_os = "linux")]
23const RECEIVER_BOOTSTRAP_ENV_PREFIX: &[u8] = b"NATIVE_IPC_VNEXT_BOOTSTRAP_FD=";
24#[cfg(target_os = "linux")]
25const RECEIVER_PUBLIC_BOOTSTRAP_ENV_ENTRY: &[u8] = b"NATIVE_IPC_VNEXT_PUBLIC_BOOTSTRAP=1";
26#[cfg(target_os = "linux")]
27const PR_GET_MDWE: libc::c_int = 66;
28#[cfg(target_os = "linux")]
29const PR_MDWE_REFUSE_EXEC_GAIN: libc::c_ulong = 1;
30#[cfg(target_os = "linux")]
31const BOOTSTRAP_ABSENT: i32 = -2;
32#[cfg(target_os = "linux")]
33const BOOTSTRAP_INVALID: i32 = -1;
34#[cfg(target_os = "linux")]
35const BOOTSTRAP_TAKEN: i32 = -3;
36#[cfg(target_os = "linux")]
37static RECEIVER_BOOTSTRAP_FD: AtomicI32 = AtomicI32::new(BOOTSTRAP_ABSENT);
38#[cfg(any(target_os = "macos", target_os = "windows"))]
39static RECEIVER_BOOTSTRAP_TAKEN: AtomicBool = AtomicBool::new(false);
40
41#[doc(hidden)]
51pub unsafe extern "C" fn __receiver_bootstrap_preinit(
52 _argument_count: core::ffi::c_int,
53 _arguments: *mut *mut core::ffi::c_char,
54 environment: *mut *mut core::ffi::c_char,
55) {
56 #[cfg(target_os = "linux")]
57 unsafe {
60 receiver_bootstrap_preinit_linux(environment);
61 }
62 #[cfg(not(target_os = "linux"))]
63 let _ = environment;
64}
65
66#[cfg(target_os = "linux")]
67unsafe fn receiver_bootstrap_preinit_linux(environment: *mut *mut libc::c_char) {
68 let mut entry = environment;
72 let public_bootstrap = loop {
73 if entry.is_null() {
74 return;
75 }
76 let candidate = unsafe { *entry };
78 if candidate.is_null() {
79 return;
80 }
81 let mut matches = true;
82 for (offset, expected) in RECEIVER_PUBLIC_BOOTSTRAP_ENV_ENTRY.iter().enumerate() {
83 let actual = unsafe { *candidate.add(offset) }.to_ne_bytes()[0];
86 if actual == 0 || actual != *expected {
87 matches = false;
88 break;
89 }
90 }
91 if matches
92 && unsafe { *candidate.add(RECEIVER_PUBLIC_BOOTSTRAP_ENV_ENTRY.len()) } == 0
94 {
95 break candidate;
96 }
97 entry = unsafe { entry.add(1) };
99 };
100 unsafe { *public_bootstrap = 0 };
104
105 let mut entry = environment;
106 let value = loop {
107 if entry.is_null() {
108 return;
109 }
110 let candidate = unsafe { *entry };
112 if candidate.is_null() {
113 return;
114 }
115 let mut matches = true;
116 for (offset, expected) in RECEIVER_BOOTSTRAP_ENV_PREFIX.iter().enumerate() {
117 let actual = unsafe { *candidate.add(offset) }.to_ne_bytes()[0];
120 if actual == 0 || actual != *expected {
121 matches = false;
122 break;
123 }
124 }
125 if matches {
126 let value = unsafe { candidate.add(RECEIVER_BOOTSTRAP_ENV_PREFIX.len()) };
128 unsafe { *candidate = 0 };
132 break value;
133 }
134 entry = unsafe { entry.add(1) };
136 };
137 let mut raw = 0_i32;
138 let mut length = 0_usize;
139 loop {
140 if length == 10 {
141 RECEIVER_BOOTSTRAP_FD.store(BOOTSTRAP_INVALID, Ordering::Release);
142 return;
143 }
144 let byte = unsafe { *value.add(length) }.to_ne_bytes()[0];
146 if byte == 0 {
147 break;
148 }
149 if !byte.is_ascii_digit() || (length == 0 && byte == b'0') {
150 RECEIVER_BOOTSTRAP_FD.store(BOOTSTRAP_INVALID, Ordering::Release);
151 return;
152 }
153 let Some(next) = raw
154 .checked_mul(10)
155 .and_then(|current| current.checked_add(i32::from(byte - b'0')))
156 else {
157 RECEIVER_BOOTSTRAP_FD.store(BOOTSTRAP_INVALID, Ordering::Release);
158 return;
159 };
160 raw = next;
161 length += 1;
162 }
163 if length == 0 || raw < 3 {
164 RECEIVER_BOOTSTRAP_FD.store(BOOTSTRAP_INVALID, Ordering::Release);
165 return;
166 }
167
168 let descriptor_flags = unsafe { libc::fcntl(raw, libc::F_GETFD) };
171 let descriptor_status = unsafe { libc::fcntl(raw, libc::F_GETFL) };
172 let mdwe = unsafe { libc::prctl(PR_GET_MDWE, 0, 0, 0, 0) } as libc::c_ulong;
173 let pid = unsafe { libc::getpid() };
174 let sid = unsafe { libc::getsid(0) };
175 let process_group = unsafe { libc::getpgrp() };
176 let mut socket_type = 0_i32;
177 let mut socket_type_len = core::mem::size_of::<i32>() as libc::socklen_t;
178 let socket_result = unsafe {
179 libc::getsockopt(
180 raw,
181 libc::SOL_SOCKET,
182 libc::SO_TYPE,
183 (&mut socket_type as *mut i32).cast(),
184 &mut socket_type_len,
185 )
186 };
187 if descriptor_flags != 0
188 || descriptor_status < 0
189 || descriptor_status & libc::O_NONBLOCK == 0
190 || mdwe != PR_MDWE_REFUSE_EXEC_GAIN
191 || pid <= 0
192 || sid != pid
193 || process_group != pid
194 || socket_result != 0
195 || socket_type_len as usize != core::mem::size_of::<i32>()
196 || socket_type != libc::SOCK_SEQPACKET
197 {
198 let _ = unsafe { libc::close(raw) };
202 RECEIVER_BOOTSTRAP_FD.store(BOOTSTRAP_INVALID, Ordering::Release);
203 return;
204 }
205 if unsafe { libc::fcntl(raw, libc::F_SETFD, libc::FD_CLOEXEC) } != 0 {
209 let _ = unsafe { libc::close(raw) };
210 RECEIVER_BOOTSTRAP_FD.store(BOOTSTRAP_INVALID, Ordering::Release);
211 return;
212 }
213 RECEIVER_BOOTSTRAP_FD.store(raw, Ordering::Release);
214}
215
216pub const HARD_MAX_REGIONS_PER_BATCH: u16 = 16;
218pub const HARD_MAX_BOOTSTRAP_PAYLOAD_BYTES: u32 = 16 * 1024 * 1024;
220pub const HARD_MAX_CONTROL_PAYLOAD_BYTES: u32 = 16 * 1024 * 1024;
222pub const HARD_MAX_REGION_BYTES: u64 = 1 << 40;
224pub const HARD_MAX_BATCH_BYTES: u64 = 1 << 42;
226pub const HARD_MAX_ACTIVE_REGIONS: u32 = 1 << 20;
228pub const HARD_MAX_ACTIVE_BYTES: u64 = 1 << 44;
230pub const HARD_MAX_TRANSACTIONS: u64 = 1 << 48;
232
233pub struct Coordinator;
235pub struct Receiver;
237pub struct Negotiating;
239pub struct Ready;
241
242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
249pub enum BackendStatus {
250 Available,
252 Unavailable,
256}
257
258pub const fn backend_status() -> BackendStatus {
265 BackendStatus::Available
266}
267
268#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270pub struct ProtocolVersion {
271 major: u16,
272 minor: u16,
273}
274
275impl ProtocolVersion {
276 #[allow(dead_code, reason = "wired into accepted session facts below")]
277 pub(crate) const fn new(major: u16, minor: u16) -> Self {
278 Self { major, minor }
279 }
280
281 pub const fn major(self) -> u16 {
283 self.major
284 }
285
286 pub const fn minor(self) -> u16 {
288 self.minor
289 }
290}
291
292#[derive(Clone, Copy, Debug, Eq, PartialEq)]
294pub enum SessionState {
295 Ready,
297 Poisoned,
299}
300
301#[derive(Clone, Copy, Debug, Eq, PartialEq)]
303pub enum PeerStatus {
304 Connected,
306 Disconnected,
308}
309
310#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub enum ChildExitStatus {
313 Exited(i32),
315 Signaled {
317 signal: i32,
319 dumped_core: bool,
321 },
322 AlreadyReaped,
324}
325
326#[derive(Clone, Copy, Debug, Eq, PartialEq)]
328pub enum DescendantCleanupStatus {
329 NotEstablished,
331 FreshGroupUnverified,
334 FreshGroupTerminated,
338 ContainedProcessTreeComplete,
340 OwnedContainmentUnverified,
342}
343
344#[derive(Clone, Copy, Debug, Eq, PartialEq)]
346pub struct ChildCleanupFacts {
347 direct_child: Option<ChildExitStatus>,
348 descendants: DescendantCleanupStatus,
349 native_error: Option<i32>,
350}
351
352impl ChildCleanupFacts {
353 #[allow(dead_code, reason = "wired into coordinator lifecycle facts below")]
354 pub(crate) const fn new(
355 direct_child: Option<ChildExitStatus>,
356 descendants: DescendantCleanupStatus,
357 native_error: Option<i32>,
358 ) -> Self {
359 Self {
360 direct_child,
361 descendants,
362 native_error,
363 }
364 }
365
366 pub const fn direct_child(self) -> Option<ChildExitStatus> {
368 self.direct_child
369 }
370
371 pub const fn descendants(self) -> DescendantCleanupStatus {
373 self.descendants
374 }
375
376 pub const fn native_error(self) -> Option<i32> {
378 self.native_error
379 }
380
381 pub const fn direct_child_complete(self) -> bool {
383 self.direct_child.is_some()
384 }
385}
386
387pub enum CoordinatorCloseOutcome {
389 Closed(ChildCleanupFacts),
391 ActiveLeases {
393 session: CoordinatorSession<Ready>,
395 facts: ActiveLeaseFacts,
397 },
398 CleanupPending {
400 session: CoordinatorSession<Ready>,
402 facts: ChildCleanupFacts,
404 failure: SessionFailure,
406 },
407 Failed {
409 session: CoordinatorSession<Ready>,
411 error: SessionFailure,
413 },
414}
415
416pub enum ReceiverCloseOutcome {
418 Closed,
420 ActiveLeases {
422 session: ReceiverSession<Ready>,
424 facts: ActiveLeaseFacts,
426 },
427 Failed {
429 session: ReceiverSession<Ready>,
431 error: SessionFailure,
433 },
434}
435
436#[derive(Clone, Copy, Debug, Eq, PartialEq)]
438pub struct CoordinatorAbortOutcome {
439 cleanup: ChildCleanupFacts,
440 failure: Option<SessionFailure>,
441}
442
443impl CoordinatorAbortOutcome {
444 pub const fn cleanup(self) -> ChildCleanupFacts {
446 self.cleanup
447 }
448
449 pub const fn failure(self) -> Option<SessionFailure> {
451 self.failure
452 }
453}
454
455pub struct Session<Role, State> {
461 inner: SessionInner,
462 role: PhantomData<Role>,
463 state: PhantomData<State>,
464 not_sync: PhantomData<Cell<()>>,
465}
466
467pub type CoordinatorSession<State> = Session<Coordinator, State>;
469pub type ReceiverSession<State> = Session<Receiver, State>;
471
472pub struct ReceiverBootstrap {
478 #[cfg(target_os = "linux")]
479 inherited: OwnedFd,
480 not_sync: PhantomData<Cell<()>>,
481}
482
483#[macro_export]
494macro_rules! receiver_main {
495 ($entry:expr) => {
496 #[cfg(target_os = "linux")]
497 #[used]
498 #[unsafe(link_section = ".preinit_array")]
499 static NATIVE_IPC_RECEIVER_BOOTSTRAP_PREINIT: unsafe extern "C" fn(
500 ::core::ffi::c_int,
501 *mut *mut ::core::ffi::c_char,
502 *mut *mut ::core::ffi::c_char,
503 ) = $crate::session::__receiver_bootstrap_preinit;
504
505 fn main() {
506 let bootstrap = $crate::session::__take_receiver_bootstrap();
507 ($entry)(bootstrap);
508 }
509 };
510}
511
512#[doc(hidden)]
517pub fn __take_receiver_bootstrap() -> Result<ReceiverBootstrap, SessionFailure> {
518 #[cfg(target_os = "linux")]
519 {
520 let raw = RECEIVER_BOOTSTRAP_FD.swap(BOOTSTRAP_TAKEN, Ordering::AcqRel);
521 if raw < 3 {
522 return Err(SessionFailure::new(
523 SessionOperation::Bootstrap,
524 SessionTransactionState::NotEstablished,
525 SessionError::InvalidInput,
526 ));
527 }
528 let inherited = unsafe { OwnedFd::from_raw_fd(raw) };
531 Ok(ReceiverBootstrap {
532 inherited,
533 not_sync: PhantomData,
534 })
535 }
536 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
537 {
538 Err(SessionFailure::new(
539 SessionOperation::Bootstrap,
540 SessionTransactionState::NotEstablished,
541 SessionError::BackendUnavailable,
542 ))
543 }
544 #[cfg(target_os = "windows")]
545 {
546 if RECEIVER_BOOTSTRAP_TAKEN.swap(true, Ordering::AcqRel)
547 || std::env::var_os("NATIVE_IPC_VNEXT_PUBLIC_BOOTSTRAP").as_deref()
548 != Some(std::ffi::OsStr::new("1"))
549 {
550 return Err(SessionFailure::new(
551 SessionOperation::Bootstrap,
552 SessionTransactionState::NotEstablished,
553 SessionError::InvalidInput,
554 ));
555 }
556 Ok(ReceiverBootstrap {
557 not_sync: PhantomData,
558 })
559 }
560 #[cfg(target_os = "macos")]
561 {
562 if RECEIVER_BOOTSTRAP_TAKEN.swap(true, Ordering::AcqRel)
563 || std::env::var_os("NATIVE_IPC_VNEXT_PUBLIC_BOOTSTRAP").as_deref()
564 != Some(std::ffi::OsStr::new("1"))
565 {
566 return Err(SessionFailure::new(
567 SessionOperation::Bootstrap,
568 SessionTransactionState::NotEstablished,
569 SessionError::InvalidInput,
570 ));
571 }
572 unsafe { std::env::remove_var("NATIVE_IPC_VNEXT_PUBLIC_BOOTSTRAP") };
579 Ok(ReceiverBootstrap {
580 not_sync: PhantomData,
581 })
582 }
583}
584
585enum SessionInner {
586 #[cfg(target_os = "linux")]
587 CoordinatorNegotiating(crate::backend::linux_vnext::spawn::LinuxCoordinatorNegotiatingSession),
588 #[cfg(target_os = "linux")]
589 ReceiverNegotiating(crate::backend::linux_vnext::spawn::LinuxReceiverNegotiatingSession),
590 #[cfg(target_os = "linux")]
591 CoordinatorReady(crate::backend::linux_vnext::spawn::LinuxCoordinatorReadySession),
592 #[cfg(target_os = "linux")]
593 ReceiverReady(crate::backend::linux_vnext::spawn::LinuxReceiverReadySession),
594 #[cfg(target_os = "macos")]
595 CoordinatorNegotiating(crate::backend::macos::vnext_session::MacCoordinatorNegotiatingSession),
596 #[cfg(target_os = "macos")]
597 ReceiverNegotiating(crate::backend::macos::vnext_session::MacReceiverNegotiatingSession),
598 #[cfg(target_os = "macos")]
599 CoordinatorReady(crate::backend::macos::vnext_session::MacCoordinatorReadySession),
600 #[cfg(target_os = "macos")]
601 ReceiverReady(crate::backend::macos::vnext_session::MacReceiverReadySession),
602 #[cfg(target_os = "windows")]
603 CoordinatorNegotiating(
604 Box<crate::backend::windows::vnext_session::WindowsCoordinatorNegotiatingSession>,
605 ),
606 #[cfg(target_os = "windows")]
607 ReceiverNegotiating(
608 Box<crate::backend::windows::vnext_session::WindowsReceiverNegotiatingSession>,
609 ),
610 #[cfg(target_os = "windows")]
611 CoordinatorReady(Box<crate::backend::windows::vnext_session::WindowsCoordinatorReadySession>),
612 #[cfg(target_os = "windows")]
613 ReceiverReady(Box<crate::backend::windows::vnext_session::WindowsReceiverReadySession>),
614 #[allow(dead_code)]
615 Unavailable,
616}
617
618#[derive(Clone, Copy, Debug, Eq, PartialEq)]
620pub enum ExecutableIdentityPolicy {
621 ExactOpenedFile,
635}
636
637#[derive(Clone, Debug, Eq, PartialEq)]
639pub struct SessionCommand {
640 executable: PathBuf,
641 arguments: Vec<OsString>,
642 environment: Vec<(OsString, OsString)>,
643}
644
645impl SessionCommand {
646 pub fn new(executable: impl Into<PathBuf>) -> Self {
648 let executable = executable.into();
649 Self {
650 arguments: vec![executable.as_os_str().to_owned()],
651 executable,
652 environment: Vec::new(),
653 }
654 }
655
656 pub fn arg0(mut self, argument: impl Into<OsString>) -> Self {
658 self.arguments[0] = argument.into();
659 self
660 }
661
662 pub fn arg(mut self, argument: impl Into<OsString>) -> Self {
664 self.arguments.push(argument.into());
665 self
666 }
667
668 pub fn env(mut self, key: impl Into<OsString>, value: impl Into<OsString>) -> Self {
670 let key = key.into();
671 let value = value.into();
672 if let Some((_, existing)) = self
673 .environment
674 .iter_mut()
675 .find(|(existing, _)| *existing == key)
676 {
677 *existing = value;
678 } else {
679 self.environment.push((key, value));
680 }
681 self
682 }
683
684 fn has_reserved_environment(&self) -> bool {
688 const RESERVED: [&str; 6] = [
689 "NATIVE_IPC_VNEXT_BOOTSTRAP_FD",
690 "NATIVE_IPC_VNEXT_PUBLIC_BOOTSTRAP",
691 "NATIVE_IPC_MACH_NONCE",
692 "NATIVE_IPC_PARENT_PID",
693 "NATIVE_IPC_WINDOWS_PIPE",
694 "NATIVE_IPC_WINDOWS_NONCE",
695 ];
696 self.environment.iter().any(|(key, _)| {
697 RESERVED
698 .iter()
699 .any(|name| key.as_os_str() == std::ffi::OsStr::new(name))
700 })
701 }
702
703 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
704 pub(crate) fn executable(&self) -> &Path {
705 &self.executable
706 }
707
708 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
709 pub(crate) fn arguments(&self) -> &[OsString] {
710 &self.arguments
711 }
712
713 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
714 pub(crate) fn environment(&self) -> &[(OsString, OsString)] {
715 &self.environment
716 }
717}
718
719#[derive(Clone, Debug, Eq, PartialEq)]
721pub struct SessionOptions {
722 deadline: AbsoluteDeadline,
723 limits: SessionLimits,
724 application_payload: Vec<u8>,
725 executable_identity: ExecutableIdentityPolicy,
726 require_atomic_u32: bool,
727 require_atomic_u64: bool,
728}
729
730impl SessionOptions {
731 pub fn new(deadline: AbsoluteDeadline, executable_identity: ExecutableIdentityPolicy) -> Self {
733 Self {
734 deadline,
735 limits: SessionLimits::default(),
736 application_payload: Vec::new(),
737 executable_identity,
738 require_atomic_u32: false,
739 require_atomic_u64: false,
740 }
741 }
742
743 pub fn with_limits(mut self, limits: SessionLimits) -> Self {
745 self.limits = limits;
746 self
747 }
748
749 pub fn with_application_payload(mut self, payload: Vec<u8>) -> Self {
751 self.application_payload = payload;
752 self
753 }
754
755 pub fn require_atomic_u32(mut self) -> Self {
757 self.require_atomic_u32 = true;
758 self
759 }
760
761 pub fn require_atomic_u64(mut self) -> Self {
763 self.require_atomic_u64 = true;
764 self
765 }
766
767 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
768 pub(crate) const fn limits(&self) -> SessionLimits {
769 self.limits
770 }
771
772 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
773 pub(crate) fn application_payload(&self) -> &[u8] {
774 &self.application_payload
775 }
776
777 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
778 pub(crate) const fn requires_atomic_u32(&self) -> bool {
779 self.require_atomic_u32
780 }
781
782 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
783 pub(crate) const fn requires_atomic_u64(&self) -> bool {
784 self.require_atomic_u64
785 }
786
787 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
788 pub(crate) const fn deadline(&self) -> AbsoluteDeadline {
789 self.deadline
790 }
791}
792
793#[derive(Clone, Copy, Debug, Eq, PartialEq)]
795pub enum SessionEndpoint {
796 Coordinator,
798 Receiver,
800}
801
802#[derive(Clone, Copy, Debug, Eq, PartialEq)]
804pub struct RejectionReason(NonZeroU32);
805
806impl RejectionReason {
807 pub const APPLICATION_DECLINED: Self = Self(NonZeroU32::MIN);
809 pub const INCOMPATIBLE_APPLICATION_PROTOCOL: Self =
811 Self(NonZeroU32::new(2).expect("two is nonzero"));
812 pub const APPLICATION_POLICY: Self = Self(NonZeroU32::new(3).expect("three is nonzero"));
814
815 pub const fn application_specific(value: u32) -> Option<Self> {
817 if value < 0x8000_0000 {
818 return None;
819 }
820 match NonZeroU32::new(value) {
821 Some(value) => Some(Self(value)),
822 None => None,
823 }
824 }
825
826 pub const fn get(self) -> u32 {
828 self.0.get()
829 }
830
831 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
832 fn from_wire(value: NonZeroU32) -> Option<Self> {
833 match value.get() {
834 1 => Some(Self::APPLICATION_DECLINED),
835 2 => Some(Self::INCOMPATIBLE_APPLICATION_PROTOCOL),
836 3 => Some(Self::APPLICATION_POLICY),
837 value => Self::application_specific(value),
838 }
839 }
840
841 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
842 const fn as_nonzero(self) -> NonZeroU32 {
843 self.0
844 }
845}
846
847#[derive(Clone, Copy, Debug, Eq, PartialEq)]
849pub enum NegotiationDecision {
850 Accept,
852 Reject(RejectionReason),
854}
855
856pub enum NegotiationOutcome<T> {
858 Accepted(T),
860 Rejected {
862 by: SessionEndpoint,
864 reason: RejectionReason,
866 cleanup: Option<ChildCleanupFacts>,
868 },
869}
870
871#[derive(Clone, Copy, Debug, Eq, PartialEq)]
873pub enum SessionError {
874 BackendUnavailable,
876 InvalidInput,
878 DeadlineExpired,
880 PeerDisconnected,
882 IdentityMismatch,
884 MalformedPeer,
886 Ambiguous,
888 NegotiationFailed,
890 NativeNegotiation(NegotiationError),
892 Control(ControlError),
894 Batch(BatchError),
896 ActiveLimit,
898 PeerPreparationFailed,
900 ActivationFailed,
902 Poisoned,
904 Native,
906}
907
908impl core::fmt::Display for SessionError {
909 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
910 write!(formatter, "session operation failed: {self:?}")
911 }
912}
913
914impl std::error::Error for SessionError {}
915
916#[derive(Clone, Copy, Debug, Eq, PartialEq)]
918pub enum SessionOperation {
919 Bootstrap,
921 Spawn,
923 Negotiate,
925 PollPeer,
927 WaitForExit,
929 Close,
931 Abort,
933 TransferBatch,
935 ReceiveBatch,
937 SendControl,
939 ReceiveControl,
941}
942
943#[derive(Clone, Copy, Debug, Eq, PartialEq)]
945pub enum SessionTransactionState {
946 NotEstablished,
948 Spawned,
950 Negotiating,
952 Ready,
954 TransactionOpen,
959 Poisoned,
961}
962
963#[derive(Clone, Copy, Debug, Eq, PartialEq)]
965pub struct SessionFailure {
966 operation: SessionOperation,
967 transaction_state: SessionTransactionState,
968 reason: SessionError,
969 native_code: Option<i32>,
970 poisoned: bool,
971 peer: Option<PeerStatus>,
972 cleanup: Option<ChildCleanupFacts>,
973}
974
975impl SessionFailure {
976 const fn new(
977 operation: SessionOperation,
978 transaction_state: SessionTransactionState,
979 reason: SessionError,
980 ) -> Self {
981 Self {
982 operation,
983 transaction_state,
984 reason,
985 native_code: None,
986 poisoned: matches!(transaction_state, SessionTransactionState::Poisoned),
987 peer: if matches!(reason, SessionError::PeerDisconnected) {
988 Some(PeerStatus::Disconnected)
989 } else {
990 None
991 },
992 cleanup: None,
993 }
994 }
995
996 const fn with_cleanup(mut self, cleanup: ChildCleanupFacts) -> Self {
997 self.cleanup = Some(cleanup);
998 self
999 }
1000
1001 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1002 const fn with_optional_cleanup(mut self, cleanup: Option<ChildCleanupFacts>) -> Self {
1003 self.cleanup = cleanup;
1004 self
1005 }
1006
1007 const fn with_native_code(mut self, native_code: Option<i32>) -> Self {
1008 self.native_code = native_code;
1009 self
1010 }
1011
1012 const fn with_poisoned(mut self, poisoned: bool) -> Self {
1013 self.poisoned = poisoned;
1014 self
1015 }
1016
1017 pub const fn operation(self) -> SessionOperation {
1019 self.operation
1020 }
1021
1022 pub const fn transaction_state(self) -> SessionTransactionState {
1024 self.transaction_state
1025 }
1026
1027 pub const fn reason(self) -> SessionError {
1029 self.reason
1030 }
1031
1032 pub const fn native_code(self) -> Option<i32> {
1034 self.native_code
1035 }
1036
1037 pub const fn is_poisoned(self) -> bool {
1039 self.poisoned
1040 }
1041
1042 pub const fn peer(self) -> Option<PeerStatus> {
1044 self.peer
1045 }
1046
1047 pub const fn cleanup(self) -> Option<ChildCleanupFacts> {
1049 self.cleanup
1050 }
1051}
1052
1053impl core::fmt::Display for SessionFailure {
1054 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1055 write!(
1056 formatter,
1057 "session {:?} failed in {:?}: {:?}",
1058 self.operation, self.transaction_state, self.reason
1059 )
1060 }
1061}
1062
1063impl std::error::Error for SessionFailure {}
1064
1065#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1067pub struct SessionLimits {
1068 pub max_regions_per_batch: u16,
1070 pub max_region_bytes: u64,
1072 pub max_batch_bytes: u64,
1074 pub max_active_regions: u32,
1076 pub max_active_bytes: u64,
1078 pub max_transactions: u64,
1080 pub max_bootstrap_payload_bytes: u32,
1082 pub max_control_payload_bytes: u32,
1084}
1085
1086impl Default for SessionLimits {
1087 fn default() -> Self {
1088 Self {
1089 max_regions_per_batch: 16,
1090 max_region_bytes: 256 * 1024 * 1024,
1091 max_batch_bytes: 1024 * 1024 * 1024,
1092 max_active_regions: 4096,
1093 max_active_bytes: 8 * 1024 * 1024 * 1024,
1094 max_transactions: 1 << 32,
1095 max_bootstrap_payload_bytes: 1024 * 1024,
1096 max_control_payload_bytes: 1024 * 1024,
1097 }
1098 }
1099}
1100
1101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1103pub enum NegotiationError {
1104 ZeroLimit,
1106 AboveHardMaximum,
1108 NativeSizeNarrowing,
1110 AtomicUnsupported,
1112 InvalidDeadline,
1114}
1115
1116impl core::fmt::Display for NegotiationError {
1117 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1118 write!(formatter, "session negotiation failed: {self:?}")
1119 }
1120}
1121
1122impl std::error::Error for NegotiationError {}
1123
1124impl SessionLimits {
1125 pub fn validate(self) -> Result<Self, NegotiationError> {
1127 self.validate_for_native_max(usize::MAX as u64)
1128 }
1129
1130 fn validate_for_native_max(self, native_usize_max: u64) -> Result<Self, NegotiationError> {
1131 if self.max_regions_per_batch == 0
1132 || self.max_region_bytes == 0
1133 || self.max_batch_bytes == 0
1134 || self.max_active_regions == 0
1135 || self.max_active_bytes == 0
1136 || self.max_transactions == 0
1137 || self.max_bootstrap_payload_bytes == 0
1138 || self.max_control_payload_bytes == 0
1139 {
1140 return Err(NegotiationError::ZeroLimit);
1141 }
1142 if self.max_regions_per_batch > HARD_MAX_REGIONS_PER_BATCH
1143 || self.max_region_bytes > HARD_MAX_REGION_BYTES
1144 || self.max_batch_bytes > HARD_MAX_BATCH_BYTES
1145 || self.max_active_regions > HARD_MAX_ACTIVE_REGIONS
1146 || self.max_active_bytes > HARD_MAX_ACTIVE_BYTES
1147 || self.max_transactions > HARD_MAX_TRANSACTIONS
1148 || self.max_bootstrap_payload_bytes > HARD_MAX_BOOTSTRAP_PAYLOAD_BYTES
1149 || self.max_control_payload_bytes > HARD_MAX_CONTROL_PAYLOAD_BYTES
1150 {
1151 return Err(NegotiationError::AboveHardMaximum);
1152 }
1153 if self.max_region_bytes > native_usize_max
1154 || self.max_batch_bytes > native_usize_max
1155 || self.max_active_bytes > native_usize_max
1156 || u64::from(self.max_bootstrap_payload_bytes) > native_usize_max
1157 || u64::from(self.max_control_payload_bytes) > native_usize_max
1158 {
1159 return Err(NegotiationError::NativeSizeNarrowing);
1160 }
1161 Ok(self)
1162 }
1163
1164 pub fn negotiate(local: Self, peer: Self) -> Result<Self, NegotiationError> {
1166 let local = local.validate()?;
1167 let peer = peer.validate()?;
1168 Self {
1169 max_regions_per_batch: local.max_regions_per_batch.min(peer.max_regions_per_batch),
1170 max_region_bytes: local.max_region_bytes.min(peer.max_region_bytes),
1171 max_batch_bytes: local.max_batch_bytes.min(peer.max_batch_bytes),
1172 max_active_regions: local.max_active_regions.min(peer.max_active_regions),
1173 max_active_bytes: local.max_active_bytes.min(peer.max_active_bytes),
1174 max_transactions: local.max_transactions.min(peer.max_transactions),
1175 max_bootstrap_payload_bytes: local
1176 .max_bootstrap_payload_bytes
1177 .min(peer.max_bootstrap_payload_bytes),
1178 max_control_payload_bytes: local
1179 .max_control_payload_bytes
1180 .min(peer.max_control_payload_bytes),
1181 }
1182 .validate()
1183 }
1184}
1185
1186#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1188pub struct AtomicCapabilities {
1189 atomic_u32_lock_free: bool,
1190 atomic_u32_alignment: usize,
1191 atomic_u64_lock_free: bool,
1192 atomic_u64_alignment: usize,
1193 page_alignment: usize,
1194 cache_line_alignment: usize,
1195}
1196
1197impl AtomicCapabilities {
1198 pub(crate) const fn from_accepted_offer(value: crate::negotiation::AtomicOffer) -> Self {
1199 Self {
1200 atomic_u32_lock_free: value.u32_lock_free,
1201 atomic_u32_alignment: value.u32_alignment as usize,
1202 atomic_u64_lock_free: value.u64_lock_free,
1203 atomic_u64_alignment: value.u64_alignment as usize,
1204 page_alignment: value.page_alignment as usize,
1205 cache_line_alignment: value.cache_line_alignment as usize,
1206 }
1207 }
1208
1209 #[allow(dead_code, reason = "wired into native HELLO discovery in phase 4b")]
1212 pub(crate) fn from_verified_native(
1213 page_alignment: usize,
1214 cache_line_alignment: usize,
1215 atomic_u32_lock_free: bool,
1216 atomic_u64_lock_free: bool,
1217 ) -> Result<Self, NegotiationError> {
1218 let atomic_u32_alignment = core::mem::align_of::<core::sync::atomic::AtomicU32>();
1219 let atomic_u64_alignment = core::mem::align_of::<core::sync::atomic::AtomicU64>();
1220 if !page_alignment.is_power_of_two()
1221 || !cache_line_alignment.is_power_of_two()
1222 || page_alignment < atomic_u32_alignment.max(atomic_u64_alignment)
1223 || cache_line_alignment < atomic_u32_alignment.max(atomic_u64_alignment)
1224 {
1225 return Err(NegotiationError::AtomicUnsupported);
1226 }
1227 Ok(Self {
1228 atomic_u32_lock_free,
1229 atomic_u32_alignment,
1230 atomic_u64_lock_free,
1231 atomic_u64_alignment,
1232 page_alignment,
1233 cache_line_alignment,
1234 })
1235 }
1236
1237 pub fn atomic_u32_lock_free(self) -> bool {
1239 self.atomic_u32_lock_free
1240 }
1241
1242 pub fn atomic_u32_alignment(self) -> usize {
1244 self.atomic_u32_alignment
1245 }
1246
1247 pub fn atomic_u64_lock_free(self) -> bool {
1249 self.atomic_u64_lock_free
1250 }
1251
1252 pub fn atomic_u64_alignment(self) -> usize {
1254 self.atomic_u64_alignment
1255 }
1256
1257 pub fn page_alignment(self) -> usize {
1259 self.page_alignment
1260 }
1261
1262 pub fn cache_line_alignment(self) -> usize {
1264 self.cache_line_alignment
1265 }
1266
1267 #[allow(dead_code, reason = "wired into native HELLO negotiation in phase 4b")]
1269 pub(crate) fn require(
1270 self,
1271 u32_required: bool,
1272 u64_required: bool,
1273 ) -> Result<Self, NegotiationError> {
1274 if (u32_required && !self.atomic_u32_lock_free)
1275 || (u64_required && !self.atomic_u64_lock_free)
1276 {
1277 return Err(NegotiationError::AtomicUnsupported);
1278 }
1279 Ok(self)
1280 }
1281}
1282
1283#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1285pub struct AbsoluteDeadline(Instant);
1286
1287impl AbsoluteDeadline {
1288 pub fn after(duration: Duration) -> Result<Self, NegotiationError> {
1290 if duration.is_zero() {
1291 return Err(NegotiationError::InvalidDeadline);
1292 }
1293 Instant::now()
1294 .checked_add(duration)
1295 .map(Self)
1296 .ok_or(NegotiationError::InvalidDeadline)
1297 }
1298
1299 pub fn remaining(self) -> Duration {
1301 self.0.saturating_duration_since(Instant::now())
1302 }
1303
1304 pub fn is_expired(self) -> bool {
1306 self.remaining().is_zero()
1307 }
1308}
1309
1310#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1311impl<Role, State> Session<Role, State> {
1312 fn from_inner(inner: SessionInner) -> Self {
1313 Self {
1314 inner,
1315 role: PhantomData,
1316 state: PhantomData,
1317 not_sync: PhantomData,
1318 }
1319 }
1320}
1321
1322impl Session<Coordinator, Negotiating> {
1323 pub fn spawn(command: SessionCommand, options: SessionOptions) -> Result<Self, SessionFailure> {
1326 validate_public_options(&options).map_err(|reason| {
1327 SessionFailure::new(
1328 SessionOperation::Spawn,
1329 SessionTransactionState::NotEstablished,
1330 reason,
1331 )
1332 })?;
1333 if command.has_reserved_environment() {
1334 return Err(SessionFailure::new(
1335 SessionOperation::Spawn,
1336 SessionTransactionState::NotEstablished,
1337 SessionError::InvalidInput,
1338 ));
1339 }
1340 #[cfg(target_os = "linux")]
1341 {
1342 let inner =
1343 crate::backend::linux_vnext::spawn::LinuxCoordinatorNegotiatingSession::spawn(
1344 &command, &options,
1345 )
1346 .map_err(|failure| {
1347 let native_code = linux_public_native_code(failure.error);
1348 let transaction_state = match failure.state {
1349 crate::backend::linux_vnext::spawn::LinuxCoordinatorFailureState::NotEstablished => {
1350 SessionTransactionState::NotEstablished
1351 }
1352 crate::backend::linux_vnext::spawn::LinuxCoordinatorFailureState::Spawned => {
1353 SessionTransactionState::Spawned
1354 }
1355 crate::backend::linux_vnext::spawn::LinuxCoordinatorFailureState::Negotiating => {
1356 SessionTransactionState::Negotiating
1357 }
1358 };
1359 SessionFailure::new(
1360 SessionOperation::Spawn,
1361 transaction_state,
1362 failure.error.into(),
1363 )
1364 .with_native_code(native_code)
1365 .with_poisoned(failure.poisoned)
1366 .with_optional_cleanup(failure.cleanup)
1367 })?;
1368 Ok(Self::from_inner(SessionInner::CoordinatorNegotiating(
1369 inner,
1370 )))
1371 }
1372 #[cfg(target_os = "macos")]
1373 {
1374 let inner =
1375 crate::backend::macos::vnext_session::MacCoordinatorNegotiatingSession::spawn(
1376 &command, &options,
1377 )
1378 .map_err(|failure| {
1379 let transaction_state = match failure.state {
1380 crate::backend::macos::vnext_session::MacCoordinatorFailureState::NotEstablished => {
1381 SessionTransactionState::NotEstablished
1382 }
1383 crate::backend::macos::vnext_session::MacCoordinatorFailureState::Spawned => {
1384 SessionTransactionState::Spawned
1385 }
1386 crate::backend::macos::vnext_session::MacCoordinatorFailureState::Negotiating => {
1387 SessionTransactionState::Negotiating
1388 }
1389 };
1390 mac_session_failure(
1391 SessionOperation::Spawn,
1392 transaction_state,
1393 failure.error,
1394 failure.poisoned,
1395 )
1396 .with_optional_cleanup(failure.cleanup)
1397 })?;
1398 Ok(Self::from_inner(SessionInner::CoordinatorNegotiating(
1399 inner,
1400 )))
1401 }
1402 #[cfg(target_os = "windows")]
1403 {
1404 let inner = crate::backend::windows::vnext_session::WindowsCoordinatorNegotiatingSession::spawn(
1405 &command,
1406 &options,
1407 )
1408 .map_err(|failure| {
1409 let transaction_state = match failure.state {
1410 crate::backend::windows::vnext_session::WindowsCoordinatorFailureState::NotEstablished => SessionTransactionState::NotEstablished,
1411 crate::backend::windows::vnext_session::WindowsCoordinatorFailureState::Spawned => SessionTransactionState::Spawned,
1412 crate::backend::windows::vnext_session::WindowsCoordinatorFailureState::Negotiating => SessionTransactionState::Negotiating,
1413 };
1414 windows_session_failure(
1415 SessionOperation::Spawn,
1416 transaction_state,
1417 failure.error,
1418 failure.poisoned,
1419 )
1420 .with_optional_cleanup(failure.cleanup)
1421 })?;
1422 Ok(Self::from_inner(SessionInner::CoordinatorNegotiating(
1423 Box::new(inner),
1424 )))
1425 }
1426 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1427 {
1428 let _ = command;
1429 Err(SessionFailure::new(
1430 SessionOperation::Spawn,
1431 SessionTransactionState::NotEstablished,
1432 SessionError::BackendUnavailable,
1433 ))
1434 }
1435 }
1436
1437 pub fn peer_application_payload(&self) -> &[u8] {
1439 match &self.inner {
1440 #[cfg(target_os = "linux")]
1441 SessionInner::CoordinatorNegotiating(inner) => inner.peer_application_payload(),
1442 #[cfg(target_os = "macos")]
1443 SessionInner::CoordinatorNegotiating(inner) => inner.peer_application_payload(),
1444 #[cfg(target_os = "windows")]
1445 SessionInner::CoordinatorNegotiating(inner) => inner.peer_application_payload(),
1446 #[cfg(target_os = "linux")]
1447 _ => unreachable!("coordinator negotiating typestate owns its exact backend state"),
1448 #[cfg(target_os = "macos")]
1449 _ => unreachable!("coordinator negotiating typestate owns its exact backend state"),
1450 #[cfg(target_os = "windows")]
1451 _ => unreachable!("coordinator negotiating typestate owns its exact backend state"),
1452 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1453 SessionInner::Unavailable => {
1454 unreachable!("unavailable backend cannot construct a session")
1455 }
1456 }
1457 }
1458
1459 pub fn decide(
1461 self,
1462 decision: NegotiationDecision,
1463 ) -> Result<NegotiationOutcome<Session<Coordinator, Ready>>, SessionFailure> {
1464 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1465 let _ = decision;
1466 match self.inner {
1467 #[cfg(target_os = "linux")]
1468 SessionInner::CoordinatorNegotiating(inner) => {
1469 let outcome = inner
1470 .decide(decision_rejection(decision))
1471 .map_err(|failure| {
1472 let native_code = linux_public_native_code(failure.error);
1473 SessionFailure::new(
1474 SessionOperation::Negotiate,
1475 SessionTransactionState::Negotiating,
1476 failure.error.into(),
1477 )
1478 .with_native_code(native_code)
1479 .with_poisoned(failure.poisoned)
1480 .with_optional_cleanup(failure.cleanup)
1481 })?;
1482 map_linux_coordinator_outcome(outcome)
1483 }
1484 #[cfg(target_os = "macos")]
1485 SessionInner::CoordinatorNegotiating(inner) => {
1486 let outcome = inner
1487 .decide(decision_rejection(decision))
1488 .map_err(|failure| {
1489 mac_session_failure(
1490 SessionOperation::Negotiate,
1491 SessionTransactionState::Negotiating,
1492 failure.error,
1493 failure.poisoned,
1494 )
1495 .with_optional_cleanup(failure.cleanup)
1496 })?;
1497 map_mac_coordinator_outcome(outcome)
1498 }
1499 #[cfg(target_os = "windows")]
1500 SessionInner::CoordinatorNegotiating(inner) => {
1501 let outcome = (*inner)
1502 .decide(decision_rejection(decision))
1503 .map_err(|failure| {
1504 windows_session_failure(
1505 SessionOperation::Negotiate,
1506 SessionTransactionState::Negotiating,
1507 failure.error,
1508 failure.poisoned,
1509 )
1510 .with_optional_cleanup(failure.cleanup)
1511 })?;
1512 map_windows_coordinator_outcome(outcome)
1513 }
1514 #[cfg(target_os = "linux")]
1515 _ => unreachable!("coordinator negotiating typestate owns its exact backend state"),
1516 #[cfg(target_os = "macos")]
1517 _ => unreachable!("coordinator negotiating typestate owns its exact backend state"),
1518 #[cfg(target_os = "windows")]
1519 _ => unreachable!("coordinator negotiating typestate owns its exact backend state"),
1520 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1521 SessionInner::Unavailable => {
1522 unreachable!("unavailable backend cannot construct a session")
1523 }
1524 }
1525 }
1526}
1527
1528impl Session<Receiver, Negotiating> {
1529 pub fn from_bootstrap(
1531 bootstrap: ReceiverBootstrap,
1532 options: SessionOptions,
1533 ) -> Result<Self, SessionFailure> {
1534 validate_public_options(&options).map_err(|reason| {
1535 SessionFailure::new(
1536 SessionOperation::Bootstrap,
1537 SessionTransactionState::NotEstablished,
1538 reason,
1539 )
1540 })?;
1541 #[cfg(target_os = "linux")]
1542 {
1543 let inner = crate::backend::linux_vnext::spawn::LinuxReceiverNegotiatingSession::from_inherited_bootstrap(
1544 bootstrap.inherited,
1545 options.limits,
1546 options.application_payload,
1547 options.require_atomic_u32,
1548 options.require_atomic_u64,
1549 options.deadline,
1550 )
1551 .map_err(|error| {
1552 SessionFailure::new(
1553 SessionOperation::Bootstrap,
1554 SessionTransactionState::Negotiating,
1555 error.into(),
1556 )
1557 .with_native_code(linux_public_native_code(error))
1558 .with_poisoned(true)
1559 })?;
1560 Ok(Self::from_inner(SessionInner::ReceiverNegotiating(inner)))
1561 }
1562 #[cfg(target_os = "macos")]
1563 {
1564 let _bootstrap = bootstrap;
1565 let inner =
1566 crate::backend::macos::vnext_session::MacReceiverNegotiatingSession::from_environment(
1567 options.limits,
1568 options.application_payload,
1569 options.require_atomic_u32,
1570 options.require_atomic_u64,
1571 options.deadline,
1572 )
1573 .map_err(|error| {
1574 let invalid_input = matches!(
1578 error,
1579 crate::backend::macos::vnext_session::MacPublicSessionError::InvalidInput
1580 );
1581 let state = if invalid_input {
1582 SessionTransactionState::NotEstablished
1583 } else {
1584 SessionTransactionState::Negotiating
1585 };
1586 mac_session_failure(
1587 SessionOperation::Bootstrap,
1588 state,
1589 error,
1590 !invalid_input,
1591 )
1592 })?;
1593 Ok(Self::from_inner(SessionInner::ReceiverNegotiating(inner)))
1594 }
1595 #[cfg(target_os = "windows")]
1596 {
1597 let _bootstrap = bootstrap;
1598 let inner = crate::backend::windows::vnext_session::WindowsReceiverNegotiatingSession::from_environment(&options)
1599 .map_err(|error| {
1600 let invalid_input = matches!(
1604 error,
1605 crate::backend::windows::vnext_session::WindowsPublicSessionError::InvalidInput
1606 );
1607 let state = if invalid_input {
1608 SessionTransactionState::NotEstablished
1609 } else {
1610 SessionTransactionState::Negotiating
1611 };
1612 windows_session_failure(
1613 SessionOperation::Bootstrap,
1614 state,
1615 error,
1616 !invalid_input,
1617 )
1618 })?;
1619 Ok(Self::from_inner(SessionInner::ReceiverNegotiating(
1620 Box::new(inner),
1621 )))
1622 }
1623 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1624 {
1625 let _ = bootstrap;
1626 Err(SessionFailure::new(
1627 SessionOperation::Bootstrap,
1628 SessionTransactionState::NotEstablished,
1629 SessionError::BackendUnavailable,
1630 ))
1631 }
1632 }
1633
1634 pub fn peer_application_payload(&self) -> &[u8] {
1636 match &self.inner {
1637 #[cfg(target_os = "linux")]
1638 SessionInner::ReceiverNegotiating(inner) => inner.peer_application_payload(),
1639 #[cfg(target_os = "macos")]
1640 SessionInner::ReceiverNegotiating(inner) => inner.peer_application_payload(),
1641 #[cfg(target_os = "windows")]
1642 SessionInner::ReceiverNegotiating(inner) => inner.peer_application_payload(),
1643 #[cfg(target_os = "linux")]
1644 _ => unreachable!("receiver negotiating typestate owns its exact backend state"),
1645 #[cfg(target_os = "macos")]
1646 _ => unreachable!("receiver negotiating typestate owns its exact backend state"),
1647 #[cfg(target_os = "windows")]
1648 _ => unreachable!("receiver negotiating typestate owns its exact backend state"),
1649 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1650 SessionInner::Unavailable => {
1651 unreachable!("unavailable backend cannot construct a session")
1652 }
1653 }
1654 }
1655
1656 pub fn decide_after_coordinator(
1658 self,
1659 decide: impl FnOnce(&[u8]) -> NegotiationDecision,
1660 ) -> Result<NegotiationOutcome<Session<Receiver, Ready>>, SessionFailure> {
1661 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1662 let _ = decide;
1663 match self.inner {
1664 #[cfg(target_os = "linux")]
1665 SessionInner::ReceiverNegotiating(inner) => {
1666 let outcome = inner
1667 .decide_after_coordinator(|payload| decision_rejection(decide(payload)))
1668 .map_err(|error| {
1669 SessionFailure::new(
1670 SessionOperation::Negotiate,
1671 SessionTransactionState::Negotiating,
1672 error.into(),
1673 )
1674 .with_native_code(linux_public_native_code(error))
1675 .with_poisoned(true)
1676 })?;
1677 map_linux_receiver_outcome(outcome)
1678 }
1679 #[cfg(target_os = "macos")]
1680 SessionInner::ReceiverNegotiating(inner) => {
1681 let outcome = inner
1682 .decide_after_coordinator(|payload| decision_rejection(decide(payload)))
1683 .map_err(|error| {
1684 mac_session_failure(
1685 SessionOperation::Negotiate,
1686 SessionTransactionState::Negotiating,
1687 error,
1688 true,
1689 )
1690 })?;
1691 map_mac_receiver_outcome(outcome)
1692 }
1693 #[cfg(target_os = "windows")]
1694 SessionInner::ReceiverNegotiating(inner) => {
1695 let outcome = (*inner)
1696 .decide_after_coordinator(|payload| decision_rejection(decide(payload)))
1697 .map_err(|error| {
1698 windows_session_failure(
1699 SessionOperation::Negotiate,
1700 SessionTransactionState::Negotiating,
1701 error,
1702 true,
1703 )
1704 })?;
1705 map_windows_receiver_outcome(outcome)
1706 }
1707 #[cfg(target_os = "linux")]
1708 _ => unreachable!("receiver negotiating typestate owns its exact backend state"),
1709 #[cfg(target_os = "macos")]
1710 _ => unreachable!("receiver negotiating typestate owns its exact backend state"),
1711 #[cfg(target_os = "windows")]
1712 _ => unreachable!("receiver negotiating typestate owns its exact backend state"),
1713 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1714 SessionInner::Unavailable => {
1715 unreachable!("unavailable backend cannot construct a session")
1716 }
1717 }
1718 }
1719}
1720
1721impl Session<Coordinator, Ready> {
1722 #[cfg(all(test, target_os = "linux"))]
1723 pub(crate) fn fail_next_cleanup_signal_for_test(&self, code: i32) {
1724 match &self.inner {
1725 SessionInner::CoordinatorReady(inner) => {
1726 inner.fail_next_cleanup_signal_for_test(code);
1727 }
1728 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1729 }
1730 }
1731
1732 pub fn negotiated_limits(&self) -> SessionLimits {
1734 match &self.inner {
1735 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1736 SessionInner::CoordinatorReady(inner) => inner.limits(),
1737 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1738 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1739 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1740 SessionInner::Unavailable => {
1741 unreachable!("unavailable backend cannot construct a session")
1742 }
1743 }
1744 }
1745
1746 pub fn atomic_capabilities(&self) -> AtomicCapabilities {
1748 match &self.inner {
1749 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1750 SessionInner::CoordinatorReady(inner) => inner.atomics(),
1751 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1752 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1753 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1754 SessionInner::Unavailable => {
1755 unreachable!("unavailable backend cannot construct a session")
1756 }
1757 }
1758 }
1759
1760 pub fn protocol_version(&self) -> ProtocolVersion {
1762 match &self.inner {
1763 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1764 SessionInner::CoordinatorReady(inner) => inner.protocol_version(),
1765 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1766 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1767 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1768 SessionInner::Unavailable => {
1769 unreachable!("unavailable backend cannot construct a session")
1770 }
1771 }
1772 }
1773
1774 pub fn state(&self) -> SessionState {
1776 match &self.inner {
1777 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1778 SessionInner::CoordinatorReady(inner) => inner.state(),
1779 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1780 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1781 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1782 SessionInner::Unavailable => {
1783 unreachable!("unavailable backend cannot construct a session")
1784 }
1785 }
1786 }
1787
1788 pub fn active_leases(&self) -> ActiveLeaseFacts {
1790 match &self.inner {
1791 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1792 SessionInner::CoordinatorReady(inner) => inner.active_leases(),
1793 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1794 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1795 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1796 SessionInner::Unavailable => {
1797 unreachable!("unavailable backend cannot construct a session")
1798 }
1799 }
1800 }
1801
1802 pub fn poll_peer(&mut self) -> Result<PeerStatus, SessionFailure> {
1804 match &mut self.inner {
1805 #[cfg(target_os = "linux")]
1806 SessionInner::CoordinatorReady(inner) => {
1807 let result = inner.poll_peer();
1808 let state = inner.state();
1809 result
1810 .map_err(|error| linux_ready_failure(SessionOperation::PollPeer, state, error))
1811 }
1812 #[cfg(target_os = "macos")]
1813 SessionInner::CoordinatorReady(inner) => {
1814 let result = inner.poll_peer();
1815 let state = inner.state();
1816 result.map_err(|error| mac_ready_failure(SessionOperation::PollPeer, state, error))
1817 }
1818 #[cfg(target_os = "windows")]
1819 SessionInner::CoordinatorReady(inner) => {
1820 let result = inner.poll_peer();
1821 let state = inner.state();
1822 result.map_err(|error| {
1823 windows_ready_failure(SessionOperation::PollPeer, state, error)
1824 })
1825 }
1826 #[cfg(target_os = "linux")]
1827 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1828 #[cfg(target_os = "macos")]
1829 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1830 #[cfg(target_os = "windows")]
1831 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1832 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1833 SessionInner::Unavailable => Err(SessionFailure::new(
1834 SessionOperation::PollPeer,
1835 SessionTransactionState::NotEstablished,
1836 SessionError::BackendUnavailable,
1837 )),
1838 }
1839 }
1840
1841 pub fn wait_for_exit(&mut self, deadline: AbsoluteDeadline) -> ChildCleanupFacts {
1843 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1844 let _ = deadline;
1845 match &mut self.inner {
1846 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1847 SessionInner::CoordinatorReady(inner) => inner.wait_for_exit(deadline),
1848 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1849 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1850 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1851 SessionInner::Unavailable => {
1852 ChildCleanupFacts::new(None, DescendantCleanupStatus::NotEstablished, None)
1853 }
1854 }
1855 }
1856
1857 pub fn try_close(mut self, deadline: AbsoluteDeadline) -> CoordinatorCloseOutcome {
1859 let facts = self.active_leases();
1860 if !facts.is_empty() {
1861 return CoordinatorCloseOutcome::ActiveLeases {
1862 session: self,
1863 facts,
1864 };
1865 }
1866 let cleanup = self.wait_for_exit(deadline);
1867 if !cleanup.direct_child_complete() {
1868 let reason = if cleanup.native_error().is_some() {
1869 SessionError::Native
1870 } else {
1871 SessionError::DeadlineExpired
1872 };
1873 let failure = SessionFailure::new(
1874 SessionOperation::Close,
1875 if self.state() == SessionState::Poisoned {
1876 SessionTransactionState::Poisoned
1877 } else {
1878 SessionTransactionState::Ready
1879 },
1880 reason,
1881 )
1882 .with_native_code(cleanup.native_error())
1883 .with_poisoned(self.state() == SessionState::Poisoned)
1884 .with_cleanup(cleanup);
1885 return CoordinatorCloseOutcome::CleanupPending {
1886 session: self,
1887 facts: cleanup,
1888 failure,
1889 };
1890 }
1891 let close: Result<(), SessionFailure> = match &mut self.inner {
1892 #[cfg(target_os = "linux")]
1893 SessionInner::CoordinatorReady(inner) => {
1894 let result = inner.close_resources();
1895 let state = inner.state();
1896 result.map_err(|error| {
1897 linux_ready_failure(SessionOperation::Close, state, error).with_cleanup(cleanup)
1898 })
1899 }
1900 #[cfg(target_os = "macos")]
1901 SessionInner::CoordinatorReady(inner) => {
1902 let result = inner.close_resources();
1903 let state = inner.state();
1904 result.map_err(|error| {
1905 mac_ready_failure(SessionOperation::Close, state, error).with_cleanup(cleanup)
1906 })
1907 }
1908 #[cfg(target_os = "windows")]
1909 SessionInner::CoordinatorReady(inner) => {
1910 let result = inner.close_resources();
1911 let state = inner.state();
1912 result.map_err(|error| {
1913 windows_ready_failure(SessionOperation::Close, state, error)
1914 .with_cleanup(cleanup)
1915 })
1916 }
1917 #[cfg(target_os = "linux")]
1918 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1919 #[cfg(target_os = "macos")]
1920 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1921 #[cfg(target_os = "windows")]
1922 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1923 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1924 SessionInner::Unavailable => Err(SessionFailure::new(
1925 SessionOperation::Close,
1926 SessionTransactionState::NotEstablished,
1927 SessionError::BackendUnavailable,
1928 )
1929 .with_cleanup(cleanup)),
1930 };
1931 if let Err(error) = close {
1932 return CoordinatorCloseOutcome::Failed {
1933 session: self,
1934 error,
1935 };
1936 }
1937 CoordinatorCloseOutcome::Closed(cleanup)
1938 }
1939
1940 pub fn abort(mut self, deadline: AbsoluteDeadline) -> CoordinatorAbortOutcome {
1942 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1943 let _ = deadline;
1944 let cleanup = match &mut self.inner {
1945 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1946 SessionInner::CoordinatorReady(inner) => inner.abort(deadline),
1947 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
1948 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
1949 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1950 SessionInner::Unavailable => {
1951 ChildCleanupFacts::new(None, DescendantCleanupStatus::NotEstablished, None)
1952 }
1953 };
1954 let failure = if cleanup.direct_child_complete() {
1955 None
1956 } else {
1957 let reason = if cleanup.native_error().is_some() {
1958 SessionError::Native
1959 } else {
1960 SessionError::DeadlineExpired
1961 };
1962 Some(
1963 SessionFailure::new(
1964 SessionOperation::Abort,
1965 SessionTransactionState::Poisoned,
1966 reason,
1967 )
1968 .with_native_code(cleanup.native_error())
1969 .with_poisoned(true)
1970 .with_cleanup(cleanup),
1971 )
1972 };
1973 CoordinatorAbortOutcome { cleanup, failure }
1974 }
1975
1976 pub fn new_transfer_batch(&self) -> Result<TransferBatch, BatchError> {
1978 let limits = self.negotiated_limits();
1979 TransferBatch::new(
1980 limits.max_regions_per_batch,
1981 limits.max_region_bytes,
1982 limits.max_batch_bytes,
1983 )
1984 }
1985
1986 pub fn transfer_batch(
1988 &mut self,
1989 batch: TransferBatch,
1990 deadline: AbsoluteDeadline,
1991 ) -> Result<ActiveRegionSet, SessionFailure> {
1992 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
1993 let _ = (batch, deadline);
1994 match &mut self.inner {
1995 #[cfg(target_os = "linux")]
1996 SessionInner::CoordinatorReady(inner) => {
1997 let result = inner.transfer_batch(batch, deadline);
1998 let state = inner.state();
1999 result.map_err(|error| {
2000 linux_ready_batch_failure(SessionOperation::TransferBatch, state, error)
2001 })
2002 }
2003 #[cfg(target_os = "macos")]
2004 SessionInner::CoordinatorReady(inner) => {
2005 let result = inner.transfer_batch(batch, deadline);
2006 let state = inner.state();
2007 result.map_err(|error| {
2008 mac_ready_failure(SessionOperation::TransferBatch, state, error)
2009 })
2010 }
2011 #[cfg(target_os = "windows")]
2012 SessionInner::CoordinatorReady(inner) => {
2013 let result = inner.transfer_batch(batch, deadline);
2014 let state = inner.state();
2015 result.map_err(|error| {
2016 windows_ready_failure(SessionOperation::TransferBatch, state, error)
2017 })
2018 }
2019 #[cfg(target_os = "linux")]
2020 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2021 #[cfg(target_os = "macos")]
2022 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2023 #[cfg(target_os = "windows")]
2024 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2025 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2026 SessionInner::Unavailable => {
2027 unreachable!("unavailable backend cannot construct a session")
2028 }
2029 }
2030 }
2031
2032 pub fn send_control(
2034 &mut self,
2035 kind: u32,
2036 payload: &[u8],
2037 deadline: AbsoluteDeadline,
2038 ) -> Result<(), SessionFailure> {
2039 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2040 let _ = (kind, payload, deadline);
2041 match &mut self.inner {
2042 #[cfg(target_os = "linux")]
2043 SessionInner::CoordinatorReady(inner) => {
2044 let result = inner.send_control(kind, payload, deadline);
2045 let state = inner.state();
2046 result.map_err(|error| {
2047 linux_ready_failure(SessionOperation::SendControl, state, error)
2048 })
2049 }
2050 #[cfg(target_os = "macos")]
2051 SessionInner::CoordinatorReady(inner) => {
2052 let result = inner.send_control(kind, payload, deadline);
2053 let state = inner.state();
2054 result
2055 .map_err(|error| mac_ready_failure(SessionOperation::SendControl, state, error))
2056 }
2057 #[cfg(target_os = "windows")]
2058 SessionInner::CoordinatorReady(inner) => {
2059 let result = inner.send_control(kind, payload, deadline);
2060 let state = inner.state();
2061 result.map_err(|error| {
2062 windows_ready_failure(SessionOperation::SendControl, state, error)
2063 })
2064 }
2065 #[cfg(target_os = "linux")]
2066 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2067 #[cfg(target_os = "macos")]
2068 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2069 #[cfg(target_os = "windows")]
2070 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2071 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2072 SessionInner::Unavailable => {
2073 unreachable!("unavailable backend cannot construct a session")
2074 }
2075 }
2076 }
2077
2078 pub fn receive_control(
2080 &mut self,
2081 deadline: AbsoluteDeadline,
2082 ) -> Result<ControlFrame, SessionFailure> {
2083 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2084 let _ = deadline;
2085 match &mut self.inner {
2086 #[cfg(target_os = "linux")]
2087 SessionInner::CoordinatorReady(inner) => {
2088 let result = inner.receive_control(deadline);
2089 let state = inner.state();
2090 result.map_err(|error| {
2091 linux_ready_failure(SessionOperation::ReceiveControl, state, error)
2092 })
2093 }
2094 #[cfg(target_os = "macos")]
2095 SessionInner::CoordinatorReady(inner) => {
2096 let result = inner.receive_control(deadline);
2097 let state = inner.state();
2098 result.map_err(|error| {
2099 mac_ready_failure(SessionOperation::ReceiveControl, state, error)
2100 })
2101 }
2102 #[cfg(target_os = "windows")]
2103 SessionInner::CoordinatorReady(inner) => {
2104 let result = inner.receive_control(deadline);
2105 let state = inner.state();
2106 result.map_err(|error| {
2107 windows_ready_failure(SessionOperation::ReceiveControl, state, error)
2108 })
2109 }
2110 #[cfg(target_os = "linux")]
2111 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2112 #[cfg(target_os = "macos")]
2113 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2114 #[cfg(target_os = "windows")]
2115 _ => unreachable!("coordinator ready typestate owns its exact backend state"),
2116 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2117 SessionInner::Unavailable => {
2118 unreachable!("unavailable backend cannot construct a session")
2119 }
2120 }
2121 }
2122}
2123
2124impl Session<Receiver, Ready> {
2125 pub fn negotiated_limits(&self) -> SessionLimits {
2127 match &self.inner {
2128 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2129 SessionInner::ReceiverReady(inner) => inner.limits(),
2130 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2131 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2132 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2133 SessionInner::Unavailable => {
2134 unreachable!("unavailable backend cannot construct a session")
2135 }
2136 }
2137 }
2138
2139 pub fn atomic_capabilities(&self) -> AtomicCapabilities {
2141 match &self.inner {
2142 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2143 SessionInner::ReceiverReady(inner) => inner.atomics(),
2144 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2145 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2146 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2147 SessionInner::Unavailable => {
2148 unreachable!("unavailable backend cannot construct a session")
2149 }
2150 }
2151 }
2152
2153 pub fn protocol_version(&self) -> ProtocolVersion {
2155 match &self.inner {
2156 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2157 SessionInner::ReceiverReady(inner) => inner.protocol_version(),
2158 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2159 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2160 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2161 SessionInner::Unavailable => {
2162 unreachable!("unavailable backend cannot construct a session")
2163 }
2164 }
2165 }
2166
2167 pub fn state(&self) -> SessionState {
2169 match &self.inner {
2170 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2171 SessionInner::ReceiverReady(inner) => inner.state(),
2172 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2173 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2174 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2175 SessionInner::Unavailable => {
2176 unreachable!("unavailable backend cannot construct a session")
2177 }
2178 }
2179 }
2180
2181 pub fn active_leases(&self) -> ActiveLeaseFacts {
2183 match &self.inner {
2184 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2185 SessionInner::ReceiverReady(inner) => inner.active_leases(),
2186 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2187 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2188 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2189 SessionInner::Unavailable => {
2190 unreachable!("unavailable backend cannot construct a session")
2191 }
2192 }
2193 }
2194
2195 pub fn poll_peer(&mut self) -> Result<PeerStatus, SessionFailure> {
2197 match &mut self.inner {
2198 #[cfg(target_os = "linux")]
2199 SessionInner::ReceiverReady(inner) => {
2200 let result = inner.poll_peer();
2201 let state = inner.state();
2202 result
2203 .map_err(|error| linux_ready_failure(SessionOperation::PollPeer, state, error))
2204 }
2205 #[cfg(target_os = "macos")]
2206 SessionInner::ReceiverReady(inner) => {
2207 let result = inner.poll_peer();
2208 let state = inner.state();
2209 result.map_err(|error| mac_ready_failure(SessionOperation::PollPeer, state, error))
2210 }
2211 #[cfg(target_os = "windows")]
2212 SessionInner::ReceiverReady(inner) => {
2213 let result = inner.poll_peer();
2214 let state = inner.state();
2215 result.map_err(|error| {
2216 windows_ready_failure(SessionOperation::PollPeer, state, error)
2217 })
2218 }
2219 #[cfg(target_os = "linux")]
2220 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2221 #[cfg(target_os = "macos")]
2222 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2223 #[cfg(target_os = "windows")]
2224 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2225 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2226 SessionInner::Unavailable => Err(SessionFailure::new(
2227 SessionOperation::PollPeer,
2228 SessionTransactionState::NotEstablished,
2229 SessionError::BackendUnavailable,
2230 )),
2231 }
2232 }
2233
2234 pub fn wait_for_exit(
2236 &mut self,
2237 deadline: AbsoluteDeadline,
2238 ) -> Result<PeerStatus, SessionFailure> {
2239 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2240 let _ = deadline;
2241 match &mut self.inner {
2242 #[cfg(target_os = "linux")]
2243 SessionInner::ReceiverReady(inner) => {
2244 let result = inner.wait_for_exit(deadline);
2245 let state = inner.state();
2246 result.map_err(|error| {
2247 linux_ready_failure(SessionOperation::WaitForExit, state, error)
2248 })
2249 }
2250 #[cfg(target_os = "macos")]
2251 SessionInner::ReceiverReady(inner) => {
2252 let result = inner.wait_for_exit(deadline);
2253 let state = inner.state();
2254 result
2255 .map_err(|error| mac_ready_failure(SessionOperation::WaitForExit, state, error))
2256 }
2257 #[cfg(target_os = "windows")]
2258 SessionInner::ReceiverReady(inner) => {
2259 let result = inner.wait_for_exit(deadline);
2260 let state = inner.state();
2261 result.map_err(|error| {
2262 windows_ready_failure(SessionOperation::WaitForExit, state, error)
2263 })
2264 }
2265 #[cfg(target_os = "linux")]
2266 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2267 #[cfg(target_os = "macos")]
2268 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2269 #[cfg(target_os = "windows")]
2270 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2271 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2272 SessionInner::Unavailable => Err(SessionFailure::new(
2273 SessionOperation::WaitForExit,
2274 SessionTransactionState::NotEstablished,
2275 SessionError::BackendUnavailable,
2276 )),
2277 }
2278 }
2279
2280 pub fn try_close(mut self) -> ReceiverCloseOutcome {
2282 let facts = self.active_leases();
2283 if !facts.is_empty() {
2284 return ReceiverCloseOutcome::ActiveLeases {
2285 session: self,
2286 facts,
2287 };
2288 }
2289 let close: Result<(), SessionFailure> = match &mut self.inner {
2290 #[cfg(target_os = "linux")]
2291 SessionInner::ReceiverReady(inner) => {
2292 let result = inner.close_resources();
2293 let state = inner.state();
2294 result.map_err(|error| linux_ready_failure(SessionOperation::Close, state, error))
2295 }
2296 #[cfg(target_os = "macos")]
2297 SessionInner::ReceiverReady(inner) => {
2298 let result = inner.close_resources();
2299 let state = inner.state();
2300 result.map_err(|error| mac_ready_failure(SessionOperation::Close, state, error))
2301 }
2302 #[cfg(target_os = "windows")]
2303 SessionInner::ReceiverReady(inner) => {
2304 let result = inner.close_resources();
2305 let state = inner.state();
2306 result.map_err(|error| windows_ready_failure(SessionOperation::Close, state, error))
2307 }
2308 #[cfg(target_os = "linux")]
2309 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2310 #[cfg(target_os = "macos")]
2311 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2312 #[cfg(target_os = "windows")]
2313 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2314 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2315 SessionInner::Unavailable => Err(SessionFailure::new(
2316 SessionOperation::Close,
2317 SessionTransactionState::NotEstablished,
2318 SessionError::BackendUnavailable,
2319 )),
2320 };
2321 if let Err(error) = close {
2322 return ReceiverCloseOutcome::Failed {
2323 session: self,
2324 error,
2325 };
2326 }
2327 ReceiverCloseOutcome::Closed
2328 }
2329
2330 pub fn abort(mut self) {
2332 match &mut self.inner {
2333 #[cfg(target_os = "linux")]
2334 SessionInner::ReceiverReady(inner) => inner.abort(),
2335 #[cfg(target_os = "macos")]
2336 SessionInner::ReceiverReady(inner) => inner.abort(),
2337 #[cfg(target_os = "windows")]
2338 SessionInner::ReceiverReady(inner) => inner.abort(),
2339 #[cfg(target_os = "linux")]
2340 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2341 #[cfg(target_os = "macos")]
2342 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2343 #[cfg(target_os = "windows")]
2344 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2345 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2346 SessionInner::Unavailable => {}
2347 }
2348 }
2349
2350 pub fn receive_batch(
2352 &mut self,
2353 expected: ExpectedBatch,
2354 deadline: AbsoluteDeadline,
2355 ) -> Result<ActiveRegionSet, SessionFailure> {
2356 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2357 let _ = (expected, deadline);
2358 match &mut self.inner {
2359 #[cfg(target_os = "linux")]
2360 SessionInner::ReceiverReady(inner) => {
2361 let result = inner.receive_batch(expected, deadline);
2362 let state = inner.state();
2363 result.map_err(|error| {
2364 linux_ready_batch_failure(SessionOperation::ReceiveBatch, state, error)
2365 })
2366 }
2367 #[cfg(target_os = "macos")]
2368 SessionInner::ReceiverReady(inner) => {
2369 let result = inner.receive_batch(expected, deadline);
2370 let state = inner.state();
2371 result.map_err(|error| {
2372 mac_ready_failure(SessionOperation::ReceiveBatch, state, error)
2373 })
2374 }
2375 #[cfg(target_os = "windows")]
2376 SessionInner::ReceiverReady(inner) => {
2377 let result = inner.receive_batch(expected, deadline);
2378 let state = inner.state();
2379 result.map_err(|error| {
2380 windows_ready_failure(SessionOperation::ReceiveBatch, state, error)
2381 })
2382 }
2383 #[cfg(target_os = "linux")]
2384 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2385 #[cfg(target_os = "macos")]
2386 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2387 #[cfg(target_os = "windows")]
2388 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2389 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2390 SessionInner::Unavailable => {
2391 unreachable!("unavailable backend cannot construct a session")
2392 }
2393 }
2394 }
2395
2396 pub fn send_control(
2398 &mut self,
2399 kind: u32,
2400 payload: &[u8],
2401 deadline: AbsoluteDeadline,
2402 ) -> Result<(), SessionFailure> {
2403 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2404 let _ = (kind, payload, deadline);
2405 match &mut self.inner {
2406 #[cfg(target_os = "linux")]
2407 SessionInner::ReceiverReady(inner) => {
2408 let result = inner.send_control(kind, payload, deadline);
2409 let state = inner.state();
2410 result.map_err(|error| {
2411 linux_ready_failure(SessionOperation::SendControl, state, error)
2412 })
2413 }
2414 #[cfg(target_os = "macos")]
2415 SessionInner::ReceiverReady(inner) => {
2416 let result = inner.send_control(kind, payload, deadline);
2417 let state = inner.state();
2418 result
2419 .map_err(|error| mac_ready_failure(SessionOperation::SendControl, state, error))
2420 }
2421 #[cfg(target_os = "windows")]
2422 SessionInner::ReceiverReady(inner) => {
2423 let result = inner.send_control(kind, payload, deadline);
2424 let state = inner.state();
2425 result.map_err(|error| {
2426 windows_ready_failure(SessionOperation::SendControl, state, error)
2427 })
2428 }
2429 #[cfg(target_os = "linux")]
2430 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2431 #[cfg(target_os = "macos")]
2432 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2433 #[cfg(target_os = "windows")]
2434 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2435 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2436 SessionInner::Unavailable => {
2437 unreachable!("unavailable backend cannot construct a session")
2438 }
2439 }
2440 }
2441
2442 pub fn receive_control(
2444 &mut self,
2445 deadline: AbsoluteDeadline,
2446 ) -> Result<ControlFrame, SessionFailure> {
2447 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2448 let _ = deadline;
2449 match &mut self.inner {
2450 #[cfg(target_os = "linux")]
2451 SessionInner::ReceiverReady(inner) => {
2452 let result = inner.receive_control(deadline);
2453 let state = inner.state();
2454 result.map_err(|error| {
2455 linux_ready_failure(SessionOperation::ReceiveControl, state, error)
2456 })
2457 }
2458 #[cfg(target_os = "macos")]
2459 SessionInner::ReceiverReady(inner) => {
2460 let result = inner.receive_control(deadline);
2461 let state = inner.state();
2462 result.map_err(|error| {
2463 mac_ready_failure(SessionOperation::ReceiveControl, state, error)
2464 })
2465 }
2466 #[cfg(target_os = "windows")]
2467 SessionInner::ReceiverReady(inner) => {
2468 let result = inner.receive_control(deadline);
2469 let state = inner.state();
2470 result.map_err(|error| {
2471 windows_ready_failure(SessionOperation::ReceiveControl, state, error)
2472 })
2473 }
2474 #[cfg(target_os = "linux")]
2475 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2476 #[cfg(target_os = "macos")]
2477 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2478 #[cfg(target_os = "windows")]
2479 _ => unreachable!("receiver ready typestate owns its exact backend state"),
2480 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
2481 SessionInner::Unavailable => {
2482 unreachable!("unavailable backend cannot construct a session")
2483 }
2484 }
2485 }
2486}
2487
2488fn validate_public_options(options: &SessionOptions) -> Result<(), SessionError> {
2489 if options.deadline.is_expired()
2490 || options.application_payload.len() > options.limits.max_bootstrap_payload_bytes as usize
2491 {
2492 return Err(if options.deadline.is_expired() {
2493 SessionError::DeadlineExpired
2494 } else {
2495 SessionError::InvalidInput
2496 });
2497 }
2498 match options.executable_identity {
2499 ExecutableIdentityPolicy::ExactOpenedFile => {}
2500 }
2501 options
2502 .limits
2503 .validate()
2504 .map(|_| ())
2505 .map_err(SessionError::NativeNegotiation)
2506}
2507
2508#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
2509const fn decision_rejection(decision: NegotiationDecision) -> Option<NonZeroU32> {
2510 match decision {
2511 NegotiationDecision::Accept => None,
2512 NegotiationDecision::Reject(reason) => Some(reason.as_nonzero()),
2513 }
2514}
2515
2516#[cfg(target_os = "macos")]
2517fn map_mac_role(role: crate::backend::macos::vnext_session::MacNegotiationRole) -> SessionEndpoint {
2518 match role {
2519 crate::backend::macos::vnext_session::MacNegotiationRole::Coordinator => {
2520 SessionEndpoint::Coordinator
2521 }
2522 crate::backend::macos::vnext_session::MacNegotiationRole::Receiver => {
2523 SessionEndpoint::Receiver
2524 }
2525 }
2526}
2527
2528#[cfg(target_os = "macos")]
2529fn map_mac_coordinator_outcome(
2530 outcome: crate::backend::macos::vnext_session::MacNegotiationOutcome<
2531 crate::backend::macos::vnext_session::MacCoordinatorReadySession,
2532 >,
2533) -> Result<NegotiationOutcome<Session<Coordinator, Ready>>, SessionFailure> {
2534 match outcome {
2535 crate::backend::macos::vnext_session::MacNegotiationOutcome::Accepted(inner) => {
2536 Ok(NegotiationOutcome::Accepted(Session::from_inner(
2537 SessionInner::CoordinatorReady(inner),
2538 )))
2539 }
2540 crate::backend::macos::vnext_session::MacNegotiationOutcome::Rejected {
2541 by,
2542 reason,
2543 cleanup,
2544 } => {
2545 let reason = RejectionReason::from_wire(reason).ok_or_else(|| {
2546 let failure = SessionFailure::new(
2547 SessionOperation::Negotiate,
2548 SessionTransactionState::Poisoned,
2549 SessionError::MalformedPeer,
2550 );
2551 cleanup.map_or(failure, |facts| failure.with_cleanup(facts))
2552 })?;
2553 Ok(NegotiationOutcome::Rejected {
2554 by: map_mac_role(by),
2555 reason,
2556 cleanup,
2557 })
2558 }
2559 }
2560}
2561
2562#[cfg(target_os = "macos")]
2563fn map_mac_receiver_outcome(
2564 outcome: crate::backend::macos::vnext_session::MacNegotiationOutcome<
2565 crate::backend::macos::vnext_session::MacReceiverReadySession,
2566 >,
2567) -> Result<NegotiationOutcome<Session<Receiver, Ready>>, SessionFailure> {
2568 match outcome {
2569 crate::backend::macos::vnext_session::MacNegotiationOutcome::Accepted(inner) => Ok(
2570 NegotiationOutcome::Accepted(Session::from_inner(SessionInner::ReceiverReady(inner))),
2571 ),
2572 crate::backend::macos::vnext_session::MacNegotiationOutcome::Rejected {
2573 by,
2574 reason,
2575 cleanup,
2576 } => {
2577 let reason = RejectionReason::from_wire(reason).ok_or_else(|| {
2578 SessionFailure::new(
2579 SessionOperation::Negotiate,
2580 SessionTransactionState::Poisoned,
2581 SessionError::MalformedPeer,
2582 )
2583 })?;
2584 Ok(NegotiationOutcome::Rejected {
2585 by: map_mac_role(by),
2586 reason,
2587 cleanup,
2588 })
2589 }
2590 }
2591}
2592
2593#[cfg(target_os = "windows")]
2594fn map_windows_role(
2595 role: crate::backend::windows::vnext_session::WindowsNegotiationRole,
2596) -> SessionEndpoint {
2597 match role {
2598 crate::backend::windows::vnext_session::WindowsNegotiationRole::Coordinator => {
2599 SessionEndpoint::Coordinator
2600 }
2601 crate::backend::windows::vnext_session::WindowsNegotiationRole::Receiver => {
2602 SessionEndpoint::Receiver
2603 }
2604 }
2605}
2606
2607#[cfg(target_os = "windows")]
2608fn map_windows_coordinator_outcome(
2609 outcome: crate::backend::windows::vnext_session::WindowsNegotiationOutcome<
2610 crate::backend::windows::vnext_session::WindowsCoordinatorReadySession,
2611 >,
2612) -> Result<NegotiationOutcome<Session<Coordinator, Ready>>, SessionFailure> {
2613 match outcome {
2614 crate::backend::windows::vnext_session::WindowsNegotiationOutcome::Accepted(inner) => {
2615 Ok(NegotiationOutcome::Accepted(Session::from_inner(
2616 SessionInner::CoordinatorReady(Box::new(inner)),
2617 )))
2618 }
2619 crate::backend::windows::vnext_session::WindowsNegotiationOutcome::Rejected {
2620 by,
2621 reason,
2622 cleanup,
2623 } => {
2624 let reason = RejectionReason::from_wire(reason).ok_or_else(|| {
2625 let failure = SessionFailure::new(
2626 SessionOperation::Negotiate,
2627 SessionTransactionState::Poisoned,
2628 SessionError::MalformedPeer,
2629 );
2630 cleanup.map_or(failure, |facts| failure.with_cleanup(facts))
2631 })?;
2632 Ok(NegotiationOutcome::Rejected {
2633 by: map_windows_role(by),
2634 reason,
2635 cleanup,
2636 })
2637 }
2638 }
2639}
2640
2641#[cfg(target_os = "windows")]
2642fn map_windows_receiver_outcome(
2643 outcome: crate::backend::windows::vnext_session::WindowsNegotiationOutcome<
2644 crate::backend::windows::vnext_session::WindowsReceiverReadySession,
2645 >,
2646) -> Result<NegotiationOutcome<Session<Receiver, Ready>>, SessionFailure> {
2647 match outcome {
2648 crate::backend::windows::vnext_session::WindowsNegotiationOutcome::Accepted(inner) => {
2649 Ok(NegotiationOutcome::Accepted(Session::from_inner(
2650 SessionInner::ReceiverReady(Box::new(inner)),
2651 )))
2652 }
2653 crate::backend::windows::vnext_session::WindowsNegotiationOutcome::Rejected {
2654 by,
2655 reason,
2656 cleanup,
2657 } => {
2658 let reason = RejectionReason::from_wire(reason).ok_or_else(|| {
2659 SessionFailure::new(
2660 SessionOperation::Negotiate,
2661 SessionTransactionState::Poisoned,
2662 SessionError::MalformedPeer,
2663 )
2664 })?;
2665 Ok(NegotiationOutcome::Rejected {
2666 by: map_windows_role(by),
2667 reason,
2668 cleanup,
2669 })
2670 }
2671 }
2672}
2673
2674#[cfg(target_os = "linux")]
2675fn map_linux_role(
2676 role: crate::backend::linux_vnext::spawn::LinuxNegotiationRole,
2677) -> SessionEndpoint {
2678 match role {
2679 crate::backend::linux_vnext::spawn::LinuxNegotiationRole::Coordinator => {
2680 SessionEndpoint::Coordinator
2681 }
2682 crate::backend::linux_vnext::spawn::LinuxNegotiationRole::Receiver => {
2683 SessionEndpoint::Receiver
2684 }
2685 }
2686}
2687
2688#[cfg(target_os = "linux")]
2689fn map_linux_coordinator_outcome(
2690 outcome: crate::backend::linux_vnext::spawn::LinuxNegotiationOutcome<
2691 crate::backend::linux_vnext::spawn::LinuxCoordinatorReadySession,
2692 >,
2693) -> Result<NegotiationOutcome<Session<Coordinator, Ready>>, SessionFailure> {
2694 match outcome {
2695 crate::backend::linux_vnext::spawn::LinuxNegotiationOutcome::Accepted(inner) => {
2696 Ok(NegotiationOutcome::Accepted(Session::from_inner(
2697 SessionInner::CoordinatorReady(inner),
2698 )))
2699 }
2700 crate::backend::linux_vnext::spawn::LinuxNegotiationOutcome::Rejected {
2701 by,
2702 reason,
2703 cleanup,
2704 } => {
2705 let reason = RejectionReason::from_wire(reason).ok_or_else(|| {
2706 let failure = SessionFailure::new(
2707 SessionOperation::Negotiate,
2708 SessionTransactionState::Poisoned,
2709 SessionError::MalformedPeer,
2710 );
2711 cleanup.map_or(failure, |facts| failure.with_cleanup(facts))
2712 })?;
2713 Ok(NegotiationOutcome::Rejected {
2714 by: map_linux_role(by),
2715 reason,
2716 cleanup,
2717 })
2718 }
2719 }
2720}
2721
2722#[cfg(target_os = "linux")]
2723fn map_linux_receiver_outcome(
2724 outcome: crate::backend::linux_vnext::spawn::LinuxNegotiationOutcome<
2725 crate::backend::linux_vnext::spawn::LinuxReceiverReadySession,
2726 >,
2727) -> Result<NegotiationOutcome<Session<Receiver, Ready>>, SessionFailure> {
2728 match outcome {
2729 crate::backend::linux_vnext::spawn::LinuxNegotiationOutcome::Accepted(inner) => Ok(
2730 NegotiationOutcome::Accepted(Session::from_inner(SessionInner::ReceiverReady(inner))),
2731 ),
2732 crate::backend::linux_vnext::spawn::LinuxNegotiationOutcome::Rejected {
2733 by,
2734 reason,
2735 cleanup,
2736 } => {
2737 let reason = RejectionReason::from_wire(reason).ok_or_else(|| {
2738 SessionFailure::new(
2739 SessionOperation::Negotiate,
2740 SessionTransactionState::Poisoned,
2741 SessionError::MalformedPeer,
2742 )
2743 })?;
2744 Ok(NegotiationOutcome::Rejected {
2745 by: map_linux_role(by),
2746 reason,
2747 cleanup,
2748 })
2749 }
2750 }
2751}
2752
2753#[cfg(target_os = "linux")]
2754fn linux_ready_failure(
2755 operation: SessionOperation,
2756 state: SessionState,
2757 error: crate::backend::linux_vnext::spawn::LinuxPublicSessionError,
2758) -> SessionFailure {
2759 let native_code = linux_public_native_code(error);
2760 let poisoned = state == SessionState::Poisoned;
2761 SessionFailure::new(
2762 operation,
2763 if poisoned {
2764 SessionTransactionState::Poisoned
2765 } else {
2766 SessionTransactionState::Ready
2767 },
2768 error.into(),
2769 )
2770 .with_native_code(native_code)
2771 .with_poisoned(poisoned)
2772}
2773
2774#[cfg(target_os = "linux")]
2775fn linux_ready_batch_failure(
2776 operation: SessionOperation,
2777 state: SessionState,
2778 failure: crate::backend::linux_vnext::spawn::LinuxPublicReadyFailure,
2779) -> SessionFailure {
2780 let native_code = linux_public_native_code(failure.error);
2781 let poisoned = state == SessionState::Poisoned;
2782 SessionFailure::new(
2783 operation,
2784 if failure.transaction_open_on_failure {
2785 SessionTransactionState::TransactionOpen
2786 } else if poisoned {
2787 SessionTransactionState::Poisoned
2788 } else {
2789 SessionTransactionState::Ready
2790 },
2791 failure.error.into(),
2792 )
2793 .with_native_code(native_code)
2794 .with_poisoned(poisoned)
2795}
2796
2797#[cfg(target_os = "linux")]
2798const fn linux_public_native_code(
2799 error: crate::backend::linux_vnext::spawn::LinuxPublicSessionError,
2800) -> Option<i32> {
2801 match error {
2802 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::Native(code) => code,
2803 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::ActivationFailed(code) => code,
2804 _ => None,
2805 }
2806}
2807
2808#[cfg(target_os = "linux")]
2809impl From<crate::backend::linux_vnext::spawn::LinuxPublicSessionError> for SessionError {
2810 fn from(error: crate::backend::linux_vnext::spawn::LinuxPublicSessionError) -> Self {
2811 match error {
2812 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::InvalidInput => {
2813 Self::InvalidInput
2814 }
2815 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::DeadlineExpired => {
2816 Self::DeadlineExpired
2817 }
2818 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::PeerExited => {
2819 Self::PeerDisconnected
2820 }
2821 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::IdentityMismatch => {
2822 Self::IdentityMismatch
2823 }
2824 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::MalformedPeer => {
2825 Self::MalformedPeer
2826 }
2827 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::Ambiguous => {
2828 Self::Ambiguous
2829 }
2830 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::NegotiationFailed => {
2831 Self::NegotiationFailed
2832 }
2833 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::NativeNegotiation(
2834 error,
2835 ) => Self::NativeNegotiation(error),
2836 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::Control(error) => {
2837 Self::Control(error)
2838 }
2839 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::Batch(error) => {
2840 Self::Batch(error)
2841 }
2842 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::ActiveLimit => {
2843 Self::ActiveLimit
2844 }
2845 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::PeerPreparationFailed => {
2846 Self::PeerPreparationFailed
2847 }
2848 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::ActivationFailed(_) => {
2849 Self::ActivationFailed
2850 }
2851 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::Poisoned => Self::Poisoned,
2852 crate::backend::linux_vnext::spawn::LinuxPublicSessionError::Native(_) => Self::Native,
2853 }
2854 }
2855}
2856
2857#[cfg(target_os = "macos")]
2858fn mac_session_failure(
2859 operation: SessionOperation,
2860 transaction_state: SessionTransactionState,
2861 error: crate::backend::macos::vnext_session::MacPublicSessionError,
2862 poisoned: bool,
2863) -> SessionFailure {
2864 SessionFailure::new(operation, transaction_state, error.into())
2865 .with_native_code(mac_public_native_code(error))
2866 .with_poisoned(poisoned)
2867}
2868
2869#[cfg(target_os = "macos")]
2870fn mac_ready_failure(
2871 operation: SessionOperation,
2872 state: SessionState,
2873 error: crate::backend::macos::vnext_session::MacPublicSessionError,
2874) -> SessionFailure {
2875 let poisoned = state == SessionState::Poisoned;
2876 mac_session_failure(
2877 operation,
2878 if poisoned {
2879 SessionTransactionState::Poisoned
2880 } else {
2881 SessionTransactionState::Ready
2882 },
2883 error,
2884 poisoned,
2885 )
2886}
2887
2888#[cfg(target_os = "macos")]
2889const fn mac_public_native_code(
2890 error: crate::backend::macos::vnext_session::MacPublicSessionError,
2891) -> Option<i32> {
2892 match error {
2893 crate::backend::macos::vnext_session::MacPublicSessionError::Native(code) => code,
2894 _ => None,
2895 }
2896}
2897
2898#[cfg(target_os = "macos")]
2899impl From<crate::backend::macos::vnext_session::MacPublicSessionError> for SessionError {
2900 fn from(error: crate::backend::macos::vnext_session::MacPublicSessionError) -> Self {
2901 use crate::backend::macos::vnext_session::MacPublicSessionError as MacError;
2902 match error {
2903 MacError::InvalidInput => Self::InvalidInput,
2904 MacError::DeadlineExpired => Self::DeadlineExpired,
2905 MacError::PeerExited => Self::PeerDisconnected,
2906 MacError::IdentityMismatch => Self::IdentityMismatch,
2907 MacError::MalformedPeer => Self::MalformedPeer,
2908 MacError::Ambiguous => Self::Ambiguous,
2909 MacError::NegotiationFailed => Self::NegotiationFailed,
2910 MacError::NativeNegotiation(error) => Self::NativeNegotiation(error),
2911 MacError::Control(error) => Self::Control(error),
2912 MacError::Batch(error) => Self::Batch(error),
2913 MacError::ActiveLimit => Self::ActiveLimit,
2914 MacError::PeerPreparationFailed => Self::PeerPreparationFailed,
2915 MacError::ActivationFailed => Self::ActivationFailed,
2916 MacError::Poisoned => Self::Poisoned,
2917 MacError::Native(_) => Self::Native,
2918 }
2919 }
2920}
2921
2922#[cfg(target_os = "windows")]
2923fn windows_session_failure(
2924 operation: SessionOperation,
2925 transaction_state: SessionTransactionState,
2926 error: crate::backend::windows::vnext_session::WindowsPublicSessionError,
2927 poisoned: bool,
2928) -> SessionFailure {
2929 let native_code = match &error {
2930 crate::backend::windows::vnext_session::WindowsPublicSessionError::Native(code) => *code,
2931 _ => None,
2932 };
2933 SessionFailure::new(operation, transaction_state, error.into())
2934 .with_native_code(native_code)
2935 .with_poisoned(poisoned)
2936}
2937
2938#[cfg(target_os = "windows")]
2939fn windows_ready_failure(
2940 operation: SessionOperation,
2941 state: SessionState,
2942 error: crate::backend::windows::vnext_session::WindowsPublicSessionError,
2943) -> SessionFailure {
2944 let poisoned = state == SessionState::Poisoned;
2945 windows_session_failure(
2946 operation,
2947 if poisoned {
2948 SessionTransactionState::Poisoned
2949 } else {
2950 SessionTransactionState::Ready
2951 },
2952 error,
2953 poisoned,
2954 )
2955}
2956
2957#[cfg(target_os = "windows")]
2958impl From<crate::backend::windows::vnext_session::WindowsPublicSessionError> for SessionError {
2959 fn from(error: crate::backend::windows::vnext_session::WindowsPublicSessionError) -> Self {
2960 use crate::backend::windows::vnext_session::WindowsPublicSessionError as WindowsError;
2961 match error {
2962 WindowsError::InvalidInput => Self::InvalidInput,
2963 WindowsError::DeadlineExpired => Self::DeadlineExpired,
2964 WindowsError::PeerExited => Self::PeerDisconnected,
2965 WindowsError::IdentityMismatch => Self::IdentityMismatch,
2966 WindowsError::MalformedPeer => Self::MalformedPeer,
2967 WindowsError::Ambiguous => Self::Ambiguous,
2968 WindowsError::NegotiationFailed => Self::NegotiationFailed,
2969 WindowsError::NativeNegotiation(error) => Self::NativeNegotiation(error),
2970 WindowsError::Control(error) => Self::Control(error),
2971 WindowsError::Batch(error) => Self::Batch(error),
2972 WindowsError::ActiveLimit => Self::ActiveLimit,
2973 WindowsError::PeerPreparationFailed => Self::PeerPreparationFailed,
2974 WindowsError::ActivationFailed => Self::ActivationFailed,
2975 WindowsError::Poisoned => Self::Poisoned,
2976 WindowsError::Native(_) => Self::Native,
2977 }
2978 }
2979}
2980
2981const _: () = assert!(cfg!(target_has_atomic = "32"));
2982const _: () = assert!(cfg!(target_has_atomic = "64"));
2983
2984#[cfg(test)]
2985#[path = "session_test.rs"]
2986mod tests;