1use std::any::Any;
104use std::collections::BTreeMap;
105use std::collections::BTreeSet;
106use std::fmt;
107use std::fmt::Debug;
108use std::future;
109use std::future::Future;
110use std::ops::Bound::Excluded;
111use std::pin::Pin;
112use std::sync::Arc;
113use std::sync::Condvar;
114use std::sync::Mutex;
115use std::sync::OnceLock;
116use std::sync::RwLock;
117use std::sync::Weak;
118use std::sync::atomic::AtomicU64;
119use std::sync::atomic::AtomicUsize;
120use std::sync::atomic::Ordering;
121use std::task::Context;
122use std::task::Poll;
123
124use async_trait::async_trait;
125use dashmap::DashMap;
126use dashmap::mapref::entry::Entry;
127use enum_as_inner::EnumAsInner;
128use futures::Sink;
129use futures::Stream;
130use hyperactor_config::Flattrs;
131use hyperactor_telemetry::hash_to_u64;
132use serde::Deserialize;
133use serde::Serialize;
134use serde::de::DeserializeOwned;
135use tokio::sync::mpsc;
136use tokio::sync::oneshot;
137use tokio::sync::watch;
138use tokio::task::JoinHandle;
139use tokio_util::sync::CancellationToken;
140use tracing::Instrument;
141use typeuri::Named;
142
143use crate::ActorAddr;
144use crate::Addr;
145use crate::Endpoint;
146use crate::EndpointLocation;
147use crate::OncePortRef;
149use crate::PortAddr;
150use crate::PortRef;
151use crate::ProcAddr;
152use crate::accum::Accumulator;
153use crate::accum::ReducerSpec;
154use crate::accum::StreamingReducerOpts;
155use crate::actor::ActorStatus;
156use crate::channel;
157use crate::channel::ChannelAddr;
158use crate::channel::ChannelError;
159use crate::channel::ChannelTransport;
160use crate::channel::CloseReason;
161use crate::channel::CompletionSink;
162use crate::channel::SendError;
163use crate::channel::SendErrorReason;
164use crate::channel::TxStatus;
165use crate::context;
166use crate::id::ActorId;
167use crate::metrics;
168use crate::ordering::SEQ_INFO;
169use crate::ordering::SeqInfo;
170use crate::port::ControlPort;
171use crate::port::Port;
172use crate::sequenced::SequencedEnvelope;
173use crate::sequenced::SequencedReceiver;
174use crate::sequenced::sequenced_unbounded;
175
176mod undeliverable;
177pub use undeliverable::DeliveryFailureReport;
179pub use undeliverable::Undeliverable;
180pub use undeliverable::UndeliverableMessageError;
181pub use undeliverable::custom_monitored_return_handle;
182pub use undeliverable::monitored_return_handle; pub mod mailbox_admin_message;
185pub use mailbox_admin_message::MailboxAdminMessage;
186pub use mailbox_admin_message::MailboxAdminMessageHandler;
187pub mod headers;
189
190pub trait Message: Send + Sync + 'static {}
193impl<M: Send + Sync + 'static> Message for M {}
194
195pub trait RemoteMessage: Message + Named + Serialize + DeserializeOwned {}
199
200impl<M: Message + Named + Serialize + DeserializeOwned> RemoteMessage for M {}
201
202pub type Data = Vec<u8>;
204
205const MAX_RENDERED_DELIVERY_FAILURE_ATTRS_LEN: usize = 1024;
206
207fn truncate_for_delivery_failure_rendering(value: String) -> String {
208 if value.len() <= MAX_RENDERED_DELIVERY_FAILURE_ATTRS_LEN {
209 return value;
210 }
211
212 let mut truncated = value;
213 let truncate_at = truncated
214 .char_indices()
215 .map(|(index, _)| index)
216 .take_while(|index| *index <= MAX_RENDERED_DELIVERY_FAILURE_ATTRS_LEN)
217 .last()
218 .unwrap_or(0);
219 truncated.truncate(truncate_at);
220 truncated.push_str("...");
221 truncated
222}
223
224#[derive(thiserror::Error, Debug, Serialize, Deserialize, typeuri::Named, Clone)]
226#[error("{kind}")]
227pub struct DeliveryFailure {
228 pub kind: DeliveryFailureKind,
230
231 pub attrs: Flattrs,
233}
234
235impl DeliveryFailure {
236 pub fn new(kind: impl Into<DeliveryFailureKind>) -> Self {
238 Self {
239 kind: kind.into(),
240 attrs: Flattrs::new(),
241 }
242 }
243
244 pub fn with_attrs(kind: impl Into<DeliveryFailureKind>, attrs: Flattrs) -> Self {
246 Self {
247 kind: kind.into(),
248 attrs,
249 }
250 }
251
252 pub fn render_bounded(&self) -> String {
254 let mut rendered = format!("delivery failure: {}", self.kind);
255 if !self.attrs.is_empty() {
256 rendered.push_str("; attrs: ");
257 rendered.push_str(&truncate_for_delivery_failure_rendering(
258 self.attrs.to_string(),
259 ));
260 }
261 rendered
262 }
263}
264
265#[derive(
267 thiserror::Error,
268 Debug,
269 Serialize,
270 Deserialize,
271 EnumAsInner,
272 typeuri::Named,
273 Clone,
274 PartialEq,
275 Eq
276)]
277pub enum DeliveryFailureKind {
278 #[error("{0}")]
280 InvalidReference(#[from] InvalidReference),
281
282 #[error("{0}")]
285 Undeliverable(#[from] UndeliverableReason),
286
287 #[error("{0}")]
289 Expired(#[from] ExpiredDelivery),
290}
291
292#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
294#[error("invalid reference {target}: {reason}")]
295pub struct InvalidReference {
296 pub target: Addr,
298
299 pub reason: InvalidReferenceReason,
301}
302
303impl InvalidReference {
304 pub fn new(target: impl Into<Addr>, reason: InvalidReferenceReason) -> Self {
306 Self {
307 target: target.into(),
308 reason,
309 }
310 }
311}
312
313#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
315pub enum InvalidReferenceReason {
316 #[error("actor does not exist")]
318 ActorNotExist,
319
320 #[error("handler not bound")]
322 HandlerNotBound,
323
324 #[error("actor stopped")]
326 ActorStopped,
327
328 #[error("actor failed")]
330 ActorFailed,
331
332 #[error("port never allocated")]
334 PortNeverAllocated,
335
336 #[error("protocol mismatch")]
338 ProtocolMismatch,
339
340 #[error("wrong mailbox owner")]
342 WrongMailboxOwner,
343}
344
345#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
347#[error("ttl expired for {target}")]
348pub struct ExpiredDelivery {
349 pub target: PortAddr,
351}
352
353impl ExpiredDelivery {
354 pub fn new(target: impl Into<PortAddr>) -> Self {
356 Self {
357 target: target.into(),
358 }
359 }
360}
361
362#[derive(
364 thiserror::Error,
365 Debug,
366 Serialize,
367 Deserialize,
368 EnumAsInner,
369 Clone,
370 PartialEq,
371 Eq
372)]
373pub enum UndeliverableReason {
374 #[error("{0}")]
376 Transport(#[from] TransportFailure),
377
378 #[error("{0}")]
380 PortGone(#[from] PortGone),
381}
382
383#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
385#[error("transport failure to {target}: {reason}")]
386pub struct TransportFailure {
387 pub target: Addr,
389
390 pub reason: TransportFailureReason,
392}
393
394impl TransportFailure {
395 pub fn new(target: impl Into<Addr>, reason: TransportFailureReason) -> Self {
397 Self {
398 target: target.into(),
399 reason,
400 }
401 }
402}
403
404#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
406pub enum TransportFailureReason {
407 #[error("channel closed: {addr}")]
409 ChannelClosed {
410 addr: ChannelAddr,
412 },
413
414 #[error("ack timed out: {addr}")]
416 AckTimedOut {
417 addr: ChannelAddr,
419 },
420
421 #[error("dial failed: {addr}: {error}")]
423 DialFailed {
424 addr: ChannelAddr,
426
427 error: String,
429 },
430
431 #[error("no route")]
434 NoRoute,
435
436 #[error(
438 "rejecting oversize frame: len={len} > max={max}. \
439 ack will not arrive before timeout; increase CODEC_MAX_FRAME_LENGTH to allow."
440 )]
441 OversizedFrame {
442 len: usize,
444
445 max: usize,
447 },
448
449 #[error("link unavailable: {0}")]
451 LinkUnavailable(String),
452
453 #[error("forwarder unavailable")]
455 ForwarderUnavailable,
456}
457
458#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
460#[error("port gone: {port}")]
461pub struct PortGone {
462 pub port: PortAddr,
464
465 pub message_type: Option<String>,
467}
468
469impl PortGone {
470 pub fn new(port: impl Into<PortAddr>, message_type: Option<String>) -> Self {
472 Self {
473 port: port.into(),
474 message_type,
475 }
476 }
477}
478
479#[derive(Debug, Serialize, Deserialize, Clone, typeuri::Named)]
483pub struct MessageEnvelope {
484 sender: ActorAddr,
486
487 dest: PortAddr,
489
490 #[serde(default)]
492 next_hop: Option<PortAddr>,
493
494 data: wirevalue::Any,
496
497 delivery_failures: Vec<DeliveryFailure>,
501
502 headers: Flattrs,
504
505 ttl: u8,
507
508 return_undeliverable: bool,
511 }
513wirevalue::register_type!(MessageEnvelope);
514
515impl MessageEnvelope {
516 pub fn new(
518 sender: impl Into<ActorAddr>,
519 dest: impl Into<PortAddr>,
520 data: wirevalue::Any,
521 headers: Flattrs,
522 ) -> Self {
523 let sender = sender.into();
524 let dest = dest.into();
525 Self {
526 sender,
527 dest,
528 next_hop: None,
529 data,
530 delivery_failures: Vec::new(),
531 headers,
532 ttl: hyperactor_config::global::get(crate::config::MESSAGE_TTL_DEFAULT),
533 return_undeliverable: true,
535 }
536 }
537
538 pub(crate) fn new_unknown(dest: impl Into<PortAddr>, data: wirevalue::Any) -> Self {
540 let unknown_addr = ChannelAddr::any(ChannelTransport::Local);
542 let unknown_proc_ref = ProcAddr::instance(unknown_addr, "unknown");
543 let unknown_actor_ref =
544 ActorAddr::root(unknown_proc_ref, crate::id::Label::strip("unknown"));
545 Self::new(unknown_actor_ref, dest, data, Flattrs::new())
546 }
547
548 pub fn serialize<T: Serialize + Named>(
550 source: impl Into<ActorAddr>,
551 dest: impl Into<PortAddr>,
552 value: &T,
553 headers: Flattrs,
554 ) -> Result<Self, wirevalue::Error> {
555 Ok(Self::new(
556 source,
557 dest,
558 wirevalue::Any::serialize(value)?,
559 headers,
560 ))
561 }
562
563 pub fn ttl(&self) -> u8 {
569 self.ttl
570 }
571
572 pub fn set_ttl(mut self, ttl: u8) -> Self {
582 self.ttl = ttl;
583 self
584 }
585
586 fn decrement_ttl(&mut self) -> bool {
590 if self.ttl == 0 {
591 false
592 } else {
593 self.ttl -= 1;
594 true
595 }
596 }
597
598 pub fn deserialized<T: DeserializeOwned + Named>(&self) -> Result<T, anyhow::Error> {
600 Ok(self.data.deserialized()?)
601 }
602
603 pub fn data(&self) -> &wirevalue::Any {
605 &self.data
606 }
607
608 pub fn sender(&self) -> &ActorAddr {
610 &self.sender
611 }
612
613 pub fn dest(&self) -> &PortAddr {
615 &self.dest
616 }
617
618 pub(crate) fn next_hop(&self) -> &PortAddr {
620 self.next_hop.as_ref().unwrap_or(&self.dest)
621 }
622
623 pub(crate) fn has_next_hop(&self) -> bool {
626 self.next_hop.is_some()
627 }
628
629 pub fn with_dest(mut self, dest: PortAddr) -> Self {
631 self.dest = dest;
632 self.next_hop = None;
633 self
634 }
635
636 pub(crate) fn with_next_hop(mut self, dest: PortAddr) -> Self {
638 self.next_hop = if dest == self.dest { None } else { Some(dest) };
639 self
640 }
641
642 pub fn headers(&self) -> &Flattrs {
644 &self.headers
645 }
646
647 pub fn is_signal(&self) -> bool {
649 self.dest
650 .is_control_port_kind(crate::port::ControlPort::Signal)
651 }
652
653 pub fn push_delivery_failure(&mut self, failure: DeliveryFailure) {
655 self.delivery_failures.push(failure)
656 }
657
658 pub fn ensure_root_delivery_failure(&mut self, failure: impl FnOnce() -> DeliveryFailure) {
661 if self.root_delivery_failure().is_none() {
662 self.push_delivery_failure(failure());
663 }
664 }
665
666 pub fn update_sender(&mut self, sender: impl Into<ActorAddr>) {
670 self.sender = sender.into();
671 }
672
673 pub fn set_return_undeliverable(&mut self, return_undeliverable: bool) {
677 self.return_undeliverable = return_undeliverable;
678 }
679
680 pub fn undeliverable(
683 mut self,
684 failure: DeliveryFailure,
685 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
686 ) {
687 let error = failure.render_bounded();
688 tracing::debug!(
689 name = "undelivered_message_attempt",
690 sender = self.sender.to_string(),
691 dest = self.dest.to_string(),
692 error = %error,
693 return_handle = %return_handle,
694 );
695 metrics::MAILBOX_UNDELIVERABLE_MESSAGES.add(
696 1,
697 hyperactor_telemetry::kv_pairs!(
698 "sender_actor_id" => self.sender.to_string(),
699 "dest_actor_id" => self.dest.to_string(),
700 "message_type" => self.data.typename().unwrap_or("unknown"),
701 "error_type" => error,
702 ),
703 );
704
705 self.push_delivery_failure(failure);
706 undeliverable::return_undeliverable(return_handle, self);
707 }
708
709 pub fn delivery_failures(&self) -> &[DeliveryFailure] {
712 &self.delivery_failures
713 }
714
715 pub fn root_delivery_failure(&self) -> Option<&DeliveryFailure> {
717 self.delivery_failures.first()
718 }
719
720 pub fn root_delivery_failure_mut(&mut self) -> Option<&mut DeliveryFailure> {
722 self.delivery_failures.first_mut()
723 }
724
725 pub fn error_msg(&self) -> Option<String> {
729 if !self.delivery_failures.is_empty() {
730 return Some(
731 self.delivery_failures
732 .iter()
733 .map(DeliveryFailure::render_bounded)
734 .collect::<Vec<_>>()
735 .join("; "),
736 );
737 }
738
739 None
740 }
741
742 fn open(self) -> (MessageMetadata, wirevalue::Any) {
743 let Self {
744 sender,
745 dest,
746 next_hop,
747 data,
748 delivery_failures,
749 headers,
750 ttl,
751 return_undeliverable,
752 } = self;
753
754 (
755 MessageMetadata {
756 sender,
757 dest,
758 next_hop,
759 delivery_failures,
760 headers,
761 ttl,
762 return_undeliverable,
763 },
764 data,
765 )
766 }
767
768 fn seal(metadata: MessageMetadata, data: wirevalue::Any) -> Self {
769 let MessageMetadata {
770 sender,
771 dest,
772 next_hop,
773 delivery_failures,
774 headers,
775 ttl,
776 return_undeliverable,
777 } = metadata;
778
779 Self {
780 sender,
781 dest,
782 next_hop,
783 data,
784 delivery_failures,
785 headers,
786 ttl,
787 return_undeliverable,
788 }
789 }
790
791 fn return_undeliverable(&self) -> bool {
792 self.return_undeliverable
793 }
794
795 pub fn set_header<T: Serialize>(&mut self, key: hyperactor_config::attrs::Key<T>, value: T) {
797 self.headers.set(key, value);
798 }
799}
800
801impl fmt::Display for MessageEnvelope {
802 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
803 match &self.error_msg() {
804 None => write!(
805 f,
806 "{} > {}: {} {{{}}}",
807 self.sender, self.dest, self.data, self.headers
808 ),
809 Some(err) => write!(
810 f,
811 "{} > {}: {} {{{}}}: delivery error: {}",
812 self.sender, self.dest, self.data, self.headers, err
813 ),
814 }
815 }
816}
817
818#[derive(Clone)]
820pub struct MessageMetadata {
821 sender: ActorAddr,
822 dest: PortAddr,
823 next_hop: Option<PortAddr>,
824 delivery_failures: Vec<DeliveryFailure>,
828 headers: Flattrs,
829 ttl: u8,
830 return_undeliverable: bool,
831}
832
833#[derive(Debug)]
836pub struct MailboxError {
837 actor_id: ActorAddr,
838 kind: MailboxErrorKind,
839}
840
841#[derive(thiserror::Error, Debug)]
844#[non_exhaustive]
845pub enum MailboxErrorKind {
846 #[error("mailbox closed")]
848 Closed,
849
850 #[error("invalid port: {0}")]
852 InvalidPort(PortAddr),
853
854 #[error("no sender for port: {0}")]
856 NoSenderForPort(PortAddr),
857
858 #[error("no local sender for port: {0}")]
861 NoLocalSenderForPort(PortAddr),
862
863 #[error("{0}: port closed")]
865 PortClosed(PortAddr),
866
867 #[error("send {0}: {1}")]
869 Send(PortAddr, #[source] anyhow::Error),
870
871 #[error("recv {0}: {1}")]
873 Recv(PortAddr, #[source] anyhow::Error),
874
875 #[error("serialize: {0}")]
877 Serialize(#[source] anyhow::Error),
878
879 #[error("deserialize {0}: {1}")]
881 Deserialize(&'static str, anyhow::Error),
882
883 #[error(transparent)]
885 Channel(#[from] ChannelError),
886
887 #[error("owner terminated: {0}")]
889 OwnerTerminated(ActorStatus),
890}
891
892impl MailboxError {
893 pub fn new(actor_id: impl Into<ActorAddr>, kind: MailboxErrorKind) -> Self {
896 Self {
897 actor_id: actor_id.into(),
898 kind,
899 }
900 }
901
902 pub fn actor_addr(&self) -> &ActorAddr {
904 &self.actor_id
905 }
906
907 pub fn kind(&self) -> &MailboxErrorKind {
909 &self.kind
910 }
911}
912
913impl fmt::Display for MailboxError {
914 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
915 write!(f, "{}: ", self.actor_id)?;
916 fmt::Display::fmt(&self.kind, f)
917 }
918}
919
920impl std::error::Error for MailboxError {
921 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
922 self.kind.source()
923 }
924}
925
926#[derive(Debug, Clone)]
930pub enum PortLocation {
931 Bound(PortAddr),
933 Unbound(ActorAddr, &'static str),
935}
936
937impl PortLocation {
938 fn new_unbound<M: Message>(actor_id: ActorAddr) -> Self {
939 PortLocation::Unbound(actor_id, std::any::type_name::<M>())
940 }
941
942 #[allow(dead_code)]
943 fn new_unbound_type(actor_id: ActorAddr, ty: &'static str) -> Self {
944 PortLocation::Unbound(actor_id, ty)
945 }
946
947 pub fn actor_addr(&self) -> ActorAddr {
949 match self {
950 PortLocation::Bound(port_addr) => port_addr.actor_addr(),
951 PortLocation::Unbound(actor_addr, _) => actor_addr.clone(),
952 }
953 }
954}
955
956impl fmt::Display for PortLocation {
957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
958 match self {
959 PortLocation::Bound(port_ref) => write!(f, "{}", port_ref),
960 PortLocation::Unbound(actor_ref, name) => write!(f, "{}<{}>", actor_ref, name),
961 }
962 }
963}
964
965#[derive(Debug)]
968pub struct MailboxSenderError {
969 location: Box<PortLocation>,
970 kind: Box<MailboxSenderErrorKind>,
971}
972
973#[derive(thiserror::Error, Debug)]
975pub enum MailboxSenderErrorKind {
976 #[error("serialization error: {0}")]
978 Serialize(anyhow::Error),
979
980 #[error("deserialization error for type {0}: {1}")]
982 Deserialize(&'static str, anyhow::Error),
983
984 #[error("invalid port")]
986 Invalid,
987
988 #[error("port closed")]
990 Closed,
991
992 #[error(transparent)]
995 Mailbox(#[from] MailboxError),
996
997 #[error(transparent)]
999 Channel(#[from] ChannelError),
1000
1001 #[error("send error: {0}")]
1003 Other(#[from] anyhow::Error),
1004
1005 #[error("unreachable: {0}")]
1007 Unreachable(anyhow::Error),
1008}
1009
1010impl MailboxSenderError {
1011 pub fn new_unbound<M>(actor_id: impl Into<ActorAddr>, kind: MailboxSenderErrorKind) -> Self {
1013 Self {
1014 location: Box::new(PortLocation::Unbound(
1015 actor_id.into(),
1016 std::any::type_name::<M>(),
1017 )),
1018 kind: Box::new(kind),
1019 }
1020 }
1021
1022 pub fn new_unbound_type(
1024 actor_id: impl Into<ActorAddr>,
1025 kind: MailboxSenderErrorKind,
1026 ty: &'static str,
1027 ) -> Self {
1028 Self {
1029 location: Box::new(PortLocation::Unbound(actor_id.into(), ty)),
1030 kind: Box::new(kind),
1031 }
1032 }
1033
1034 pub fn new_bound(port_id: impl Into<PortAddr>, kind: MailboxSenderErrorKind) -> Self {
1036 Self {
1037 location: Box::new(PortLocation::Bound(port_id.into())),
1038 kind: Box::new(kind),
1039 }
1040 }
1041
1042 pub fn location(&self) -> &PortLocation {
1044 &self.location
1045 }
1046
1047 pub fn kind(&self) -> &MailboxSenderErrorKind {
1049 &self.kind
1050 }
1051}
1052
1053impl fmt::Display for MailboxSenderError {
1054 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1055 write!(f, "{}: ", self.location)?;
1056 fmt::Display::fmt(&self.kind, f)
1057 }
1058}
1059
1060impl std::error::Error for MailboxSenderError {
1061 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1062 self.kind.source()
1063 }
1064}
1065
1066#[async_trait]
1069pub trait MailboxSender: Send + Sync + Any {
1070 fn post(
1073 &self,
1074 mut envelope: MessageEnvelope,
1075 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1076 ) {
1077 if !envelope.decrement_ttl() {
1078 let failure = DeliveryFailure::new(ExpiredDelivery::new(envelope.dest().clone()));
1079 envelope.undeliverable(failure, return_handle);
1080 return;
1081 }
1082 self.post_unchecked(envelope, return_handle);
1083 }
1084
1085 fn post_unchecked(
1087 &self,
1088 envelope: MessageEnvelope,
1089 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1090 );
1091
1092 async fn flush(&self) -> Result<(), anyhow::Error> {
1097 Ok(())
1098 }
1099}
1100
1101pub trait PortSender: MailboxSender {
1104 fn serialize_and_send<M: RemoteMessage>(
1106 &self,
1107 port: &PortRef<M>,
1108 message: M,
1109 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1110 ) -> Result<(), MailboxSenderError> {
1111 let serialized = wirevalue::Any::serialize(&message).map_err(|err| {
1113 MailboxSenderError::new_bound(
1114 port.port_addr().clone(),
1115 MailboxSenderErrorKind::Serialize(err.into()),
1116 )
1117 })?;
1118 self.post(
1119 MessageEnvelope::new_unknown(port.port_addr().clone(), serialized),
1120 return_handle,
1121 );
1122 Ok(())
1123 }
1124
1125 fn serialize_and_send_once<M: RemoteMessage>(
1128 &self,
1129 once_port: OncePortRef<M>,
1130 message: M,
1131 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1132 ) -> Result<(), MailboxSenderError> {
1133 let serialized = wirevalue::Any::serialize(&message).map_err(|err| {
1134 MailboxSenderError::new_bound(
1135 once_port.port_addr().clone(),
1136 MailboxSenderErrorKind::Serialize(err.into()),
1137 )
1138 })?;
1139 self.post(
1140 MessageEnvelope::new_unknown(once_port.port_addr().clone(), serialized),
1141 return_handle,
1142 );
1143 Ok(())
1144 }
1145}
1146
1147impl<T: ?Sized + MailboxSender> PortSender for T {}
1148
1149#[derive(Debug, Clone)]
1153pub struct PanickingMailboxSender;
1154
1155#[async_trait]
1156impl MailboxSender for PanickingMailboxSender {
1157 fn post_unchecked(
1158 &self,
1159 envelope: MessageEnvelope,
1160 _return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1161 ) {
1162 panic!("panic! in the mailbox! attempted post: {}", envelope)
1163 }
1164}
1165
1166#[derive(Debug)]
1169pub struct UndeliverableMailboxSender;
1170
1171#[async_trait]
1172impl MailboxSender for UndeliverableMailboxSender {
1173 fn post_unchecked(
1174 &self,
1175 envelope: MessageEnvelope,
1176 _return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1177 ) {
1178 let sender_name = envelope
1179 .sender
1180 .label()
1181 .map_or("?".to_string(), |l| l.to_string());
1182 let error_str = envelope.error_msg().unwrap_or("".to_string());
1183 let operation_endpoint = envelope.headers().get(headers::OPERATION_ENDPOINT);
1184 let operation_adverb = envelope.headers().get(headers::OPERATION_ADVERB);
1185 match &operation_endpoint {
1187 Some(endpoint) => tracing::error!(
1188 name = "undelivered_message_abandoned",
1189 actor_name = sender_name,
1190 actor_id = envelope.sender.to_string(),
1191 dest = envelope.dest.to_string(),
1192 message_type = envelope.data().typename().unwrap_or("unknown"),
1193 data_len = envelope.data().len(),
1194 endpoint = %endpoint,
1195 adverb = operation_adverb.as_deref().unwrap_or(""),
1196 error = %error_str,
1197 "abandoned message for {}",
1198 endpoint,
1199 ),
1200 None => tracing::error!(
1201 name = "undelivered_message_abandoned",
1202 actor_name = sender_name,
1203 actor_id = envelope.sender.to_string(),
1204 dest = envelope.dest.to_string(),
1205 message_type = envelope.data().typename().unwrap_or("unknown"),
1206 data_len = envelope.data().len(),
1207 error = %error_str,
1208 "message not delivered to {}",
1209 envelope.dest,
1210 ),
1211 }
1212 }
1213}
1214
1215#[derive(Clone)]
1221pub struct BoxedMailboxSender(Arc<dyn MailboxSender + Send + Sync + 'static>);
1222
1223impl fmt::Debug for BoxedMailboxSender {
1224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1225 f.debug_struct("BoxedMailboxSender")
1226 .field("sender", &"<dyn MailboxSender>")
1227 .finish()
1228 }
1229}
1230
1231impl BoxedMailboxSender {
1232 pub fn new(sender: impl MailboxSender + 'static) -> Self {
1234 Self(Arc::new(sender))
1235 }
1236
1237 pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
1240 (&*self.0 as &dyn Any).downcast_ref::<T>()
1241 }
1242}
1243
1244pub trait BoxableMailboxSender: MailboxSender + Clone + 'static {
1246 fn boxed(&self) -> BoxedMailboxSender;
1248}
1249impl<T: MailboxSender + Clone + 'static> BoxableMailboxSender for T {
1250 fn boxed(&self) -> BoxedMailboxSender {
1251 BoxedMailboxSender::new(self.clone())
1252 }
1253}
1254
1255pub trait IntoBoxedMailboxSender: MailboxSender {
1257 fn into_boxed(self) -> BoxedMailboxSender;
1259}
1260impl<T: MailboxSender + 'static> IntoBoxedMailboxSender for T {
1261 fn into_boxed(self) -> BoxedMailboxSender {
1262 BoxedMailboxSender::new(self)
1263 }
1264}
1265
1266#[async_trait]
1267impl MailboxSender for BoxedMailboxSender {
1268 fn post_unchecked(
1269 &self,
1270 envelope: MessageEnvelope,
1271 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1272 ) {
1273 self.0.post_unchecked(envelope, return_handle);
1274 }
1275
1276 async fn flush(&self) -> Result<(), anyhow::Error> {
1277 self.0.flush().await
1278 }
1279}
1280
1281#[derive(thiserror::Error, Debug)]
1283pub enum MailboxServerError {
1284 #[error(transparent)]
1286 Channel(#[from] ChannelError),
1287
1288 #[error(transparent)]
1290 MailboxSender(#[from] MailboxSenderError),
1291}
1292
1293#[derive(Debug)]
1296pub struct MailboxServerHandle {
1297 join_handle: JoinHandle<Result<(), MailboxServerError>>,
1298 stopped_tx: watch::Sender<bool>,
1299}
1300
1301impl MailboxServerHandle {
1302 pub fn stop(&self, reason: &str) {
1305 tracing::info!("stopping mailbox server; reason: {}", reason);
1306 let _ = self.stopped_tx.send(true);
1311 }
1312
1313 pub fn from_parts(
1318 join_handle: JoinHandle<Result<(), MailboxServerError>>,
1319 stopped_tx: watch::Sender<bool>,
1320 ) -> Self {
1321 Self {
1322 join_handle,
1323 stopped_tx,
1324 }
1325 }
1326}
1327
1328impl Future for MailboxServerHandle {
1330 type Output = <JoinHandle<Result<(), MailboxServerError>> as Future>::Output;
1331
1332 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1333 let join_handle_pinned =
1335 unsafe { self.map_unchecked_mut(|container| &mut container.join_handle) };
1336 join_handle_pinned.poll(cx)
1337 }
1338}
1339
1340pub trait MailboxServer: MailboxSender + Clone + Sized + 'static {
1343 fn serve(
1347 self,
1348 mut rx: impl channel::Rx<MessageEnvelope> + Send + 'static,
1349 ) -> MailboxServerHandle {
1350 let (return_handle, mut undeliverable_rx) = undeliverable::new_undeliverable_port();
1355 tokio::task::spawn(async move {
1356 let client = crate::client("undeliverable_supervisor");
1357 while let Ok(undeliverable) = undeliverable_rx.recv().await {
1358 match undeliverable {
1359 Undeliverable::Returned(mut envelope) => {
1360 match envelope.deserialized::<Undeliverable<MessageEnvelope>>() {
1361 Ok(Undeliverable::Returned(e)) => {
1362 UndeliverableMailboxSender.post(e, monitored_return_handle());
1364 continue;
1365 }
1366 Ok(Undeliverable::Report(report)) => {
1367 tracing::error!(
1368 sender = %report.sender,
1369 dest = %report.dest,
1370 message_type = report.message_type.as_deref().unwrap_or("unknown"),
1371 error = %report.error_msg().unwrap_or_default(),
1372 "undeliverable message report was undeliverable"
1373 );
1374 continue;
1375 }
1376 Err(_) => {}
1377 }
1378 let target = envelope.dest().clone();
1379 envelope.ensure_root_delivery_failure(|| {
1380 DeliveryFailure::new(UndeliverableReason::Transport(
1381 TransportFailure::new(
1382 target,
1383 TransportFailureReason::LinkUnavailable(
1384 "message was undeliverable".to_owned(),
1385 ),
1386 ),
1387 ))
1388 });
1389 let sender_id: ActorAddr = envelope.sender().clone();
1390 let return_port =
1391 PortRef::<Undeliverable<MessageEnvelope>>::attest_handler_port(
1392 &sender_id,
1393 );
1394 return_port.post_serialized(
1395 &client,
1396 Flattrs::new(),
1397 wirevalue::Any::serialize(&Undeliverable::Returned(envelope)).unwrap(),
1398 );
1399 }
1400 Undeliverable::Report(report) => {
1401 tracing::error!(
1402 sender = %report.sender,
1403 dest = %report.dest,
1404 message_type = report.message_type.as_deref().unwrap_or("unknown"),
1405 error = %report.error_msg().unwrap_or_default(),
1406 "undeliverable message report was undeliverable"
1407 );
1408 }
1409 }
1410 }
1411 });
1412
1413 let (stopped_tx, mut stopped_rx) = watch::channel(false);
1414 let join_handle = tokio::spawn(async move {
1415 let mut detached = false;
1416
1417 let result = loop {
1418 if *stopped_rx.borrow_and_update() {
1419 break Ok(());
1420 }
1421
1422 tokio::select! {
1423 message = rx.recv() => {
1424 match message {
1425 Ok(envelope) => self.post(envelope, return_handle.clone()),
1427
1428 Err(ChannelError::Closed) => break Ok(()),
1431 Err(channel_err) => break Err(MailboxServerError::from(channel_err)),
1432 }
1433 }
1434 result = stopped_rx.changed(), if !detached => {
1435 detached = result.is_err();
1436 if detached {
1437 tracing::debug!(
1438 "the mailbox server is detached for Rx {}", rx.addr()
1439 );
1440 } else {
1441 tracing::debug!(
1442 "the mailbox server is stopped for Rx {}", rx.addr()
1443 );
1444 }
1445 }
1446 }
1447 };
1448
1449 rx.join().await;
1452
1453 result
1454 }.instrument(tracing::debug_span!("MailboxServer")));
1455
1456 MailboxServerHandle {
1457 join_handle,
1458 stopped_tx,
1459 }
1460 }
1461}
1462
1463impl<T: MailboxSender + Clone + Sized + Sync + Send + 'static> MailboxServer for T {}
1464
1465struct Buffer<T: Message> {
1466 queue: mpsc::UnboundedSender<(T, PortHandle<Undeliverable<T>>)>,
1467 #[allow(dead_code)]
1468 processed: watch::Receiver<usize>,
1469 seq: AtomicUsize,
1470}
1471
1472impl<T: Message> Buffer<T> {
1473 fn new<Fut>(
1474 process: impl Fn(T, PortHandle<Undeliverable<T>>) -> Fut + Send + Sync + 'static,
1475 ) -> Self
1476 where
1477 Fut: Future<Output = ()> + Send + 'static,
1478 {
1479 let (queue, mut next) = mpsc::unbounded_channel();
1480 let (last_processed, processed) = watch::channel(0);
1481 crate::init::get_runtime().spawn(
1482 async move {
1483 let mut seq = 0;
1484 while let Some((msg, return_handle)) = next.recv().await {
1485 process(msg, return_handle).await;
1486 seq += 1;
1487 let _ = last_processed.send(seq);
1488 }
1489 }
1490 .instrument(tracing::debug_span!("mailbox::Buffer")),
1491 );
1492 Self {
1493 queue,
1494 processed,
1495 seq: AtomicUsize::new(0),
1496 }
1497 }
1498
1499 fn send(
1500 &self,
1501 item: (T, PortHandle<Undeliverable<T>>),
1502 ) -> Result<(), Box<mpsc::error::SendError<(T, PortHandle<Undeliverable<T>>)>>> {
1503 self.seq.fetch_add(1, Ordering::SeqCst);
1504 self.queue.send(item).map_err(Box::new)?;
1505 Ok(())
1506 }
1507}
1508
1509pub struct MailboxClient {
1511 addr: ChannelAddr,
1513
1514 buffer: Buffer<MessageEnvelope>,
1516
1517 _tx_monitoring: CancellationToken,
1519
1520 submitted: Arc<AtomicUsize>,
1522 completed: Arc<AtomicUsize>,
1525 completed_notify: Arc<tokio::sync::Notify>,
1527
1528 tx_status: watch::Receiver<TxStatus>,
1531}
1532
1533impl fmt::Debug for MailboxClient {
1534 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1535 f.debug_struct("MailboxClient")
1536 .field("buffer", &"<Buffer>")
1537 .finish()
1538 }
1539}
1540
1541impl MailboxClient {
1542 pub fn new(tx: impl channel::Tx<MessageEnvelope> + Send + Sync + 'static) -> Self {
1545 let addr = tx.addr();
1546 let tx = Arc::new(tx);
1547 let tx_status = tx.status().clone();
1548 let tx_monitoring = CancellationToken::new();
1549 let completed = Arc::new(AtomicUsize::new(0));
1550 let completed_notify = Arc::new(tokio::sync::Notify::new());
1551 let buffer = {
1552 let completed = completed.clone();
1553 let completed_notify = completed_notify.clone();
1554 let addr = addr.clone();
1555 Buffer::new(move |envelope, return_handle| {
1556 let tx = Arc::clone(&tx);
1557 let addr = addr.clone();
1558 let return_handle_0 = return_handle.clone();
1560 let tracker =
1561 channel::CompletionTracker::new(completed.clone(), completed_notify.clone());
1562 let completion = CompletionSink::tracked(
1563 tracker,
1564 move |send_error: SendError<MessageEnvelope>| {
1565 let SendError {
1566 error,
1567 message,
1568 reason,
1569 } = send_error;
1570 let target = message.dest().clone();
1571 let reason_text = reason
1572 .as_ref()
1573 .map(ToString::to_string)
1574 .unwrap_or_else(|| "channel closed".to_owned());
1575 let reason = match reason {
1576 Some(SendErrorReason::OversizedFrame { len, max }) => {
1577 TransportFailureReason::OversizedFrame { len, max }
1578 }
1579 Some(SendErrorReason::Other(_)) | None => {
1580 TransportFailureReason::ChannelClosed { addr }
1581 }
1582 };
1583 let failure = DeliveryFailure::new(UndeliverableReason::Transport(
1584 TransportFailure::new(target, reason.clone()),
1585 ));
1586 tracing::debug!(
1587 %error,
1588 send_error_reason = %reason_text,
1589 ?reason,
1590 "failed to enqueue in mailbox client while processing buffer",
1591 );
1592 message.undeliverable(failure, return_handle_0);
1593 },
1594 );
1595 tx.do_post(envelope, completion);
1597 future::ready(())
1598 })
1599 };
1600 let this = Self {
1601 addr: addr.clone(),
1602 buffer,
1603 _tx_monitoring: tx_monitoring.clone(),
1604 submitted: Arc::new(AtomicUsize::new(0)),
1605 completed,
1606 completed_notify,
1607 tx_status: tx_status.clone(),
1608 };
1609 Self::monitor_tx_health(tx_status, tx_monitoring, addr);
1610 this
1611 }
1612
1613 pub fn tx_status(&self) -> &watch::Receiver<TxStatus> {
1617 &self.tx_status
1618 }
1619
1620 pub fn dial(addr: ChannelAddr) -> Result<MailboxClient, ChannelError> {
1623 Ok(MailboxClient::new(channel::dial(addr)?))
1624 }
1625
1626 fn monitor_tx_health(
1628 mut rx: watch::Receiver<TxStatus>,
1629 cancel_token: CancellationToken,
1630 addr: ChannelAddr,
1631 ) {
1632 crate::init::get_runtime().spawn(async move {
1633 loop {
1634 tokio::select! {
1635 changed = rx.changed() => {
1636 if changed.is_err() || rx.borrow().is_closed() {
1637 let reason = rx.borrow().as_closed().map(|r| r.to_string()).unwrap_or_else(|| "unknown".to_string());
1638 tracing::warn!("connection to {} lost: {}", addr, reason);
1639 break;
1642 }
1643 }
1644 _ = cancel_token.cancelled() => {
1645 break;
1646 }
1647 }
1648 }
1649 });
1650 }
1651}
1652
1653#[async_trait]
1654impl MailboxSender for MailboxClient {
1655 fn post_unchecked(
1656 &self,
1657 envelope: MessageEnvelope,
1658 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
1659 ) {
1660 tracing::event!(target:"messages", tracing::Level::TRACE, "size"=envelope.data.len(), "sender"= %envelope.sender, "dest" = %envelope.dest.actor_addr(), "port"= envelope.dest.index(), "message_type" = envelope.data.typename().unwrap_or("unknown"), "send_message");
1661 if let Err(err) = self.buffer.send((envelope, return_handle)) {
1662 let mpsc::error::SendError((envelope, return_handle)) = *err;
1663 let target = envelope.dest().clone();
1664 let failure =
1665 DeliveryFailure::new(UndeliverableReason::Transport(TransportFailure::new(
1666 target,
1667 TransportFailureReason::LinkUnavailable(format!(
1668 "mailbox client buffer is closed for {}",
1669 self.addr
1670 )),
1671 )));
1672
1673 envelope.undeliverable(failure, return_handle);
1675 } else {
1676 self.submitted.fetch_add(1, Ordering::Relaxed);
1677 }
1678 }
1679
1680 async fn flush(&self) -> Result<(), anyhow::Error> {
1681 let target = self.submitted.load(Ordering::Relaxed);
1682 loop {
1683 let notified = self.completed_notify.notified();
1686 if self.completed.load(Ordering::Relaxed) >= target {
1687 return Ok(());
1688 }
1689 notified.await;
1690 }
1691 }
1692}
1693
1694pub struct PortSink<C: context::Actor, M: RemoteMessage> {
1696 cx: C,
1697 port: PortRef<M>,
1698}
1699
1700impl<C: context::Actor, M: RemoteMessage> PortSink<C, M> {
1701 pub fn new(cx: C, port: PortRef<M>) -> Self {
1703 Self { cx, port }
1704 }
1705}
1706
1707impl<C: context::Actor, M: RemoteMessage> Sink<M> for PortSink<C, M> {
1708 type Error = MailboxSenderError;
1709
1710 fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1711 Poll::Ready(Ok(()))
1712 }
1713
1714 fn start_send(self: Pin<&mut Self>, item: M) -> Result<(), Self::Error> {
1715 crate::Endpoint::post(&self.port, &self.cx, item);
1716 Ok(())
1717 }
1718
1719 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1720 Poll::Ready(Ok(()))
1721 }
1722
1723 fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1724 Poll::Ready(Ok(()))
1725 }
1726}
1727
1728#[derive(Clone, Debug)]
1731pub struct Mailbox {
1732 inner: Arc<State>,
1733}
1734
1735impl Mailbox {
1736 pub fn new(actor_id: impl Into<ActorAddr>) -> Self {
1738 Self {
1739 inner: Arc::new(State::new(actor_id.into())),
1740 }
1741 }
1742
1743 pub fn actor_addr(&self) -> &ActorAddr {
1745 &self.inner.actor_id
1746 }
1747
1748 pub fn open_port<M: Message>(&self) -> (PortHandle<M>, PortReceiver<M>) {
1753 let port_index = self.inner.allocate_port();
1754 let (sender, receiver) = sequenced_unbounded::<SequencedEnvelope<M>>();
1755 let port_id = self.inner.actor_id.port_addr(Port::from(port_index));
1756 tracing::trace!(
1757 name = "open_port",
1758 "opening port for {} at {}",
1759 self.inner.actor_id,
1760 port_id
1761 );
1762 (
1763 PortHandle::new(
1764 self.clone(),
1765 port_index,
1766 UnboundedPortSender::Sequenced(sender),
1767 ),
1768 PortReceiver::new(receiver, port_id, false, self.clone()),
1769 )
1770 }
1771
1772 pub(crate) fn bind_handler_port<M: RemoteMessage>(&self) -> (PortHandle<M>, PortReceiver<M>) {
1783 let (sender, receiver) = sequenced_unbounded::<SequencedEnvelope<M>>();
1784 let port_id = self.inner.actor_id.port_addr(Port::handler::<M>());
1785 let handle = PortHandle::new_full_with_target(
1786 self.clone(),
1787 UnboundedPortSender::Sequenced(sender),
1788 PortBindTarget::Handler,
1789 None,
1790 StreamingReducerOpts::default(),
1791 );
1792 handle.bind_handler_port();
1793 (
1794 handle,
1795 PortReceiver::new(receiver, port_id, false, self.clone()),
1796 )
1797 }
1798
1799 pub fn open_accum_port<A>(&self, accum: A) -> (PortHandle<A::Update>, PortReceiver<A::State>)
1802 where
1803 A: Accumulator + Send + Sync + 'static,
1804 A::Update: Message,
1805 A::State: Message + Default + Clone,
1806 {
1807 self.open_accum_port_opts(accum, StreamingReducerOpts::default())
1808 }
1809
1810 pub fn open_accum_port_opts<A>(
1818 &self,
1819 accum: A,
1820 streaming_opts: StreamingReducerOpts,
1821 ) -> (PortHandle<A::Update>, PortReceiver<A::State>)
1822 where
1823 A: Accumulator + Send + Sync + 'static,
1824 A::Update: Message,
1825 A::State: Message + Default + Clone,
1826 {
1827 let port_index = self.inner.allocate_port();
1828 let (sender, receiver) = sequenced_unbounded::<SequencedEnvelope<A::State>>();
1829 let port_id = self.inner.actor_id.port_addr(Port::from(port_index));
1830 let state = Mutex::new(A::State::default());
1831 let reducer_spec = accum.reducer_spec();
1832 let enqueue = move |_, update: A::Update| {
1833 let mut state = state.lock().unwrap();
1834 accum.accumulate(&mut state, update)?;
1835 let _ = sender.send(SequencedEnvelope::new(SeqInfo::Direct, None, state.clone()));
1836 Ok(())
1837 };
1838 (
1839 PortHandle::new_full(
1840 self.clone(),
1841 port_index,
1842 UnboundedPortSender::Func(Arc::new(enqueue)),
1843 reducer_spec,
1844 streaming_opts,
1845 ),
1846 PortReceiver::new(receiver, port_id, true, self.clone()),
1847 )
1848 }
1849
1850 #[cfg(test)]
1854 pub(crate) fn open_enqueue_port<M: Message>(
1855 &self,
1856 enqueue: impl Fn(Flattrs, M) -> Result<(), anyhow::Error> + Send + Sync + 'static,
1857 ) -> PortHandle<M> {
1858 PortHandle::new_full(
1859 self.clone(),
1860 self.inner.allocate_port(),
1861 UnboundedPortSender::Func(Arc::new(enqueue)),
1862 None,
1863 StreamingReducerOpts::default(),
1864 )
1865 }
1866
1867 pub(crate) fn open_handler_enqueue_port<M: Message>(
1870 &self,
1871 enqueue: impl Fn(Flattrs, M) -> Result<(), anyhow::Error> + Send + Sync + 'static,
1872 ) -> PortHandle<M> {
1873 let enqueue = Arc::new(enqueue);
1874 let sender = Arc::new(HandlerPortSender::new(
1875 UnboundedPortSender::Func(enqueue),
1876 self.inner.handler_ingress.clone(),
1877 ));
1878 PortHandle::new_full_with_target(
1879 self.clone(),
1880 UnboundedPortSender::Handler(sender),
1881 PortBindTarget::Handler,
1882 None,
1883 StreamingReducerOpts::default(),
1884 )
1885 }
1886
1887 pub fn open_once_port<M: Message>(&self) -> (OncePortHandle<M>, OncePortReceiver<M>) {
1891 let port_index = self.inner.allocate_port();
1892 let port_id = self.inner.actor_id.port_addr(Port::from(port_index));
1893 let (sender, receiver) = oneshot::channel::<M>();
1894 (
1895 OncePortHandle {
1896 mailbox: self.clone(),
1897 port_id: port_id.clone(),
1898 sender,
1899 reducer_spec: None,
1900 },
1901 OncePortReceiver {
1902 receiver: Some(receiver),
1903 port_id,
1904 mailbox: self.clone(),
1905 },
1906 )
1907 }
1908
1909 pub fn open_reduce_port<A, T>(
1923 &self,
1924 accum: A,
1925 ) -> (OncePortHandle<A::State>, OncePortReceiver<A::State>)
1926 where
1927 A: Accumulator<State = T, Update = T> + Send + Sync + 'static,
1928 T: Message + Default + Clone,
1929 {
1930 let port_index = self.inner.allocate_port();
1931 let (sender, receiver) = oneshot::channel::<T>();
1932 let port_id = self.inner.actor_id.port_addr(Port::from(port_index));
1933 let reducer_spec = accum.reducer_spec();
1934 assert!(
1935 reducer_spec.is_some(),
1936 "cannot use a reduce port without a ReducerSpec"
1937 );
1938
1939 (
1940 OncePortHandle {
1941 mailbox: self.clone(),
1942 port_id: port_id.clone(),
1943 sender,
1944 reducer_spec,
1945 },
1946 OncePortReceiver {
1947 receiver: Some(receiver),
1948 port_id,
1949 mailbox: self.clone(),
1950 },
1951 )
1952 }
1953
1954 #[allow(dead_code)]
1955 fn error(&self, err: MailboxErrorKind) -> MailboxError {
1956 MailboxError::new(self.inner.actor_id.clone(), err)
1957 }
1958
1959 fn lookup_sender<M: RemoteMessage>(&self) -> Option<UnboundedPortSender<M>> {
1960 let port = Port::handler::<M>();
1961 self.inner.ports.get(&port).and_then(|boxed| {
1962 boxed
1963 .as_any()
1964 .downcast_ref::<UnboundedSender<M>>()
1965 .map(|s| {
1966 assert_eq!(
1967 s.port_id,
1968 self.actor_addr().port_addr(port.clone()),
1969 "port_id mismatch in downcasted UnboundedSender"
1970 );
1971 s.sender.clone()
1972 })
1973 })
1974 }
1975
1976 pub fn bound_return_handle(&self) -> Option<PortHandle<Undeliverable<MessageEnvelope>>> {
1978 self.lookup_sender::<Undeliverable<MessageEnvelope>>()
1979 .map(|sender| PortHandle::new(self.clone(), self.inner.allocate_port(), sender))
1980 }
1981
1982 pub(crate) fn allocate_port(&self) -> u64 {
1983 self.inner.allocate_port()
1984 }
1985
1986 fn bind<M: RemoteMessage>(&self, handle: &PortHandle<M>) -> PortRef<M> {
1987 assert_eq!(
1988 handle.inner.mailbox.actor_addr(),
1989 self.actor_addr(),
1990 "port does not belong to mailbox"
1991 );
1992
1993 let port_ref = self
1996 .actor_addr()
1997 .port_addr(Port::from(handle.inner.bind_target.ephemeral_index()));
1998 match self.inner.ports.entry(port_ref.port()) {
1999 Entry::Vacant(entry) => {
2000 entry.insert(Arc::new(UnboundedSender::new(
2001 handle.inner.sender.clone(),
2002 port_ref.clone(),
2003 )));
2004 }
2005 Entry::Occupied(_entry) => {}
2006 }
2007
2008 PortRef::attest(port_ref)
2009 }
2010
2011 fn bind_to_handler_port<M: RemoteMessage>(&self, handle: &PortHandle<M>) {
2012 self.bind_to_port(handle, Port::handler::<M>());
2013 }
2014
2015 fn bind_to_control_port<M: RemoteMessage>(&self, handle: &PortHandle<M>, port: ControlPort) {
2016 self.bind_to_port(handle, Port::control(port));
2017 }
2018
2019 fn bind_to_port<M: RemoteMessage>(&self, handle: &PortHandle<M>, port: Port) {
2020 assert_eq!(
2021 handle.inner.mailbox.actor_addr(),
2022 self.actor_addr(),
2023 "port does not belong to mailbox"
2024 );
2025
2026 let port_ref = self.actor_addr().port_addr(port.clone());
2027 match self.inner.ports.entry(port) {
2028 Entry::Vacant(entry) => {
2029 entry.insert(Arc::new(UnboundedSender::new(
2030 handle.inner.sender.clone(),
2031 port_ref.clone(),
2032 )));
2033 }
2034 Entry::Occupied(_entry) => panic!("port {} already bound", port_ref),
2035 }
2036 }
2037
2038 fn bind_once<M: RemoteMessage>(&self, handle: OncePortHandle<M>) {
2039 let port_id = handle.port_addr().clone();
2040 match self.inner.ports.entry(port_id.port()) {
2041 Entry::Vacant(entry) => {
2042 entry.insert(Arc::new(OnceSender::new(handle.sender, port_id.clone())));
2043 }
2044 Entry::Occupied(_entry) => {}
2045 }
2046 }
2047
2048 pub(crate) fn bind_untyped(&self, port_id: &PortAddr, sender: UntypedUnboundedSender) {
2049 assert_eq!(
2050 port_id.actor_addr(),
2051 *self.actor_addr(),
2052 "port does not belong to mailbox"
2053 );
2054
2055 match self.inner.ports.entry(port_id.port()) {
2056 Entry::Vacant(entry) => {
2057 entry.insert(Arc::new(sender));
2058 }
2059 Entry::Occupied(_entry) => {}
2060 }
2061 }
2062
2063 pub(crate) fn close(&self, status: ActorStatus) {
2064 let mut closed = self.inner.closed.write().unwrap();
2065 if closed.is_some() {
2066 panic!("mailbox with owner {} already closed", self.actor_addr());
2067 }
2068 let _ = closed.insert(status);
2069 }
2070
2071 pub(crate) fn drain(&self) {
2089 self.inner.handler_ingress.drain();
2090 }
2091}
2092
2093impl context::Mailbox for Mailbox {
2094 fn mailbox(&self) -> &Mailbox {
2095 self
2096 }
2097}
2098
2099pub fn open_port<M: Message>(cx: &impl context::Mailbox) -> (PortHandle<M>, PortReceiver<M>) {
2104 cx.mailbox().open_port()
2105}
2106
2107pub fn open_once_port<M: Message>(
2110 cx: &impl context::Mailbox,
2111) -> (OncePortHandle<M>, OncePortReceiver<M>) {
2112 cx.mailbox().open_once_port()
2113}
2114
2115#[async_trait]
2116impl MailboxSender for Mailbox {
2117 fn post_unchecked(
2120 &self,
2121 envelope: MessageEnvelope,
2122 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
2123 ) {
2124 metrics::MAILBOX_POSTS.add(
2125 1,
2126 hyperactor_telemetry::kv_pairs!(
2127 "actor_id" => envelope.sender.to_string(),
2128 "dest_actor_id" => envelope.dest.actor_addr().to_string(),
2129 ),
2130 );
2131 tracing::trace!(
2132 name = "post",
2133 actor_name = envelope.sender.label().map_or("?", |l| l.as_str()),
2134 actor_id = envelope.sender.to_string(),
2135 "posting message to {}",
2136 envelope.dest
2137 );
2138
2139 if envelope.dest().actor_id() != self.inner.actor_id.id() {
2140 let failure = DeliveryFailure::new(InvalidReference::new(
2141 envelope.dest().actor_addr(),
2142 InvalidReferenceReason::WrongMailboxOwner,
2143 ));
2144 return envelope.undeliverable(failure, return_handle);
2145 }
2146
2147 let port = envelope.dest().port();
2148
2149 let port_sender = match self.inner.ports.get(&port) {
2159 None => {
2160 let failure = unbound_port_delivery_failure(
2161 envelope.dest(),
2162 envelope.data(),
2163 self.inner.next_ephemeral_port.load(Ordering::SeqCst),
2164 );
2165 return envelope.undeliverable(failure, return_handle);
2166 }
2167 Some(ref_) => {
2168 let closed = self.inner.closed.read().unwrap();
2169 if let Some(status) = &*closed {
2170 match status {
2171 ActorStatus::Stopped(reason) => {
2172 tracing::debug!(
2173 owner=%self.inner.actor_id,
2174 %reason,
2175 "mailbox owner is stopped",
2176 );
2177 let failure = DeliveryFailure::new(InvalidReference::new(
2178 envelope.dest().actor_addr(),
2179 InvalidReferenceReason::ActorStopped,
2180 ));
2181 return envelope.undeliverable(failure, return_handle);
2182 }
2183 ActorStatus::Failed(actor_error) => {
2184 tracing::debug!(
2185 owner=%self.inner.actor_id,
2186 %actor_error,
2187 "mailbox owner failed",
2188 );
2189 let failure = DeliveryFailure::new(InvalidReference::new(
2190 envelope.dest().actor_addr(),
2191 InvalidReferenceReason::ActorFailed,
2192 ));
2193 return envelope.undeliverable(failure, return_handle);
2194 }
2195 _ => {
2196 let failure = DeliveryFailure::new(UndeliverableReason::Transport(
2197 TransportFailure::new(
2198 envelope.dest().actor_addr(),
2199 TransportFailureReason::LinkUnavailable(format!(
2200 "mailbox owner {} closed unexpectedly: {:?}",
2201 self.inner.actor_id, status
2202 )),
2203 ),
2204 ));
2205 return envelope.undeliverable(failure, return_handle);
2206 }
2207 }
2208 }
2209 Arc::clone(&*ref_)
2212 }
2213 };
2214 let (metadata, data) = envelope.open();
2217 let MessageMetadata {
2218 mut headers,
2219 sender,
2220 dest,
2221 next_hop,
2222 delivery_failures,
2223 ttl,
2224 return_undeliverable,
2225 } = metadata;
2226
2227 let to_actor_id = hash_to_u64(dest.actor_addr().id());
2228 let message_id = hyperactor_telemetry::generate_message_id(to_actor_id);
2229 headers.set(crate::mailbox::headers::TELEMETRY_MESSAGE_ID, message_id);
2230 if !headers.contains_key(crate::mailbox::headers::SENDER_ACTOR_ID_HASH) {
2233 headers.set(
2234 crate::mailbox::headers::SENDER_ACTOR_ID_HASH,
2235 hash_to_u64(sender.id()),
2236 );
2237 }
2238 headers.set(crate::mailbox::headers::TELEMETRY_PORT_INDEX, dest.index());
2239
2240 match port_sender.send_serialized(headers, data) {
2241 Ok(disposition) => {
2242 hyperactor_telemetry::notify_message_status(
2243 hyperactor_telemetry::MessageStatusEvent {
2244 timestamp: std::time::SystemTime::now(),
2245 id: hyperactor_telemetry::generate_status_event_id(message_id),
2246 message_id,
2247 status: "queued".to_string(),
2248 },
2249 );
2250
2251 if disposition == SerializedSendDisposition::DeliveredAndExhausted {
2252 self.inner.ports.remove(&port);
2253 }
2254 }
2255 Err(SerializedSendFailure::Dead { data, headers }) => {
2256 self.inner.ports.remove(&port);
2257 let failure = port_gone_delivery_failure(&dest, &data);
2258
2259 MessageEnvelope::seal(
2260 MessageMetadata {
2261 headers,
2262 sender,
2263 dest,
2264 next_hop,
2265 delivery_failures,
2266 ttl,
2267 return_undeliverable,
2268 },
2269 data,
2270 )
2271 .undeliverable(failure, return_handle)
2272 }
2273 Err(SerializedSendFailure::Error(SerializedSendError {
2274 data,
2275 error: sender_error,
2276 headers,
2277 })) => {
2278 let failure = serialized_send_error_delivery_failure(&dest, &sender_error);
2279
2280 let envelope = MessageEnvelope::seal(
2281 MessageMetadata {
2282 headers,
2283 sender,
2284 dest,
2285 next_hop,
2286 delivery_failures,
2287 ttl,
2288 return_undeliverable,
2289 },
2290 data,
2291 );
2292 envelope.undeliverable(failure, return_handle)
2293 }
2294 }
2295 }
2296}
2297
2298fn unbound_port_delivery_failure(
2299 port: &PortAddr,
2300 data: &wirevalue::Any,
2301 next_ephemeral_port: u64,
2302) -> DeliveryFailure {
2303 if port.is_handler_port() {
2304 DeliveryFailure::new(InvalidReference::new(
2305 port.clone(),
2306 InvalidReferenceReason::HandlerNotBound,
2307 ))
2308 } else {
2309 match port.ephemeral_index() {
2310 Some(index) if index < next_ephemeral_port => port_gone_delivery_failure(port, data),
2311 _ => DeliveryFailure::new(InvalidReference::new(
2312 port.clone(),
2313 InvalidReferenceReason::PortNeverAllocated,
2314 )),
2315 }
2316 }
2317}
2318
2319fn serialized_send_error_delivery_failure(
2320 dest: &PortAddr,
2321 sender_error: &MailboxSenderError,
2322) -> DeliveryFailure {
2323 match sender_error.kind() {
2324 MailboxSenderErrorKind::Deserialize(_, _) => DeliveryFailure::new(InvalidReference::new(
2325 dest.clone(),
2326 InvalidReferenceReason::ProtocolMismatch,
2327 )),
2328 MailboxSenderErrorKind::Invalid => {
2329 let reason = if dest.is_handler_port() {
2330 InvalidReferenceReason::HandlerNotBound
2331 } else {
2332 InvalidReferenceReason::PortNeverAllocated
2333 };
2334 DeliveryFailure::new(InvalidReference::new(dest.clone(), reason))
2335 }
2336 MailboxSenderErrorKind::Closed => DeliveryFailure::new(UndeliverableReason::PortGone(
2337 PortGone::new(dest.clone(), None),
2338 )),
2339 _ => DeliveryFailure::new(UndeliverableReason::Transport(TransportFailure::new(
2340 dest.clone(),
2341 TransportFailureReason::LinkUnavailable(sender_error.to_string()),
2342 ))),
2343 }
2344}
2345
2346fn port_gone_delivery_failure(port: &PortAddr, data: &wirevalue::Any) -> DeliveryFailure {
2347 DeliveryFailure::new(port_gone(port, data))
2348}
2349
2350fn port_gone(port: &PortAddr, data: &wirevalue::Any) -> UndeliverableReason {
2351 UndeliverableReason::PortGone(PortGone::new(
2352 port.clone(),
2353 data.typename().map(str::to_string),
2354 ))
2355}
2356
2357#[derive(Debug, Clone, Copy)]
2358enum PortBindTarget {
2359 Ephemeral(u64),
2360 Handler,
2361}
2362
2363impl PortBindTarget {
2364 fn ephemeral_index(self) -> u64 {
2365 match self {
2366 Self::Ephemeral(port_index) => port_index,
2367 Self::Handler => panic!("handler port handle has no ephemeral port index"),
2368 }
2369 }
2370}
2371
2372struct PortHandleInner<M: Message> {
2375 mailbox: Mailbox,
2376 sender: UnboundedPortSender<M>,
2377 bind_target: PortBindTarget,
2378 bound: Arc<RwLock<Option<PortAddr>>>,
2385 reducer_spec: Option<ReducerSpec>,
2388 streaming_opts: StreamingReducerOpts,
2390}
2391
2392impl<M: Message> fmt::Debug for PortHandleInner<M> {
2393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2394 f.debug_struct("PortHandleInner")
2395 .field("mailbox", &self.mailbox)
2396 .field("sender", &self.sender)
2397 .field("bind_target", &self.bind_target)
2398 .field("bound", &self.bound)
2399 .field("reducer_spec", &self.reducer_spec)
2400 .field("streaming_opts", &self.streaming_opts)
2401 .finish()
2402 }
2403}
2404
2405#[derive(Debug)]
2413pub struct PortHandle<M: Message> {
2414 inner: Arc<PortHandleInner<M>>,
2415}
2416
2417impl<M: Message> PortHandle<M> {
2418 fn new_full(
2419 mailbox: Mailbox,
2420 port_index: u64,
2421 sender: UnboundedPortSender<M>,
2422 reducer_spec: Option<ReducerSpec>,
2423 streaming_opts: StreamingReducerOpts,
2424 ) -> Self {
2425 Self::new_full_with_target(
2426 mailbox,
2427 sender,
2428 PortBindTarget::Ephemeral(port_index),
2429 reducer_spec,
2430 streaming_opts,
2431 )
2432 }
2433
2434 fn new_full_with_target(
2435 mailbox: Mailbox,
2436 sender: UnboundedPortSender<M>,
2437 bind_target: PortBindTarget,
2438 reducer_spec: Option<ReducerSpec>,
2439 streaming_opts: StreamingReducerOpts,
2440 ) -> Self {
2441 Self {
2442 inner: Arc::new(PortHandleInner {
2443 mailbox,
2444 sender,
2445 bind_target,
2446 bound: Arc::new(RwLock::new(None)),
2447 reducer_spec,
2448 streaming_opts,
2449 }),
2450 }
2451 }
2452
2453 fn new(mailbox: Mailbox, port_index: u64, sender: UnboundedPortSender<M>) -> Self {
2454 Self::new_full(
2455 mailbox,
2456 port_index,
2457 sender,
2458 None,
2459 StreamingReducerOpts::default(),
2460 )
2461 }
2462
2463 pub(crate) fn location(&self) -> PortLocation {
2464 match self.inner.bound.read().unwrap().as_ref() {
2465 Some(port_id) => PortLocation::Bound(port_id.clone()),
2466 None => PortLocation::new_unbound::<M>(self.inner.mailbox.actor_addr().clone()),
2467 }
2468 }
2469
2470 pub fn try_post<C>(&self, cx: &C, message: M) -> Result<(), MailboxSenderError>
2476 where
2477 C: context::Actor,
2478 {
2479 let closed = self.inner.mailbox.inner.closed.read().unwrap();
2480
2481 if let Some(status) = &*closed {
2482 let err = MailboxError {
2483 actor_id: self.inner.mailbox.actor_addr().clone(),
2484 kind: MailboxErrorKind::OwnerTerminated(status.clone()),
2485 };
2486 return Err(MailboxSenderError::new_unbound::<M>(
2487 self.inner.mailbox.actor_addr().clone(),
2488 MailboxSenderErrorKind::Mailbox(err),
2489 ));
2490 }
2491 let mut headers = Flattrs::new();
2492
2493 crate::mailbox::headers::set_send_timestamp(&mut headers);
2494 crate::mailbox::headers::set_rust_message_type::<M>(&mut headers);
2495 let bound_guard = self.inner.bound.read().unwrap();
2500 if let Some(dest) = bound_guard.as_ref() {
2501 let sequencer = cx.instance().sequencer();
2502 let seq_info = sequencer.assign_seq(dest);
2503 if let SeqInfo::Session { seq, .. } = &seq_info {
2507 crate::mailbox::headers::stamp_sender_actor_id_fresh(
2508 &mut headers,
2509 *seq,
2510 dest,
2511 cx.mailbox().actor_addr(),
2512 );
2513 }
2514 headers.set(SEQ_INFO, seq_info);
2515 } else {
2516 headers.set(SEQ_INFO, SeqInfo::Direct);
2517 }
2518 self.inner.sender.send(headers, message).map_err(|err| {
2530 MailboxSenderError::new_unbound::<M>(
2531 self.inner.mailbox.actor_addr().clone(),
2532 classify_sender_error(err),
2533 )
2534 })
2535 }
2536}
2537
2538impl<M> Endpoint<M> for &PortHandle<M>
2539where
2540 M: Message,
2541{
2542 fn endpoint_location(&self) -> EndpointLocation {
2543 self.location().into()
2544 }
2545
2546 fn post<C>(self, cx: &C, message: M)
2547 where
2548 C: context::Actor,
2549 {
2550 if let Err(err) = self.try_post(cx, message) {
2551 cx.instance()
2552 .report_delivery_failure(DeliveryFailureReport::from_send_error::<M>(
2553 cx.mailbox().actor_addr().clone(),
2554 self.endpoint_location(),
2555 &err,
2556 ));
2557 }
2558 }
2559}
2560
2561impl<M: Message> PortHandle<M> {
2562 pub fn contramap<R, F>(&self, unmap: F) -> PortHandle<R>
2565 where
2566 R: Message,
2567 F: Fn(R) -> M + Send + Sync + 'static,
2568 {
2569 let port_index = self.inner.mailbox.inner.allocate_port();
2570 let sender = self.inner.sender.clone();
2571 PortHandle::new(
2572 self.inner.mailbox.clone(),
2573 port_index,
2574 UnboundedPortSender::Func(Arc::new(move |headers, value: R| {
2575 sender.send(headers, unmap(value))
2576 })),
2577 )
2578 }
2579}
2580
2581impl<M: RemoteMessage> PortHandle<M> {
2582 pub fn bind(&self) -> PortRef<M> {
2587 match self.inner.bind_target {
2588 PortBindTarget::Ephemeral(_) => self.bind_ephemeral_port(),
2589 PortBindTarget::Handler => self.bind_handler_port(),
2590 }
2591 }
2592
2593 pub(crate) fn bind_handler_port(&self) -> PortRef<M> {
2599 self.bind_to_port(Port::handler::<M>(), |mailbox, handle| {
2600 mailbox.bind_to_handler_port(handle);
2601 })
2602 }
2603
2604 pub(crate) fn bind_control_port(&self, port: ControlPort) -> PortRef<M> {
2609 self.bind_to_port(Port::control(port), |mailbox, handle| {
2610 mailbox.bind_to_control_port(handle, port);
2611 })
2612 }
2613
2614 fn bind_ephemeral_port(&self) -> PortRef<M> {
2615 let port_addr = {
2616 let mut guard = self.inner.bound.write().unwrap();
2617 match guard.as_ref() {
2618 Some(existing) => existing.clone(),
2619 None => {
2620 let port_addr = self.inner.mailbox.bind(self).into_port_addr();
2621 *guard = Some(port_addr.clone());
2622 port_addr
2623 }
2624 }
2625 };
2626 self.port_ref(port_addr)
2627 }
2628
2629 fn bind_to_port(&self, port: Port, bind: impl FnOnce(&Mailbox, &PortHandle<M>)) -> PortRef<M> {
2630 let port_id = self.inner.mailbox.actor_addr().port_addr(port);
2631 {
2632 let mut guard = self.inner.bound.write().unwrap();
2633 match guard.as_ref() {
2634 Some(existing) if existing == &port_id => {}
2635 Some(existing) => panic!(
2636 "could not bind port handle {:?} as {port_id}: already bound to {existing}",
2637 self.inner.bind_target
2638 ),
2639 None => {
2640 bind(&self.inner.mailbox, self);
2641 *guard = Some(port_id.clone());
2642 }
2643 }
2644 }
2645 self.port_ref(port_id)
2646 }
2647
2648 fn port_ref(&self, port_addr: PortAddr) -> PortRef<M> {
2649 PortRef::attest_reducible(
2650 port_addr,
2651 self.inner.reducer_spec.clone(),
2652 self.inner.streaming_opts.clone(),
2653 )
2654 }
2655}
2656
2657impl<M: Message> Clone for PortHandle<M> {
2658 fn clone(&self) -> Self {
2659 Self {
2660 inner: Arc::clone(&self.inner),
2661 }
2662 }
2663}
2664
2665impl<M: Message> fmt::Display for PortHandle<M> {
2666 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2667 fmt::Display::fmt(&self.location(), f)
2668 }
2669}
2670
2671#[derive(Debug)]
2673pub struct OncePortHandle<M: Message> {
2674 mailbox: Mailbox,
2675 port_id: PortAddr,
2676 sender: oneshot::Sender<M>,
2677 reducer_spec: Option<ReducerSpec>,
2678}
2679
2680impl<M: Message> OncePortHandle<M> {
2681 pub fn port_addr(&self) -> &PortAddr {
2684 &self.port_id
2685 }
2686
2687 pub fn try_post<C>(self, _cx: &C, message: M) -> Result<(), MailboxSenderError>
2692 where
2693 C: context::Actor,
2694 {
2695 assert!(
2698 !self.port_addr().is_handler_port(),
2699 "OncePortHandle currently does not support handler ports; a \
2700 prerequisite of that support is to assign seq to messages \
2701 if the port is a handler port."
2702 );
2703
2704 let actor_id = self.mailbox.actor_addr().clone();
2705 self.sender.send(message).map_err(|_| {
2706 MailboxSenderError::new_unbound::<M>(actor_id, MailboxSenderErrorKind::Closed)
2711 })
2712 }
2713}
2714
2715impl<M> Endpoint<M> for OncePortHandle<M>
2716where
2717 M: Message,
2718{
2719 fn endpoint_location(&self) -> EndpointLocation {
2720 EndpointLocation::Port(self.port_id.clone())
2721 }
2722
2723 fn post<C>(self, cx: &C, message: M)
2724 where
2725 C: context::Actor,
2726 {
2727 let endpoint_location = self.endpoint_location();
2728 if let Err(err) = self.try_post(cx, message) {
2729 cx.instance()
2730 .report_delivery_failure(DeliveryFailureReport::from_send_error::<M>(
2731 cx.mailbox().actor_addr().clone(),
2732 endpoint_location,
2733 &err,
2734 ));
2735 }
2736 }
2737}
2738
2739impl<M: RemoteMessage> OncePortHandle<M> {
2740 pub fn bind(self) -> OncePortRef<M> {
2745 let port_id: PortAddr = self.port_addr().clone();
2746 let reducer_spec = self.reducer_spec.clone();
2747 self.mailbox.clone().bind_once(self);
2748 OncePortRef::attest_reducible(port_id, reducer_spec)
2749 }
2750}
2751
2752impl<M: Message> fmt::Display for OncePortHandle<M> {
2753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2754 fmt::Display::fmt(&self.port_addr(), f)
2755 }
2756}
2757
2758#[derive(Debug)]
2761pub struct PortReceiver<M> {
2762 receiver: SequencedReceiver<SequencedEnvelope<M>>,
2763 port_id: PortAddr,
2764 coalesce: bool,
2767 mailbox: Mailbox,
2770}
2771
2772impl<M> PortReceiver<M> {
2773 fn new(
2774 receiver: SequencedReceiver<SequencedEnvelope<M>>,
2775 port_id: PortAddr,
2776 coalesce: bool,
2777 mailbox: Mailbox,
2778 ) -> Self {
2779 Self {
2780 receiver,
2781 port_id,
2782 coalesce,
2783 mailbox,
2784 }
2785 }
2786
2787 #[allow(clippy::result_large_err)] pub fn try_recv(&mut self) -> Result<Option<M>, MailboxError> {
2792 let mut next = self.receiver.try_recv();
2793 if self.coalesce
2795 && let Some(latest) = self.drain().pop()
2796 {
2797 next = Ok(latest);
2798 }
2799 match next {
2800 Ok(msg) => Ok(Some(msg)),
2801 Err(mpsc::error::TryRecvError::Empty) => Ok(None),
2802 Err(mpsc::error::TryRecvError::Disconnected) => Err(MailboxError::new(
2803 self.actor_addr().clone(),
2804 MailboxErrorKind::Closed,
2805 )),
2806 }
2807 }
2808
2809 pub async fn recv(&mut self) -> Result<M, MailboxError> {
2812 let mut next = self.receiver.recv().await;
2813 if self.coalesce
2816 && let Some(latest) = self.drain().pop()
2817 {
2818 next = Some(latest);
2819 }
2820 next.ok_or(MailboxError::new(
2821 self.actor_addr().clone(),
2822 MailboxErrorKind::Closed,
2823 ))
2824 }
2825
2826 pub fn drain(&mut self) -> Vec<M> {
2828 let mut drained: Vec<M> = Vec::new();
2829 while let Ok(msg) = self.receiver.try_recv() {
2830 if self.coalesce {
2832 drained.pop();
2833 }
2834 drained.push(msg);
2835 }
2836 drained
2837 }
2838
2839 fn port(&self) -> Port {
2840 self.port_id.port()
2841 }
2842
2843 fn actor_addr(&self) -> ActorAddr {
2844 self.port_id.actor_addr()
2845 }
2846}
2847
2848impl<M> Drop for PortReceiver<M> {
2849 fn drop(&mut self) {
2850 self.mailbox.inner.ports.remove(&self.port());
2854 }
2855}
2856
2857impl<M> Unpin for PortReceiver<M> {}
2858
2859impl<M> Stream for PortReceiver<M> {
2860 type Item = Result<M, MailboxError>;
2861
2862 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
2863 std::pin::pin!(self.recv()).poll(cx).map(Some)
2864 }
2865}
2866
2867pub struct OncePortReceiver<M> {
2869 receiver: Option<oneshot::Receiver<M>>,
2870 port_id: PortAddr,
2871
2872 mailbox: Mailbox,
2875}
2876
2877impl<M> OncePortReceiver<M> {
2878 pub async fn recv(mut self) -> Result<M, MailboxError> {
2882 std::mem::take(&mut self.receiver)
2883 .unwrap()
2884 .await
2885 .map_err(|err| {
2886 MailboxError::new(
2887 self.actor_addr().clone(),
2888 MailboxErrorKind::Recv(self.port_id.clone(), err.into()),
2889 )
2890 })
2891 }
2892
2893 fn port(&self) -> Port {
2894 self.port_id.port()
2895 }
2896
2897 fn actor_addr(&self) -> ActorAddr {
2898 self.port_id.actor_addr()
2899 }
2900}
2901
2902impl<M> Drop for OncePortReceiver<M> {
2903 fn drop(&mut self) {
2904 self.mailbox.inner.ports.remove(&self.port());
2908 }
2909}
2910
2911#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2912pub(crate) enum SerializedSendDisposition {
2913 Delivered,
2914 DeliveredAndExhausted,
2915}
2916
2917pub(crate) struct SerializedSendError {
2919 pub(crate) headers: Flattrs,
2921 pub(crate) data: wirevalue::Any,
2923 pub(crate) error: MailboxSenderError,
2925}
2926
2927pub(crate) enum SerializedSendFailure {
2928 Dead {
2929 headers: Flattrs,
2930 data: wirevalue::Any,
2931 },
2932 Error(SerializedSendError),
2933}
2934
2935trait SerializedSender: Send + Sync {
2940 fn as_any(&self) -> &dyn Any;
2946
2947 fn send_serialized(
2954 &self,
2955 headers: Flattrs,
2956 serialized: wirevalue::Any,
2957 ) -> Result<SerializedSendDisposition, SerializedSendFailure>;
2958}
2959
2960#[derive(Debug, thiserror::Error)]
2961#[error("handler port closed")]
2962struct HandlerPortClosedError;
2963
2964fn classify_sender_error(err: anyhow::Error) -> MailboxSenderErrorKind {
2965 if err.is::<HandlerPortClosedError>() {
2966 MailboxSenderErrorKind::Closed
2967 } else {
2968 MailboxSenderErrorKind::Other(err)
2969 }
2970}
2971
2972enum UnboundedPortSender<M: Message> {
2974 Sequenced(mpsc::UnboundedSender<SequencedEnvelope<M>>),
2976 Func(Arc<dyn Fn(Flattrs, M) -> Result<(), anyhow::Error> + Send + Sync>),
2978 Handler(Arc<HandlerPortSender<M>>),
2980}
2981
2982impl<M: Message> UnboundedPortSender<M> {
2983 fn send(&self, headers: Flattrs, message: M) -> Result<(), anyhow::Error> {
2984 match self {
2985 Self::Sequenced(sender) => {
2986 let seq_info = headers.get(SEQ_INFO).unwrap_or(SeqInfo::Direct);
2987 if !seq_info.is_valid() {
2988 return Err(anyhow::anyhow!("sequenced port send has invalid SEQ_INFO"));
2989 }
2990 let sender_addr = headers.get(crate::mailbox::headers::SENDER_ACTOR_ID);
2991 sender
2992 .send(SequencedEnvelope::new(seq_info, sender_addr, message))
2993 .map_err(anyhow::Error::from)
2994 }
2995 Self::Func(func) => func(headers, message),
2996 Self::Handler(sender) => sender.send(headers, message),
2997 }
2998 }
2999}
3000
3001impl<M: Message> Clone for UnboundedPortSender<M> {
3004 fn clone(&self) -> Self {
3005 match self {
3006 Self::Sequenced(sender) => Self::Sequenced(sender.clone()),
3007 Self::Func(func) => Self::Func(func.clone()),
3008 Self::Handler(sender) => Self::Handler(sender.clone()),
3009 }
3010 }
3011}
3012
3013impl<M: Message> Debug for UnboundedPortSender<M> {
3014 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
3015 match self {
3016 Self::Sequenced(q) => f
3017 .debug_tuple("UnboundedPortSender::Sequenced")
3018 .field(q)
3019 .finish(),
3020 Self::Func(_) => f
3021 .debug_tuple("UnboundedPortSender::Func")
3022 .field(&"..")
3023 .finish(),
3024 Self::Handler(_) => f
3025 .debug_tuple("UnboundedPortSender::Handler")
3026 .field(&"..")
3027 .finish(),
3028 }
3029 }
3030}
3031
3032const HANDLER_INGRESS_DRAINING: usize = 1usize << (usize::BITS as usize - 1);
3033const HANDLER_INGRESS_ACTIVE_MASK: usize = !HANDLER_INGRESS_DRAINING;
3034
3035struct HandlerIngressGate {
3036 state: AtomicUsize,
3037 wait_lock: Mutex<()>,
3038 drained: Condvar,
3039}
3040
3041struct HandlerIngressGuard {
3042 gate: Arc<HandlerIngressGate>,
3043}
3044
3045impl HandlerIngressGate {
3046 fn new() -> Self {
3047 Self {
3048 state: AtomicUsize::new(0),
3049 wait_lock: Mutex::new(()),
3050 drained: Condvar::new(),
3051 }
3052 }
3053
3054 fn try_enter(self: &Arc<Self>) -> Result<HandlerIngressGuard, HandlerPortClosedError> {
3055 let mut state = self.state.load(Ordering::Acquire);
3056 loop {
3057 if state & HANDLER_INGRESS_DRAINING != 0 {
3058 return Err(HandlerPortClosedError);
3059 }
3060
3061 let active = state & HANDLER_INGRESS_ACTIVE_MASK;
3062 assert!(
3063 active < HANDLER_INGRESS_ACTIVE_MASK,
3064 "too many active handler ingress sends"
3065 );
3066
3067 match self.state.compare_exchange_weak(
3068 state,
3069 state + 1,
3070 Ordering::AcqRel,
3071 Ordering::Acquire,
3072 ) {
3073 Ok(_) => {
3074 return Ok(HandlerIngressGuard {
3075 gate: Arc::clone(self),
3076 });
3077 }
3078 Err(next_state) => state = next_state,
3079 }
3080 }
3081 }
3082
3083 fn drain(&self) {
3084 let mut state = self.state.load(Ordering::Acquire);
3085 loop {
3086 if state & HANDLER_INGRESS_DRAINING != 0 {
3087 break;
3088 }
3089 match self.state.compare_exchange_weak(
3090 state,
3091 state | HANDLER_INGRESS_DRAINING,
3092 Ordering::AcqRel,
3093 Ordering::Acquire,
3094 ) {
3095 Ok(_) => break,
3096 Err(next_state) => state = next_state,
3097 }
3098 }
3099
3100 let mut wait_guard = self.wait_lock.lock().unwrap();
3101 while self.state.load(Ordering::Acquire) & HANDLER_INGRESS_ACTIVE_MASK != 0 {
3102 wait_guard = self.drained.wait(wait_guard).unwrap();
3103 }
3104 }
3105}
3106
3107impl Drop for HandlerIngressGuard {
3108 fn drop(&mut self) {
3109 let previous = self.gate.state.fetch_sub(1, Ordering::AcqRel);
3110 assert!(
3111 previous & HANDLER_INGRESS_ACTIVE_MASK != 0,
3112 "handler ingress active count underflow"
3113 );
3114 if previous & HANDLER_INGRESS_DRAINING != 0 && previous & HANDLER_INGRESS_ACTIVE_MASK == 1 {
3115 let _wait_guard = self.gate.wait_lock.lock().unwrap();
3121 self.gate.drained.notify_all();
3122 }
3123 }
3124}
3125
3126struct HandlerPortSender<M: Message> {
3127 sender: UnboundedPortSender<M>,
3128 gate: Arc<HandlerIngressGate>,
3129}
3130
3131impl<M: Message> HandlerPortSender<M> {
3132 fn new(sender: UnboundedPortSender<M>, gate: Arc<HandlerIngressGate>) -> Self {
3133 Self { sender, gate }
3134 }
3135
3136 fn send(&self, headers: Flattrs, message: M) -> Result<(), anyhow::Error> {
3137 let _guard = self.gate.try_enter()?;
3138 self.sender.send(headers, message)
3139 }
3140}
3141
3142struct UnboundedSender<M: Message> {
3143 sender: UnboundedPortSender<M>,
3144 port_id: PortAddr,
3145}
3146
3147impl<M: Message> UnboundedSender<M> {
3148 fn new(sender: UnboundedPortSender<M>, port_id: PortAddr) -> Self {
3151 Self { sender, port_id }
3152 }
3153
3154 #[allow(dead_code)]
3155 fn send(&self, headers: Flattrs, message: M) -> Result<(), MailboxSenderError> {
3156 self.sender.send(headers, message).map_err(|err| {
3157 MailboxSenderError::new_bound(self.port_id.clone(), classify_sender_error(err))
3158 })
3159 }
3160}
3161
3162impl<M: Message> Clone for UnboundedSender<M> {
3166 fn clone(&self) -> Self {
3167 Self {
3168 sender: self.sender.clone(),
3169 port_id: self.port_id.clone(),
3170 }
3171 }
3172}
3173
3174impl<M: RemoteMessage> SerializedSender for UnboundedSender<M> {
3175 fn as_any(&self) -> &dyn Any {
3176 self
3177 }
3178
3179 fn send_serialized(
3180 &self,
3181 headers: Flattrs,
3182 serialized: wirevalue::Any,
3183 ) -> Result<SerializedSendDisposition, SerializedSendFailure> {
3184 match serialized.deserialized_unchecked() {
3189 Ok(message) => match self.sender.send(headers.clone(), message) {
3190 Ok(()) => Ok(SerializedSendDisposition::Delivered),
3191 Err(_) if matches!(&self.sender, UnboundedPortSender::Sequenced(_)) => {
3192 Err(SerializedSendFailure::Dead {
3193 data: serialized,
3194 headers,
3195 })
3196 }
3197 Err(err) => Err(SerializedSendFailure::Error(SerializedSendError {
3198 data: serialized,
3199 error: MailboxSenderError::new_bound(
3200 self.port_id.clone(),
3201 classify_sender_error(err),
3202 ),
3203 headers,
3204 })),
3205 },
3206 Err(err) => Err(SerializedSendFailure::Error(SerializedSendError {
3207 data: serialized,
3208 error: MailboxSenderError::new_bound(
3209 self.port_id.clone(),
3210 MailboxSenderErrorKind::Deserialize(M::typename(), err.into()),
3211 ),
3212 headers,
3213 })),
3214 }
3215 }
3216}
3217
3218#[derive(Debug)]
3221struct OnceSender<M: Message> {
3222 sender: Arc<Mutex<Option<oneshot::Sender<M>>>>,
3223 port_id: PortAddr,
3224}
3225
3226impl<M: Message> OnceSender<M> {
3227 fn new(sender: oneshot::Sender<M>, port_id: PortAddr) -> Self {
3230 Self {
3231 sender: Arc::new(Mutex::new(Some(sender))),
3232 port_id,
3233 }
3234 }
3235
3236 fn send_once(&self, message: M) -> Result<SerializedSendDisposition, MailboxSenderError> {
3237 match self.sender.lock().unwrap().take() {
3239 None => Err(MailboxSenderError::new_bound(
3240 self.port_id.clone(),
3241 MailboxSenderErrorKind::Closed,
3242 )),
3243 Some(sender) => {
3244 sender.send(message).map_err(|_| {
3245 MailboxSenderError::new_bound(
3250 self.port_id.clone(),
3251 MailboxSenderErrorKind::Closed,
3252 )
3253 })?;
3254 Ok(SerializedSendDisposition::DeliveredAndExhausted)
3255 }
3256 }
3257 }
3258}
3259
3260impl<M: Message> Clone for OnceSender<M> {
3264 fn clone(&self) -> Self {
3265 Self {
3266 sender: self.sender.clone(),
3267 port_id: self.port_id.clone(),
3268 }
3269 }
3270}
3271
3272impl<M: RemoteMessage> SerializedSender for OnceSender<M> {
3273 fn as_any(&self) -> &dyn Any {
3274 self
3275 }
3276
3277 fn send_serialized(
3278 &self,
3279 headers: Flattrs,
3280 serialized: wirevalue::Any,
3281 ) -> Result<SerializedSendDisposition, SerializedSendFailure> {
3282 match serialized.deserialized() {
3283 Ok(message) => self
3284 .send_once(message)
3285 .map_err(|_| SerializedSendFailure::Dead {
3286 data: serialized,
3287 headers,
3288 }),
3289 Err(err) => Err(SerializedSendFailure::Error(SerializedSendError {
3290 data: serialized,
3291 error: MailboxSenderError::new_bound(
3292 self.port_id.clone(),
3293 MailboxSenderErrorKind::Deserialize(M::typename(), err.into()),
3294 ),
3295 headers,
3296 })),
3297 }
3298 }
3299}
3300
3301pub(crate) struct UntypedUnboundedSender {
3303 pub(crate) sender: Box<
3304 dyn Fn(Flattrs, wirevalue::Any) -> Result<SerializedSendDisposition, SerializedSendFailure>
3305 + Send
3306 + Sync,
3307 >,
3308}
3309
3310impl SerializedSender for UntypedUnboundedSender {
3311 fn as_any(&self) -> &dyn Any {
3312 self
3313 }
3314
3315 fn send_serialized(
3316 &self,
3317 headers: Flattrs,
3318 serialized: wirevalue::Any,
3319 ) -> Result<SerializedSendDisposition, SerializedSendFailure> {
3320 (self.sender)(headers, serialized)
3321 }
3322}
3323
3324struct State {
3326 actor_id: ActorAddr,
3328
3329 ports: DashMap<Port, Arc<dyn SerializedSender>>,
3333
3334 next_ephemeral_port: AtomicU64,
3336
3337 closed: RwLock<Option<ActorStatus>>,
3340
3341 handler_ingress: Arc<HandlerIngressGate>,
3343}
3344
3345impl State {
3346 fn new(actor_id: ActorAddr) -> Self {
3348 Self {
3349 actor_id,
3350 ports: DashMap::new(),
3351 next_ephemeral_port: AtomicU64::new(0),
3352 closed: RwLock::new(None),
3353 handler_ingress: Arc::new(HandlerIngressGate::new()),
3354 }
3355 }
3356
3357 fn allocate_port(&self) -> u64 {
3359 self.next_ephemeral_port.fetch_add(1, Ordering::SeqCst)
3360 }
3361}
3362
3363impl fmt::Debug for State {
3364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
3365 f.debug_struct("State")
3366 .field("actor_id", &self.actor_id)
3367 .field(
3368 "open_ports",
3369 &self
3370 .ports
3371 .iter()
3372 .map(|e| e.key().clone())
3373 .collect::<Vec<_>>(),
3374 )
3375 .field("next_ephemeral_port", &self.next_ephemeral_port)
3376 .finish()
3377 }
3378}
3379
3380#[derive(Clone)]
3384pub struct MailboxMuxer {
3385 mailboxes: Arc<DashMap<ActorId, Box<dyn MailboxSender + Send + Sync>>>,
3386 status_sender: Arc<OnceLock<Box<dyn MailboxSender + Send + Sync>>>,
3387}
3388
3389impl Default for MailboxMuxer {
3390 fn default() -> Self {
3391 Self::new()
3392 }
3393}
3394
3395impl MailboxMuxer {
3396 pub fn new() -> Self {
3398 Self {
3399 mailboxes: Arc::new(DashMap::new()),
3400 status_sender: Arc::new(OnceLock::new()),
3401 }
3402 }
3403
3404 pub fn bind(&self, actor_id: ActorId, sender: impl MailboxSender + 'static) -> bool {
3409 match self.mailboxes.entry(actor_id) {
3410 Entry::Occupied(_) => false,
3411 Entry::Vacant(entry) => {
3412 entry.insert(Box::new(sender));
3413 true
3414 }
3415 }
3416 }
3417
3418 pub fn bind_mailbox(&self, mailbox: Mailbox) -> bool {
3420 self.bind(mailbox.actor_addr().id().clone(), mailbox)
3421 }
3422
3423 pub fn bind_status(&self, sender: impl MailboxSender + 'static) -> bool {
3426 self.status_sender.set(Box::new(sender)).is_ok()
3427 }
3428
3429 #[allow(dead_code)]
3433 pub(crate) fn unbind(&self, actor_id: &ActorId) {
3434 self.mailboxes.remove(actor_id);
3435 }
3436}
3437
3438#[async_trait]
3439impl MailboxSender for MailboxMuxer {
3440 fn post_unchecked(
3441 &self,
3442 envelope: MessageEnvelope,
3443 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
3444 ) {
3445 if envelope.dest().is_control_port_kind(ControlPort::Status)
3446 && let Some(sender) = self.status_sender.get()
3447 {
3448 sender.post(envelope, return_handle);
3449 return;
3450 }
3451
3452 let dest_actor_ref = envelope.dest().actor_addr();
3453 match self.mailboxes.get(dest_actor_ref.id()) {
3454 None => {
3455 let failure = DeliveryFailure::new(InvalidReference::new(
3456 dest_actor_ref,
3457 InvalidReferenceReason::ActorNotExist,
3458 ));
3459 envelope.undeliverable(failure, return_handle)
3460 }
3461 Some(sender) => sender.post(envelope, return_handle),
3462 }
3463 }
3464
3465 async fn flush(&self) -> Result<(), anyhow::Error> {
3466 let keys: Vec<_> = self
3467 .mailboxes
3468 .iter()
3469 .map(|entry| entry.key().clone())
3470 .collect();
3471 for key in keys {
3472 if let Some(sender) = self.mailboxes.get(&key) {
3473 sender.value().flush().await?;
3474 }
3475 }
3476 Ok(())
3477 }
3478}
3479
3480#[derive(Clone)]
3483pub struct MailboxRouter {
3484 entries: Arc<RwLock<BTreeMap<Addr, Arc<dyn MailboxSender + Send + Sync>>>>,
3485}
3486
3487impl Default for MailboxRouter {
3488 fn default() -> Self {
3489 Self::new()
3490 }
3491}
3492
3493impl MailboxRouter {
3494 pub fn new() -> Self {
3496 Self {
3497 entries: Arc::new(RwLock::new(BTreeMap::new())),
3498 }
3499 }
3500
3501 pub fn downgrade(&self) -> WeakMailboxRouter {
3503 WeakMailboxRouter(Arc::downgrade(&self.entries))
3504 }
3505
3506 pub fn fallback(&self, default: BoxedMailboxSender) -> BoxedMailboxSender {
3510 FallbackMailboxRouter {
3511 router: self.clone(),
3512 default,
3513 }
3514 .into_boxed()
3515 }
3516
3517 pub fn bind(&self, dest: impl Into<Addr>, sender: impl MailboxSender + 'static) {
3521 let dest = dest.into();
3522 let mut w = self.entries.write().unwrap();
3523 w.insert(dest, Arc::new(sender));
3524 }
3525
3526 pub fn unbind(&self, dest: &Addr) {
3530 let mut w = self.entries.write().unwrap();
3531 w.remove(dest);
3532 }
3533
3534 fn sender(&self, actor_ref: &ActorAddr) -> Option<Arc<dyn MailboxSender + Send + Sync>> {
3535 let reference = Addr::from(actor_ref.clone());
3536 match self
3537 .entries
3538 .read()
3539 .unwrap()
3540 .lower_bound(Excluded(&reference))
3541 .prev()
3542 {
3543 None => None,
3544 Some((key, sender)) if key.is_prefix_of(&reference) => Some(sender.clone()),
3545 Some(_) => None,
3546 }
3547 }
3548}
3549
3550#[async_trait]
3551impl MailboxSender for MailboxRouter {
3552 fn post_unchecked(
3553 &self,
3554 envelope: MessageEnvelope,
3555 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
3556 ) {
3557 let dest_actor_ref = envelope.dest().actor_addr();
3558 match self.sender(&dest_actor_ref) {
3559 None => {
3560 let target = envelope.dest().clone();
3561 let failure = DeliveryFailure::new(UndeliverableReason::Transport(
3562 TransportFailure::new(target, TransportFailureReason::NoRoute),
3563 ));
3564 envelope.undeliverable(failure, return_handle)
3565 }
3566 Some(sender) => sender.post(envelope, return_handle),
3567 }
3568 }
3569
3570 async fn flush(&self) -> Result<(), anyhow::Error> {
3571 let senders: Vec<_> = self.entries.read().unwrap().values().cloned().collect();
3572 let futs: Vec<_> = senders.iter().map(|s| s.flush()).collect();
3573 futures::future::try_join_all(futs).await?;
3574 Ok(())
3575 }
3576}
3577
3578#[derive(Clone)]
3581pub struct FallbackMailboxRouter {
3582 router: MailboxRouter,
3583 default: BoxedMailboxSender,
3584}
3585
3586impl FallbackMailboxRouter {
3587 pub fn default_sender(&self) -> &BoxedMailboxSender {
3589 &self.default
3590 }
3591}
3592
3593#[async_trait]
3594impl MailboxSender for FallbackMailboxRouter {
3595 fn post_unchecked(
3596 &self,
3597 envelope: MessageEnvelope,
3598 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
3599 ) {
3600 let dest_actor_ref = envelope.dest().actor_addr();
3601 match self.router.sender(&dest_actor_ref) {
3602 Some(sender) => sender.post(envelope, return_handle),
3603 None => self.default.post(envelope, return_handle),
3604 }
3605 }
3606
3607 async fn flush(&self) -> Result<(), anyhow::Error> {
3608 let (r1, r2) = futures::future::join(self.router.flush(), self.default.flush()).await;
3609 r1?;
3610 r2?;
3611 Ok(())
3612 }
3613}
3614
3615#[derive(Debug, Clone)]
3624pub struct WeakMailboxRouter(Weak<RwLock<BTreeMap<Addr, Arc<dyn MailboxSender + Send + Sync>>>>);
3625
3626impl WeakMailboxRouter {
3627 pub fn upgrade(&self) -> Option<MailboxRouter> {
3629 self.0.upgrade().map(|entries| MailboxRouter { entries })
3630 }
3631}
3632
3633#[async_trait]
3634impl MailboxSender for WeakMailboxRouter {
3635 fn post_unchecked(
3636 &self,
3637 envelope: MessageEnvelope,
3638 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
3639 ) {
3640 match self.upgrade() {
3641 Some(router) => router.post(envelope, return_handle),
3642 None => {
3643 let target = envelope.dest().clone();
3644 let failure =
3645 DeliveryFailure::new(UndeliverableReason::Transport(TransportFailure::new(
3646 target,
3647 TransportFailureReason::LinkUnavailable(
3648 "mailbox router is gone".to_string(),
3649 ),
3650 )));
3651 envelope.undeliverable(failure, return_handle)
3652 }
3653 }
3654 }
3655
3656 async fn flush(&self) -> Result<(), anyhow::Error> {
3657 match self.upgrade() {
3658 Some(router) => router.flush().await,
3659 None => Ok(()),
3660 }
3661 }
3662}
3663
3664fn is_stale_session_close(status: &TxStatus) -> bool {
3674 matches!(status, TxStatus::Closed(CloseReason::SequenceMismatch(_)))
3675}
3676
3677#[derive(Clone)]
3691pub struct DialMailboxRouter {
3692 address_book: Arc<RwLock<BTreeMap<Addr, ChannelAddr>>>,
3693 sender_cache: Arc<DashMap<ChannelAddr, Arc<MailboxClient>>>,
3694
3695 default: BoxedMailboxSender,
3698
3699 direct_addressed_remote_only: bool,
3702}
3703
3704impl Default for DialMailboxRouter {
3705 fn default() -> Self {
3706 Self::new()
3707 }
3708}
3709
3710impl DialMailboxRouter {
3711 pub fn new() -> Self {
3713 Self::new_with_default(BoxedMailboxSender::new(UnroutableMailboxSender))
3714 }
3715
3716 pub fn new_with_default(default: BoxedMailboxSender) -> Self {
3721 Self {
3722 address_book: Arc::new(RwLock::new(BTreeMap::new())),
3723 sender_cache: Arc::new(DashMap::new()),
3724 default,
3725 direct_addressed_remote_only: false,
3726 }
3727 }
3728
3729 pub fn new_with_default_direct_addressed_remote_only(default: BoxedMailboxSender) -> Self {
3734 Self {
3735 address_book: Arc::new(RwLock::new(BTreeMap::new())),
3736 sender_cache: Arc::new(DashMap::new()),
3737 default,
3738 direct_addressed_remote_only: true,
3739 }
3740 }
3741
3742 pub fn bind(&self, dest: impl Into<Addr>, addr: ChannelAddr) {
3748 let dest = dest.into();
3749 let addr = addr.into_dial_addr();
3750 if let Ok(mut w) = self.address_book.write() {
3751 if let Some(old_addr) = w.insert(dest.clone(), addr.clone())
3752 && old_addr != addr
3753 {
3754 tracing::info!("rebinding {:?} from {:?} to {:?}", dest, old_addr, addr);
3755 self.sender_cache.remove(&old_addr);
3756 }
3757 } else {
3758 tracing::error!("address book poisoned during bind of {:?}", dest);
3759 }
3760 }
3761
3762 pub fn unbind(&self, dest: &Addr) {
3768 if let Ok(mut w) = self.address_book.write() {
3769 let to_remove: Vec<(Addr, ChannelAddr)> = w
3770 .range(dest..)
3771 .take_while(|(key, _)| dest.is_prefix_of(key))
3772 .map(|(key, addr)| (key.clone(), addr.clone()))
3773 .collect();
3774
3775 for (key, addr) in to_remove {
3776 tracing::info!("unbinding {:?} from {:?}", key, addr);
3777 w.remove(&key);
3778 self.sender_cache.remove(&addr);
3779 }
3780 } else {
3781 tracing::error!("address book poisoned during unbind of {:?}", dest);
3782 }
3783 }
3784
3785 pub fn lookup_addr(&self, actor_ref: &ActorAddr) -> Option<ChannelAddr> {
3787 let address_book = self.address_book.read().unwrap();
3788 let reference = Addr::from(actor_ref.clone());
3789 let found = address_book.lower_bound(Excluded(&reference)).prev();
3790
3791 if let Some((key, addr)) = found
3794 && key.is_prefix_of(&reference)
3795 {
3796 Some(addr.clone().into_dial_addr())
3797 } else {
3798 let addr = actor_ref.addr().clone().into_dial_addr();
3799 if self.direct_addressed_remote_only {
3800 addr.transport().is_remote().then_some(addr)
3801 } else {
3802 Some(addr)
3803 }
3804 }
3805 }
3806
3807 pub fn prefixes(&self) -> BTreeSet<Addr> {
3810 let addrs = self.address_book.read().unwrap();
3811 let mut prefixes: BTreeSet<Addr> = BTreeSet::new();
3812 for (reference, _) in addrs.iter() {
3813 match prefixes.lower_bound(Excluded(reference)).peek_prev() {
3814 Some(candidate) if candidate.is_prefix_of(reference) => (),
3815 _ => {
3816 prefixes.insert(reference.clone());
3817 }
3818 }
3819 }
3820
3821 prefixes
3822 }
3823
3824 fn dial(
3825 &self,
3826 addr: &ChannelAddr,
3827 actor_ref: &ActorAddr,
3828 ) -> Result<Arc<MailboxClient>, MailboxSenderError> {
3829 loop {
3843 match self.sender_cache.entry(addr.clone()) {
3844 Entry::Occupied(entry) => {
3845 let status = entry.get().tx_status().borrow().clone();
3846 if is_stale_session_close(&status) {
3847 tracing::info!(
3848 ?addr,
3849 reason = ?status.as_closed(),
3850 "evicting stale-session MailboxClient from DialMailboxRouter cache"
3851 );
3852 entry.remove();
3853 continue;
3854 }
3855 return Ok(entry.get().clone());
3856 }
3857 Entry::Vacant(entry) => {
3858 let tx = channel::dial(addr.clone()).map_err(|err| {
3859 MailboxSenderError::new_unbound_type(
3860 actor_ref.clone(),
3861 MailboxSenderErrorKind::Channel(err),
3862 "unknown",
3863 )
3864 })?;
3865 let sender = Arc::new(MailboxClient::new(tx));
3866 return Ok(entry.insert(sender).value().clone());
3867 }
3868 }
3869 }
3870 }
3871}
3872
3873#[async_trait]
3874impl MailboxSender for DialMailboxRouter {
3875 fn post_unchecked(
3876 &self,
3877 envelope: MessageEnvelope,
3878 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
3879 ) {
3880 let dest_actor_ref = envelope.dest().actor_addr();
3881 let Some(addr) = self.lookup_addr(&dest_actor_ref) else {
3882 self.default.post(envelope, return_handle);
3883 return;
3884 };
3885
3886 match self.dial(&addr, &dest_actor_ref) {
3887 Err(err) => {
3888 let target = envelope.dest().clone();
3889 let failure =
3890 DeliveryFailure::new(UndeliverableReason::Transport(TransportFailure::new(
3891 target,
3892 TransportFailureReason::DialFailed {
3893 addr,
3894 error: err.to_string(),
3895 },
3896 )));
3897 envelope.undeliverable(failure, return_handle)
3898 }
3899 Ok(sender) => sender.post(envelope, return_handle),
3900 }
3901 }
3902
3903 async fn flush(&self) -> Result<(), anyhow::Error> {
3904 let senders: Vec<_> = self
3905 .sender_cache
3906 .iter()
3907 .map(|entry| entry.value().clone())
3908 .collect();
3909 let mut futs: Vec<_> = senders.iter().map(|s| s.flush()).collect();
3910 futs.push(self.default.flush());
3911 futures::future::try_join_all(futs).await?;
3912 Ok(())
3913 }
3914}
3915
3916#[derive(Debug)]
3919pub struct UnroutableMailboxSender;
3920
3921#[async_trait]
3922impl MailboxSender for UnroutableMailboxSender {
3923 fn post_unchecked(
3924 &self,
3925 envelope: MessageEnvelope,
3926 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
3927 ) {
3928 let target = envelope.dest().clone();
3929 let failure = DeliveryFailure::new(UndeliverableReason::Transport(TransportFailure::new(
3930 target,
3931 TransportFailureReason::NoRoute,
3932 )));
3933 envelope.undeliverable(failure, return_handle);
3934 }
3935}
3936
3937#[cfg(test)]
3938mod tests {
3939
3940 use std::assert_matches;
3941 use std::mem::drop;
3942 use std::sync::atomic::AtomicUsize;
3943 use std::time::Duration;
3944
3945 use async_trait::async_trait;
3946 use timed_test::async_timed_test;
3947
3948 use super::*;
3949 use crate as hyperactor;
3950 use crate::Actor;
3951 use crate::ActorRef;
3952 use crate::Handler;
3953 use crate::accum;
3954 use crate::accum::ReducerMode;
3955 use crate::channel::ChannelTransport;
3956 use crate::context::Actor as _;
3957 use crate::context::Mailbox as MailboxContext;
3958 use crate::context::MailboxExt as _;
3959 use crate::endpoint::Endpoint as _;
3960 use crate::proc::Proc;
3961 use crate::testing::ids::test_actor_id;
3962 use crate::testing::ids::test_port_id;
3963 use crate::testing::ids::test_proc_id;
3964
3965 fn test_proc_ref(name: &str) -> Addr {
3966 Addr::Proc(test_proc_id(name))
3967 }
3968
3969 fn test_actor_ref(proc_name: &str, actor_name: &str) -> Addr {
3970 Addr::Actor(test_actor_id(proc_name, actor_name))
3971 }
3972
3973 fn root_transport_failure(envelope: &MessageEnvelope) -> &TransportFailure {
3974 let root_failure = envelope
3975 .root_delivery_failure()
3976 .expect("expected root delivery failure");
3977 let DeliveryFailureKind::Undeliverable(UndeliverableReason::Transport(transport)) =
3978 &root_failure.kind
3979 else {
3980 panic!("expected transport failure, got {root_failure}");
3981 };
3982 transport
3983 }
3984
3985 fn root_invalid_reference(envelope: &MessageEnvelope) -> &InvalidReference {
3986 let root_failure = envelope
3987 .root_delivery_failure()
3988 .expect("expected root delivery failure");
3989 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
3990 panic!("expected invalid reference, got {root_failure}");
3991 };
3992 invalid_reference
3993 }
3994
3995 struct ClosedChannelTx {
3996 addr: ChannelAddr,
3997 status: watch::Receiver<TxStatus>,
3998 }
3999
4000 impl ClosedChannelTx {
4001 fn new(addr: ChannelAddr) -> Self {
4002 let (_sender, status) = watch::channel(TxStatus::Closed(CloseReason::Other(
4003 "test channel closed".into(),
4004 )));
4005 Self { addr, status }
4006 }
4007 }
4008
4009 #[async_trait]
4010 impl channel::Tx<MessageEnvelope> for ClosedChannelTx {
4011 fn do_post(&self, message: MessageEnvelope, completion: CompletionSink<MessageEnvelope>) {
4012 completion.reject(SendError {
4013 error: ChannelError::Closed,
4014 message,
4015 reason: None,
4016 });
4017 }
4018
4019 fn addr(&self) -> ChannelAddr {
4020 self.addr.clone()
4021 }
4022
4023 fn status(&self) -> &watch::Receiver<TxStatus> {
4024 &self.status
4025 }
4026 }
4027
4028 #[test]
4029 fn test_error() {
4030 use crate::testing::ids::test_actor_id;
4031 let err = MailboxError::new(
4032 test_actor_id("myworld_2", "myactor"),
4033 MailboxErrorKind::Closed,
4034 );
4035 let err_str = format!("{err}");
4037 assert!(
4038 err_str.contains("mailbox closed"),
4039 "expected error: {}",
4040 err_str
4041 );
4042 assert!(
4043 err_str.contains("@"),
4044 "expected ref-style location separator in {err_str}"
4045 );
4046 }
4047
4048 #[test]
4049 fn test_error_msg_renders_structured_delivery_failure() {
4050 let sender = test_actor_id("0", "sender");
4051 let dest = test_port_id("0", "dest", 42);
4052 let mut envelope = MessageEnvelope::serialize(sender, dest.clone(), &42u64, Flattrs::new())
4053 .expect("serialize");
4054 envelope.push_delivery_failure(DeliveryFailure::new(InvalidReference::new(
4055 dest,
4056 InvalidReferenceReason::PortNeverAllocated,
4057 )));
4058
4059 let error = envelope.error_msg().expect("expected error");
4060 assert!(error.contains("delivery failure: invalid reference"));
4061 assert!(error.contains("port never allocated"));
4062 }
4063
4064 #[test]
4065 fn test_delivery_failure_rendering_bounds_attrs() {
4066 use hyperactor_config::attrs::declare_attrs;
4067
4068 declare_attrs! {
4069 attr TEST_DELIVERY_FAILURE_ATTR: String;
4070 }
4071
4072 let target = test_port_id("0", "dest", 42);
4073 let mut attrs = Flattrs::new();
4074 let large_value = "x".repeat(MAX_RENDERED_DELIVERY_FAILURE_ATTRS_LEN * 2);
4075 attrs.set(TEST_DELIVERY_FAILURE_ATTR, large_value.clone());
4076 let failure = DeliveryFailure::with_attrs(
4077 InvalidReference::new(target, InvalidReferenceReason::PortNeverAllocated),
4078 attrs,
4079 );
4080 let rendered = failure.render_bounded();
4081
4082 assert!(rendered.contains("port never allocated"));
4083 assert!(rendered.contains("..."));
4084 assert!(
4085 !rendered.contains(&large_value),
4086 "rendering must not include unbounded attr values"
4087 );
4088 }
4089
4090 #[tokio::test]
4091 async fn test_mailbox_basic() {
4092 let mbox = Mailbox::new(test_actor_id("0", "test"));
4093 let (port, mut receiver) = mbox.open_port::<u64>();
4094 let port = port.bind();
4095
4096 mbox.serialize_and_send(&port, 123, monitored_return_handle())
4097 .unwrap();
4098 mbox.serialize_and_send(&port, 321, monitored_return_handle())
4099 .unwrap();
4100 assert_eq!(receiver.recv().await.unwrap(), 123u64);
4101 assert_eq!(receiver.recv().await.unwrap(), 321u64);
4102
4103 let serialized = wirevalue::Any::serialize(&999u64).unwrap();
4104 mbox.post(
4105 MessageEnvelope::new_unknown(port.port_addr().clone(), serialized),
4106 monitored_return_handle(),
4107 );
4108 assert_eq!(receiver.recv().await.unwrap(), 999u64);
4109 }
4110
4111 #[tokio::test]
4112 async fn test_mailbox_rejects_messages_for_other_actors() {
4113 let mbox = Mailbox::new(test_actor_id("0", "owner"));
4114 let dest = test_actor_id("0", "other").port_addr(Port::from(1234));
4115 let envelope =
4116 MessageEnvelope::serialize(mbox.actor_addr().clone(), dest, &42u64, Flattrs::new())
4117 .expect("serialize");
4118 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
4119
4120 mbox.post(envelope, return_handle);
4121
4122 let Undeliverable::Returned(undelivered) =
4123 tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
4124 .await
4125 .expect("timed out waiting for undeliverable")
4126 .expect("return port closed")
4127 else {
4128 panic!("expected returned message");
4129 };
4130 assert!(
4131 undelivered
4132 .error_msg()
4133 .expect("expected error")
4134 .contains("wrong mailbox owner")
4135 );
4136 let root_failure = undelivered
4137 .root_delivery_failure()
4138 .expect("expected root delivery failure");
4139 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
4140 panic!("expected invalid reference, got {root_failure}");
4141 };
4142 assert_eq!(
4143 invalid_reference.reason,
4144 InvalidReferenceReason::WrongMailboxOwner
4145 );
4146 }
4147
4148 #[tokio::test]
4149 async fn test_ephemeral_port_orders_raw_and_serialized_sends() {
4150 let proc = Proc::isolated();
4151 let client = proc.client("client");
4152 let (port_handle, mut receiver) = client.open_port::<u64>();
4153 let port = port_handle.bind();
4154 let session_id = client.instance().sequencer().session_id();
4155
4156 let mut headers = Flattrs::new();
4157 headers.set(SEQ_INFO, SeqInfo::Session { session_id, seq: 2 });
4158 let envelope = MessageEnvelope::new(
4159 client.mailbox().actor_addr().clone(),
4160 port.port_addr().clone(),
4161 wirevalue::Any::serialize(&2u64).unwrap(),
4162 headers,
4163 );
4164 client.mailbox().post(envelope, monitored_return_handle());
4165
4166 port_handle.try_post(&client, 1u64).unwrap();
4167
4168 assert_eq!(receiver.recv().await.unwrap(), 1);
4169 assert_eq!(receiver.recv().await.unwrap(), 2);
4170 }
4171
4172 #[tokio::test]
4173 async fn test_ttl_expiration_records_root_delivery_failure() {
4174 let mbox = Mailbox::new(test_actor_id("0", "test"));
4175 let (port, _) = mbox.open_port::<u64>();
4176 let port_ref = port.bind();
4177 let envelope = MessageEnvelope::serialize(
4178 mbox.actor_addr().clone(),
4179 port_ref.port_addr().clone(),
4180 &42u64,
4181 Flattrs::new(),
4182 )
4183 .expect("serialize")
4184 .set_ttl(0);
4185 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
4186
4187 mbox.post(envelope, return_handle);
4188
4189 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
4190 .await
4191 .expect("timed out waiting for undeliverable")
4192 .expect("return port closed")
4193 .into_message()
4194 .expect("expected returned envelope");
4195 let root_failure = undelivered
4196 .root_delivery_failure()
4197 .expect("expected root delivery failure");
4198 assert!(
4199 matches!(root_failure.kind, DeliveryFailureKind::Expired(_)),
4200 "expected expired delivery failure, got {root_failure}"
4201 );
4202 }
4203
4204 #[tokio::test]
4205 async fn test_missing_handler_port_records_invalid_reference() {
4206 let mbox = Mailbox::new(test_actor_id("0", "test"));
4207 let dest = mbox.actor_addr().port_addr(Port::handler::<TestMessage>());
4208 let envelope = MessageEnvelope::serialize(
4209 mbox.actor_addr().clone(),
4210 dest,
4211 &TestMessage,
4212 Flattrs::new(),
4213 )
4214 .expect("serialize");
4215 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
4216
4217 mbox.post(envelope, return_handle);
4218
4219 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
4220 .await
4221 .expect("timed out waiting for undeliverable")
4222 .expect("return port closed")
4223 .into_message()
4224 .expect("expected returned envelope");
4225 let root_failure = undelivered
4226 .root_delivery_failure()
4227 .expect("expected root delivery failure");
4228 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
4229 panic!("expected invalid reference, got {root_failure}");
4230 };
4231 assert_eq!(
4232 invalid_reference.reason,
4233 InvalidReferenceReason::HandlerNotBound
4234 );
4235 }
4236
4237 #[tokio::test]
4238 async fn test_missing_dropped_port_records_recipient_gone() {
4239 let mbox = Mailbox::new(test_actor_id("0", "test"));
4240 let (port, receiver) = mbox.open_port::<u64>();
4241 let port_ref = port.bind();
4242 drop(receiver);
4243 let envelope = MessageEnvelope::serialize(
4244 mbox.actor_addr().clone(),
4245 port_ref.port_addr().clone(),
4246 &42u64,
4247 Flattrs::new(),
4248 )
4249 .expect("serialize");
4250 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
4251
4252 mbox.post(envelope, return_handle);
4253
4254 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
4255 .await
4256 .expect("timed out waiting for undeliverable")
4257 .expect("return port closed")
4258 .into_message()
4259 .expect("expected returned envelope");
4260 let root_failure = undelivered
4261 .root_delivery_failure()
4262 .expect("expected root delivery failure");
4263 let DeliveryFailureKind::Undeliverable(UndeliverableReason::PortGone(port_gone)) =
4264 &root_failure.kind
4265 else {
4266 panic!("expected port gone, got {root_failure}");
4267 };
4268 assert_eq!(port_gone.port, *port_ref.port_addr());
4269 }
4270
4271 #[tokio::test]
4272 async fn test_missing_never_allocated_port_records_invalid_reference() {
4273 let mbox = Mailbox::new(test_actor_id("0", "test"));
4274 let dest = mbox.actor_addr().port_addr(Port::from(0));
4275 let envelope =
4276 MessageEnvelope::serialize(mbox.actor_addr().clone(), dest, &42u64, Flattrs::new())
4277 .expect("serialize");
4278 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
4279
4280 mbox.post(envelope, return_handle);
4281
4282 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
4283 .await
4284 .expect("timed out waiting for undeliverable")
4285 .expect("return port closed")
4286 .into_message()
4287 .expect("expected returned envelope");
4288 let root_failure = undelivered
4289 .root_delivery_failure()
4290 .expect("expected root delivery failure");
4291 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
4292 panic!("expected invalid reference, got {root_failure}");
4293 };
4294 assert_eq!(
4295 invalid_reference.reason,
4296 InvalidReferenceReason::PortNeverAllocated
4297 );
4298 }
4299
4300 #[tokio::test]
4301 async fn test_mailbox_accum() {
4302 let proc = Proc::isolated();
4303 let client = proc.client("client");
4304 let (port, mut receiver) = client
4305 .mailbox()
4306 .open_accum_port(accum::join_semilattice::<accum::Max<i64>>());
4307
4308 for i in -3..4 {
4309 port.post(&client, accum::Max(i));
4310 let received: accum::Max<i64> = receiver.recv().await.unwrap();
4311 let msg = received.get();
4312 assert_eq!(msg, &i);
4313 }
4314 for i in -3..4 {
4316 port.post(&client, accum::Max(i));
4317 assert_eq!(receiver.recv().await.unwrap().get(), &3);
4318 }
4319 port.post(&client, accum::Max(4));
4321 assert_eq!(receiver.recv().await.unwrap().get(), &4);
4322
4323 for i in 5..10 {
4325 port.post(&client, accum::Max(i));
4326 }
4327 assert_eq!(receiver.recv().await.unwrap().get(), &9);
4328 port.post(&client, accum::Max(1));
4329 port.post(&client, accum::Max(3));
4330 port.post(&client, accum::Max(2));
4331 assert_eq!(receiver.recv().await.unwrap().get(), &9);
4332 }
4333
4334 #[test]
4335 fn test_port_and_reducer() {
4336 let mbox = Mailbox::new(test_actor_id("0", "test"));
4337 {
4339 let accumulator = accum::join_semilattice::<accum::Max<u64>>();
4340 let reducer_spec = accumulator.reducer_spec().unwrap();
4341 let (port, _) = mbox.open_accum_port(accum::join_semilattice::<accum::Max<u64>>());
4342 assert_eq!(port.inner.reducer_spec, Some(reducer_spec.clone()));
4343 let port_ref = port.bind();
4344 assert_eq!(port_ref.reducer_spec(), &Some(reducer_spec));
4345 }
4346 {
4348 let (port, _) = mbox.open_port::<u64>();
4349 assert_eq!(port.inner.reducer_spec, None);
4350 let port_ref = port.bind();
4351 assert_eq!(port_ref.reducer_spec(), &None);
4352 }
4353 }
4354
4355 #[tokio::test]
4356 async fn test_mailbox_once() {
4357 let proc = Proc::isolated();
4358 let client = proc.client("client");
4359
4360 let (port, receiver) = client.open_once_port::<u64>();
4361
4362 port.post(&client, 123u64);
4365 assert_eq!(receiver.recv().await.unwrap(), 123u64);
4366
4367 }
4378
4379 #[cfg(any())]
4380 #[tokio::test]
4381 async fn test_mailbox_receiver_drop() {
4382 let mbox = Mailbox::new(test_actor_id("0", "test"));
4383 let (port, mut receiver) = mbox.open_port::<u64>();
4384 let port = port.bind();
4386 mbox.serialize_and_send(&port, 123u64, monitored_return_handle())
4387 .unwrap();
4388 assert_eq!(receiver.recv().await.unwrap(), 123u64);
4389 drop(receiver);
4390 let Err(err) = mbox.serialize_and_send(&port, 123u64, monitored_return_handle()) else {
4391 panic!();
4392 };
4393
4394 assert_matches!(err.kind(), MailboxSenderErrorKind::Closed);
4395 assert_matches!(err.location(), PortLocation::Bound(bound) if *bound == *port.port_addr());
4396 }
4397
4398 #[tokio::test]
4399 async fn test_mailbox_type_mismatch_does_not_evict_unbounded_port() {
4400 let mbox = Mailbox::new(test_actor_id("0", "test"));
4401 let (port, mut receiver) = mbox.open_port::<u64>();
4402 let port = port.bind();
4403 let port_index = port.port_addr().index();
4404 let target: Addr = port.port_addr().clone().into();
4405 let (return_handle, mut return_receiver) =
4406 crate::mailbox::undeliverable::new_undeliverable_port();
4407
4408 let wrong_message = wirevalue::Any::serialize(&TestMessage).unwrap();
4409 mbox.post(
4410 MessageEnvelope::new_unknown(port.port_addr().clone(), wrong_message),
4411 return_handle.clone(),
4412 );
4413
4414 let envelope = tokio::time::timeout(Duration::from_secs(1), return_receiver.recv())
4415 .await
4416 .expect("undeliverable mismatch should arrive")
4417 .unwrap()
4418 .into_message()
4419 .expect("expected returned envelope");
4420 assert!(
4421 envelope
4422 .error_msg()
4423 .is_some_and(|message| message.contains("protocol mismatch")),
4424 "expected protocol mismatch in {envelope}",
4425 );
4426 let invalid_reference = root_invalid_reference(&envelope);
4427 assert_eq!(invalid_reference.target, target);
4428 assert_eq!(
4429 invalid_reference.reason,
4430 InvalidReferenceReason::ProtocolMismatch
4431 );
4432 assert!(
4433 mbox.inner.ports.contains_key(&Port::from(port_index)),
4434 "deserialization mismatch should not evict reusable port",
4435 );
4436
4437 mbox.serialize_and_send(&port, 123u64, return_handle)
4438 .unwrap();
4439 assert_eq!(
4440 tokio::time::timeout(Duration::from_secs(1), receiver.recv())
4441 .await
4442 .expect("valid message should still be delivered")
4443 .unwrap(),
4444 123u64
4445 );
4446 }
4447
4448 #[tokio::test]
4449 async fn test_mailbox_closed_unbounded_port_is_removed_after_send_failure() {
4450 let mbox = Mailbox::new(test_actor_id("0", "test"));
4451 let port_index = mbox.allocate_port();
4452 let port_id = mbox.actor_addr().port_addr(Port::from(port_index));
4453 let port = crate::PortRef::attest(port_id.clone());
4454 let (return_handle, mut return_receiver) =
4455 crate::mailbox::undeliverable::new_undeliverable_port();
4456 let (sender, receiver) = sequenced_unbounded::<SequencedEnvelope<u64>>();
4457
4458 drop(receiver);
4459
4460 mbox.inner.ports.insert(
4461 Port::from(port_index),
4462 Arc::new(UnboundedSender::new(
4463 UnboundedPortSender::Sequenced(sender),
4464 port_id,
4465 )),
4466 );
4467
4468 mbox.serialize_and_send(&port, 123u64, return_handle.clone())
4469 .unwrap();
4470
4471 let envelope = tokio::time::timeout(Duration::from_secs(1), return_receiver.recv())
4472 .await
4473 .expect("closed port should produce undeliverable")
4474 .unwrap()
4475 .into_message()
4476 .expect("expected returned envelope");
4477 let first_error = envelope.error_msg().expect("expected delivery error");
4478 assert!(
4479 first_error.contains("port gone"),
4480 "expected port-gone error in {envelope}",
4481 );
4482 assert!(
4483 !mbox.inner.ports.contains_key(&Port::from(port_index)),
4484 "dead reusable port should be removed after send failure",
4485 );
4486
4487 mbox.serialize_and_send(&port, 456u64, return_handle)
4488 .unwrap();
4489 let envelope = tokio::time::timeout(Duration::from_secs(1), return_receiver.recv())
4490 .await
4491 .expect("removed port should produce unbound undeliverable")
4492 .unwrap()
4493 .into_message()
4494 .expect("expected returned envelope");
4495 let second_error = envelope.error_msg().expect("expected delivery error");
4496 assert_eq!(
4497 first_error, second_error,
4498 "dead-port undeliverable should match unbound-port undeliverable exactly",
4499 );
4500 }
4501
4502 #[tokio::test]
4503 async fn test_mailbox_once_type_mismatch_preserves_sender_until_delivery() {
4504 let mbox = Mailbox::new(test_actor_id("0", "test"));
4505 let (port, receiver) = mbox.open_once_port::<u64>();
4506 let port = port.bind();
4507 let port_index = port.port_addr().index();
4508 let target: Addr = port.port_addr().clone().into();
4509 let (return_handle, mut return_receiver) =
4510 crate::mailbox::undeliverable::new_undeliverable_port();
4511
4512 let wrong_message = wirevalue::Any::serialize(&TestMessage).unwrap();
4513 mbox.post(
4514 MessageEnvelope::new_unknown(port.port_addr().clone(), wrong_message),
4515 return_handle.clone(),
4516 );
4517
4518 let envelope = tokio::time::timeout(Duration::from_secs(1), return_receiver.recv())
4519 .await
4520 .expect("once-port mismatch should arrive")
4521 .unwrap()
4522 .into_message()
4523 .expect("expected returned envelope");
4524 assert!(
4525 envelope
4526 .error_msg()
4527 .is_some_and(|message| message.contains("protocol mismatch")),
4528 "expected protocol mismatch in {envelope}",
4529 );
4530 let invalid_reference = root_invalid_reference(&envelope);
4531 assert_eq!(invalid_reference.target, target);
4532 assert_eq!(
4533 invalid_reference.reason,
4534 InvalidReferenceReason::ProtocolMismatch
4535 );
4536 assert!(
4537 mbox.inner.ports.contains_key(&Port::from(port_index)),
4538 "once port should survive deserialization mismatch before delivery",
4539 );
4540
4541 mbox.serialize_and_send_once(port, 123u64, return_handle)
4542 .unwrap();
4543 assert_eq!(
4544 tokio::time::timeout(Duration::from_secs(1), receiver.recv())
4545 .await
4546 .expect("valid once message should still be delivered")
4547 .unwrap(),
4548 123u64
4549 );
4550 assert!(
4551 !mbox.inner.ports.contains_key(&Port::from(port_index)),
4552 "successful once send should remove the sender entry",
4553 );
4554 }
4555
4556 #[tokio::test]
4557 async fn test_drain() {
4558 let mbox = Mailbox::new(test_actor_id("0", "test"));
4559
4560 let (port, mut receiver) = mbox.open_port();
4561 let port = port.bind();
4562
4563 for i in 0..10 {
4564 mbox.serialize_and_send(&port, i, monitored_return_handle())
4565 .unwrap();
4566 }
4567
4568 for i in 0..10 {
4569 assert_eq!(receiver.recv().await.unwrap(), i);
4570 }
4571
4572 assert!(receiver.drain().is_empty());
4573 }
4574
4575 #[tokio::test]
4576 async fn test_mailbox_muxer() {
4577 let muxer = MailboxMuxer::new();
4578
4579 let mbox0 = Mailbox::new(test_actor_id("0", "actor1"));
4580 let mbox1 = Mailbox::new(test_actor_id("0", "actor2"));
4581
4582 muxer.bind(mbox0.actor_addr().id().clone(), mbox0.clone());
4583 muxer.bind(mbox1.actor_addr().id().clone(), mbox1.clone());
4584
4585 let (port, receiver) = mbox0.open_once_port::<u64>();
4586
4587 let muxer_sender = muxer.clone();
4588 let proc = Proc::configured(test_proc_id("0"), BoxedMailboxSender::new(muxer));
4589 let client = proc.client("client");
4590
4591 port.post(&client, 123u64);
4592 assert_eq!(receiver.recv().await.unwrap(), 123u64);
4593
4594 let missing_actor = test_actor_id("0", "missing_actor");
4595 let missing_dest = missing_actor.port_addr(Port::from(1234));
4596 let envelope = MessageEnvelope::serialize(
4597 client.self_addr().clone(),
4598 missing_dest,
4599 &456u64,
4600 Flattrs::new(),
4601 )
4602 .expect("serialize");
4603 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
4604
4605 muxer_sender.post(envelope, return_handle);
4606
4607 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
4608 .await
4609 .expect("timed out waiting for undeliverable")
4610 .expect("return port closed")
4611 .into_message()
4612 .expect("expected returned envelope");
4613 let root_failure = undelivered
4614 .root_delivery_failure()
4615 .expect("expected root delivery failure");
4616 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
4617 panic!("expected invalid reference, got {root_failure}");
4618 };
4619 assert_eq!(invalid_reference.target, Addr::Actor(missing_actor));
4620 assert_eq!(
4621 invalid_reference.reason,
4622 InvalidReferenceReason::ActorNotExist
4623 );
4624
4625 }
4635
4636 #[tokio::test]
4637 async fn test_local_client_server() {
4638 let mbox = Mailbox::new(test_actor_id("0", "actor0"));
4639 let (addr, rx) =
4640 channel::serve(ChannelAddr::any(ChannelTransport::Local)).expect("serve local");
4641 let tx = channel::dial(addr).expect("dial local");
4642 let serve_handle = mbox.clone().serve(rx);
4643 let client = MailboxClient::new(tx);
4644
4645 let (port, receiver) = mbox.open_once_port::<u64>();
4646 let port = port.bind();
4647
4648 client
4649 .serialize_and_send_once(port, 123u64, monitored_return_handle())
4650 .unwrap();
4651 assert_eq!(receiver.recv().await.unwrap(), 123u64);
4652 serve_handle.stop("fromt test");
4653 serve_handle.await.unwrap().unwrap();
4654 }
4655
4656 #[tokio::test]
4657 async fn test_mailbox_client_records_channel_closed_failure() {
4658 let mbox = Mailbox::new(test_actor_id("0", "actor0"));
4659 let client = MailboxClient::new(ClosedChannelTx::new(ChannelAddr::Local(0)));
4660 let addr = client.addr.clone();
4661
4662 let (port, _receiver) = mbox.open_once_port::<u64>();
4663 let port = port.bind();
4664 let target: Addr = port.port_addr().clone().into();
4665 let (return_handle, mut return_receiver) =
4666 crate::mailbox::undeliverable::new_undeliverable_port();
4667
4668 client
4669 .serialize_and_send_once(port, 123u64, return_handle)
4670 .unwrap();
4671
4672 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_receiver.recv())
4673 .await
4674 .expect("timed out waiting for undeliverable")
4675 .expect("return port closed")
4676 .into_message()
4677 .expect("expected returned envelope");
4678 let root_failure = undelivered
4679 .root_delivery_failure()
4680 .expect("expected root delivery failure");
4681 let DeliveryFailureKind::Undeliverable(UndeliverableReason::Transport(transport)) =
4682 &root_failure.kind
4683 else {
4684 panic!("expected transport failure, got {root_failure}");
4685 };
4686 assert_eq!(transport.target, target);
4687 assert_eq!(
4688 transport.reason,
4689 TransportFailureReason::ChannelClosed { addr }
4690 );
4691 }
4692
4693 #[tokio::test]
4694 async fn test_mailbox_router() {
4695 let mbox0 = Mailbox::new(test_actor_id("world0_0", "actor0"));
4696 let mbox1 = Mailbox::new(test_actor_id("world1_0", "actor0"));
4697 let mbox2 = Mailbox::new(test_actor_id("world1_1", "actor0"));
4698 let mbox3 = Mailbox::new(test_actor_id("world1_1", "actor1"));
4699
4700 let comms: Vec<(OncePortRef<u64>, OncePortReceiver<u64>)> =
4701 [&mbox0, &mbox1, &mbox2, &mbox3]
4702 .into_iter()
4703 .map(|mbox| {
4704 let (port, receiver) = mbox.open_once_port::<u64>();
4705 (port.bind(), receiver)
4706 })
4707 .collect();
4708
4709 let router = MailboxRouter::new();
4710
4711 router.bind(test_proc_ref("world0_0"), mbox0);
4712 router.bind(test_proc_ref("world1_0"), mbox1);
4713 router.bind(test_proc_ref("world1_1"), mbox2);
4714 router.bind(test_actor_ref("world1_1", "actor1"), mbox3);
4715
4716 for (i, (port, receiver)) in comms.into_iter().enumerate() {
4717 router
4718 .serialize_and_send_once(port, i as u64, monitored_return_handle())
4719 .unwrap();
4720 assert_eq!(receiver.recv().await.unwrap(), i as u64);
4721 }
4722
4723 let mbox4 = Mailbox::new(test_actor_id("fallback_0", "actor"));
4726
4727 let (return_handle, mut return_receiver) =
4728 crate::mailbox::undeliverable::new_undeliverable_port();
4729 let (port, _receiver) = mbox4.open_once_port();
4730 let port = port.bind();
4731 let target: Addr = port.port_addr().clone().into();
4732 router
4733 .serialize_and_send_once(port, 0, return_handle.clone())
4734 .unwrap();
4735 let undelivered = return_receiver
4736 .recv()
4737 .await
4738 .unwrap()
4739 .into_message()
4740 .expect("expected returned envelope");
4741 let transport = root_transport_failure(&undelivered);
4742 assert_eq!(transport.target, target);
4743 assert_eq!(transport.reason, TransportFailureReason::NoRoute);
4744
4745 let router = router.fallback(mbox4.clone().into_boxed());
4746 let (port, receiver) = mbox4.open_once_port();
4747 router
4748 .serialize_and_send_once(port.bind(), 0, return_handle)
4749 .unwrap();
4750 assert_eq!(receiver.recv().await.unwrap(), 0);
4751 }
4752
4753 #[tokio::test]
4754 async fn test_weak_mailbox_router_records_link_unavailable_failure() {
4755 let router = MailboxRouter::new();
4756 let weak_router = router.downgrade();
4757 drop(router);
4758
4759 let mbox = Mailbox::new(test_actor_id("0", "actor0"));
4760 let (port, _receiver) = mbox.open_once_port::<u64>();
4761 let port = port.bind();
4762 let target: Addr = port.port_addr().clone().into();
4763 let (return_handle, mut return_receiver) =
4764 crate::mailbox::undeliverable::new_undeliverable_port();
4765
4766 weak_router
4767 .serialize_and_send_once(port, 123u64, return_handle)
4768 .unwrap();
4769
4770 let undelivered = return_receiver
4771 .recv()
4772 .await
4773 .unwrap()
4774 .into_message()
4775 .expect("expected returned envelope");
4776 let transport = root_transport_failure(&undelivered);
4777 assert_eq!(transport.target, target);
4778 assert_eq!(
4779 transport.reason,
4780 TransportFailureReason::LinkUnavailable("mailbox router is gone".to_string())
4781 );
4782 }
4783
4784 #[tokio::test]
4785 async fn test_unroutable_mailbox_sender_records_no_route_failure() {
4786 let mbox = Mailbox::new(test_actor_id("0", "actor0"));
4787 let (port, _receiver) = mbox.open_once_port::<u64>();
4788 let port = port.bind();
4789 let target: Addr = port.port_addr().clone().into();
4790 let (return_handle, mut return_receiver) =
4791 crate::mailbox::undeliverable::new_undeliverable_port();
4792
4793 UnroutableMailboxSender
4794 .serialize_and_send_once(port, 123u64, return_handle)
4795 .unwrap();
4796
4797 let undelivered = return_receiver
4798 .recv()
4799 .await
4800 .unwrap()
4801 .into_message()
4802 .expect("expected returned envelope");
4803 let transport = root_transport_failure(&undelivered);
4804 assert_eq!(transport.target, target);
4805 assert_eq!(transport.reason, TransportFailureReason::NoRoute);
4806 }
4807
4808 #[tokio::test]
4809 async fn test_dial_mailbox_router() {
4810 let router = DialMailboxRouter::new();
4811
4812 router.bind(test_proc_ref("world0_0"), "unix!@1".parse().unwrap());
4813 router.bind(test_proc_ref("world1_0"), "unix!@2".parse().unwrap());
4814 router.bind(test_proc_ref("world1_1"), "unix!@3".parse().unwrap());
4815 router.bind(
4816 test_actor_ref("world1_1", "actor1"),
4817 "unix!@4".parse().unwrap(),
4818 );
4819 let direct_actor_ref: ActorAddr =
4823 ProcAddr::singleton("unix:@4".parse().unwrap(), "my_proc").actor_addr("my_actor");
4824 router.bind(
4825 Addr::Actor(direct_actor_ref.clone()),
4826 "unix:@5".parse().unwrap(),
4827 );
4828
4829 router
4831 .lookup_addr(&test_actor_id("world0_0", "actor"))
4832 .unwrap();
4833 router
4834 .lookup_addr(&test_actor_id("world1_0", "actor"))
4835 .unwrap();
4836
4837 let actor_id = direct_actor_ref;
4838 assert_eq!(
4839 router.lookup_addr(&actor_id).unwrap(),
4840 "unix!@5".parse().unwrap(),
4841 );
4842 router.unbind(&actor_id.clone().into());
4843 assert_eq!(
4844 router.lookup_addr(&actor_id).unwrap(),
4845 "unix!@4".parse().unwrap(),
4846 );
4847
4848 let fallback = ChannelAddr::any(ChannelTransport::Local);
4853 router.unbind(&test_proc_ref("world1_0"));
4854 router.unbind(&test_proc_ref("world1_1"));
4855 assert_eq!(
4856 router
4857 .lookup_addr(&test_actor_id("world1_0", "actor1"))
4858 .unwrap(),
4859 fallback,
4860 );
4861 assert_eq!(
4862 router
4863 .lookup_addr(&test_actor_id("world1_1", "actor1"))
4864 .unwrap(),
4865 fallback,
4866 );
4867 router
4868 .lookup_addr(&test_actor_id("world0_0", "actor"))
4869 .unwrap();
4870 router.unbind(&test_proc_ref("world0_0"));
4871 assert_eq!(
4872 router
4873 .lookup_addr(&test_actor_id("world0_0", "actor"))
4874 .unwrap(),
4875 fallback,
4876 );
4877 }
4878
4879 #[test]
4880 fn test_dial_mailbox_router_canonicalizes_alias_addresses() {
4881 let router = DialMailboxRouter::new();
4882 let dial_to = ChannelAddr::from_zmq_url("tcp://127.0.0.1:9000").unwrap();
4883 let alias = ChannelAddr::from_zmq_url("tcp://127.0.0.1:9000@tcp://0.0.0.0:9000").unwrap();
4884
4885 router.bind(test_proc_ref("world_alias"), alias.clone());
4886 assert_eq!(
4887 router
4888 .lookup_addr(&test_actor_id("world_alias", "actor"))
4889 .unwrap(),
4890 dial_to
4891 );
4892
4893 let direct_actor_ref = ProcAddr::singleton(alias, "direct_alias").actor_addr("actor");
4894 assert_eq!(router.lookup_addr(&direct_actor_ref).unwrap(), dial_to);
4895 }
4896
4897 #[tokio::test]
4898 async fn test_dial_mailbox_router_records_dial_failure() {
4899 let router = DialMailboxRouter::new();
4900 let addr = ChannelAddr::Local(9_876_543_210);
4901 let mbox = Mailbox::new(test_actor_id("world0_0", "actor0"));
4902 router.bind(test_proc_ref("world0_0"), addr.clone());
4903
4904 let (port, _receiver) = mbox.open_once_port::<u64>();
4905 let port = port.bind();
4906 let target: Addr = port.port_addr().clone().into();
4907 let (return_handle, mut return_receiver) =
4908 crate::mailbox::undeliverable::new_undeliverable_port();
4909
4910 router
4911 .serialize_and_send_once(port, 123u64, return_handle)
4912 .unwrap();
4913
4914 let undelivered = return_receiver
4915 .recv()
4916 .await
4917 .unwrap()
4918 .into_message()
4919 .expect("expected returned envelope");
4920 let transport = root_transport_failure(&undelivered);
4921 assert_eq!(transport.target, target);
4922 let TransportFailureReason::DialFailed {
4923 addr: failure_addr,
4924 error,
4925 } = &transport.reason
4926 else {
4927 panic!("expected dial failure, got {}", transport.reason);
4928 };
4929 assert_eq!(failure_addr, &addr);
4930 assert!(
4931 error.contains("channel closed"),
4932 "unexpected error: {error}"
4933 );
4934 }
4935
4936 #[cfg(any())]
4937 #[tokio::test]
4938 async fn test_dial_mailbox_router_default() {
4939 let mbox0 = Mailbox::new(test_actor_id("world0_0", "actor0"));
4940 let mbox1 = Mailbox::new(test_actor_id("world1_0", "actor0"));
4941 let mbox2 = Mailbox::new(test_actor_id("world1_1", "actor0"));
4942 let mbox3 = Mailbox::new(test_actor_id("world1_1", "actor1"));
4943
4944 let root = MailboxRouter::new();
4947 let world0_router = DialMailboxRouter::new_with_default(root.boxed());
4948 let world1_router = DialMailboxRouter::new_with_default(root.boxed());
4949
4950 root.bind(test_proc_ref("world0"), world0_router.clone());
4951 root.bind(test_proc_ref("world1"), world1_router.clone());
4952
4953 let mailboxes = [&mbox0, &mbox1, &mbox2, &mbox3];
4954
4955 let mut handles = Vec::new(); for mbox in mailboxes.iter() {
4957 let (addr, rx) = channel::serve(ChannelAddr::any(ChannelTransport::Local)).unwrap();
4958 let handle = (*mbox).clone().serve(rx);
4959 handles.push(handle);
4960
4961 eprintln!("{}: {}", mbox.actor_addr(), addr);
4962 if mbox
4963 .actor_addr()
4964 .proc_addr()
4965 .label()
4966 .is_some_and(|l| l.as_str().starts_with("world0"))
4967 {
4968 world0_router.bind(Addr::from(mbox.actor_addr().clone()), addr);
4969 } else {
4970 world1_router.bind(Addr::from(mbox.actor_addr().clone()), addr);
4971 }
4972 }
4973
4974 for router in [root.boxed(), world0_router.boxed(), world1_router.boxed()] {
4976 for mbox in mailboxes.iter() {
4977 let (port, receiver) = mbox.open_once_port::<u64>();
4978 let port = port.bind();
4979 router
4980 .serialize_and_send_once(port, 123u64, monitored_return_handle())
4981 .unwrap();
4982 assert_eq!(receiver.recv().await.unwrap(), 123u64);
4983 }
4984 }
4985 }
4986
4987 #[test]
4988 fn test_is_stale_session_close() {
4989 let stale = TxStatus::Closed(CloseReason::SequenceMismatch(
4991 "out-of-sequence message, expected seq 0, got 7".into(),
4992 ));
4993 assert!(is_stale_session_close(&stale));
4994
4995 let oversize = TxStatus::Closed(CloseReason::OversizedFrame {
4998 size: 55_001_392,
4999 max: 50_000_000,
5000 });
5001 assert!(!is_stale_session_close(&oversize));
5002
5003 let generic = TxStatus::Closed(CloseReason::Other("test teardown".into()));
5004 assert!(!is_stale_session_close(&generic));
5005
5006 assert!(!is_stale_session_close(&TxStatus::Active));
5008 }
5009
5010 #[tokio::test]
5011 async fn test_dial_router_keeps_client_closed_for_non_stale_reason() {
5012 let mbox = Mailbox::new(test_actor_id("non_stale_close_0", "actor0"));
5019 let (addr, rx) =
5020 channel::serve::<MessageEnvelope>(ChannelAddr::any(ChannelTransport::Local)).unwrap();
5021 let h = mbox.clone().serve(rx);
5022
5023 let router = DialMailboxRouter::new();
5024 router.bind(Addr::from(mbox.actor_addr().clone()), addr.clone());
5025
5026 let client1 = router.dial(&addr, mbox.actor_addr()).unwrap();
5027 let mut status = client1.tx_status().clone();
5028
5029 let (port, receiver) = mbox.open_once_port::<u64>();
5036 let port = port.bind();
5037 router
5038 .serialize_and_send_once(port, 67u64, monitored_return_handle())
5039 .unwrap();
5040 assert_eq!(receiver.recv().await.unwrap(), 67u64);
5041 client1.flush().await.unwrap();
5042
5043 h.stop("test teardown");
5047 let _ = h.await;
5048 while !status.borrow_and_update().is_closed() {
5049 status.changed().await.unwrap();
5050 }
5051 assert!(!is_stale_session_close(&status.borrow()));
5052
5053 let client2 = router.dial(&addr, mbox.actor_addr()).unwrap();
5054 assert!(
5055 Arc::ptr_eq(&client1, &client2),
5056 "router must not evict a client closed for a non-stale reason"
5057 );
5058 let client3 = router.dial(&addr, mbox.actor_addr()).unwrap();
5059 assert!(Arc::ptr_eq(&client1, &client3));
5060 }
5061
5062 #[tokio::test]
5063 async fn test_enqueue_port() {
5064 let proc = Proc::isolated();
5065 let client = proc.client("client");
5066
5067 let count = Arc::new(AtomicUsize::new(0));
5068 let count_clone = count.clone();
5069 let port = client.mailbox().open_enqueue_port(move |_, n| {
5070 count_clone.fetch_add(n, Ordering::SeqCst);
5071 Ok(())
5072 });
5073
5074 port.post(&client, 10);
5075 port.post(&client, 5);
5076 port.post(&client, 1);
5077 port.post(&client, 0);
5078
5079 assert_eq!(count.load(Ordering::SeqCst), 16);
5080 }
5081
5082 #[derive(Clone, Debug, Serialize, Deserialize, typeuri::Named)]
5083 struct TestMessage;
5084
5085 #[derive(Clone, Debug, Serialize, Deserialize, typeuri::Named)]
5086 #[named(name = "some::custom::path")]
5087 struct TestMessage2;
5088
5089 #[test]
5090 fn test_remote_message_macros() {
5091 assert_eq!(
5092 TestMessage::typename(),
5093 "hyperactor::mailbox::tests::TestMessage"
5094 );
5095 assert_eq!(TestMessage2::typename(), "some::custom::path");
5096 }
5097
5098 #[test]
5099 fn test_message_envelope_display() {
5100 #[derive(typeuri::Named, Serialize, Deserialize)]
5101 struct MyTest {
5102 a: u64,
5103 b: String,
5104 }
5105 wirevalue::register_type!(MyTest);
5106
5107 let envelope = MessageEnvelope::serialize(
5108 test_actor_id("source_0", "actor"),
5109 test_port_id("dest_1", "actor", 123),
5110 &MyTest {
5111 a: 123,
5112 b: "hello".into(),
5113 },
5114 Flattrs::new(),
5115 )
5116 .unwrap();
5117
5118 assert!(format!("{}", envelope).contains("MyTest{\"a\":123,\"b\":\"hello\"}"));
5120 }
5121
5122 #[derive(Debug, Default)]
5123 struct Foo;
5124
5125 impl Actor for Foo {}
5126
5127 #[tokio::test]
5130 async fn test_actor_delivery_failure() {
5131 use crate::actor::ActorStatus;
5134 use crate::testing::proc_supervison::ProcSupervisionCoordinator;
5135
5136 let proc_forwarder = BoxedMailboxSender::new(DialMailboxRouter::new_with_default(
5137 BoxedMailboxSender::new(PanickingMailboxSender),
5138 ));
5139 let proc_id = test_proc_id("quux_0");
5140 let mut proc = Proc::configured(proc_id.clone(), proc_forwarder);
5141 let (_reported, _coordinator) = ProcSupervisionCoordinator::set(&proc).await.unwrap();
5142 let client = proc.client("client");
5143
5144 let foo = proc.spawn(Foo);
5145 let return_handle = foo.port::<Undeliverable<MessageEnvelope>>();
5146 let message = MessageEnvelope::new(
5147 foo.actor_addr().clone(),
5148 test_port_id("corge_0", "bar", 9999),
5149 wirevalue::Any::serialize(&1u64).unwrap(),
5150 Flattrs::new(),
5151 );
5152 return_handle.post(&client, Undeliverable::Returned(message));
5153
5154 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
5155
5156 let foo_status = foo.status();
5157 assert!(matches!(*foo_status.borrow(), ActorStatus::Failed(_)));
5158 let ActorStatus::Failed(ref msg) = *foo_status.borrow() else {
5159 unreachable!()
5160 };
5161 let msg_str = msg.to_string();
5162 assert!(
5167 msg_str.contains("undeliverable message to"),
5168 "expected destination-named top line, got:\n{msg_str}"
5169 );
5170 assert!(
5171 !msg_str.contains("undeliverable message error"),
5172 "retired neutral fallback must not appear, got:\n{msg_str}"
5173 );
5174 assert!(msg_str.contains("sender:") && msg_str.contains("quux_0"));
5175 assert!(msg_str.contains("dest:") && msg_str.contains("corge_0"));
5176
5177 proc.destroy_and_wait(tokio::time::Duration::from_secs(1), "test cleanup")
5178 .await
5179 .unwrap();
5180 }
5181
5182 #[tokio::test]
5183 async fn test_detached_return_handle() {
5184 let (return_handle, mut return_receiver) =
5185 crate::mailbox::undeliverable::new_undeliverable_port();
5186 let envelope = MessageEnvelope::new(
5188 test_actor_id("foo_0", "bar"),
5189 test_port_id("baz_0", "corge", 9999),
5190 wirevalue::Any::serialize(&1u64).unwrap(),
5191 Flattrs::new(),
5192 );
5193 let proc = Proc::isolated();
5194 let client = proc.client("client");
5195 return_handle.post(&client, Undeliverable::Returned(envelope.clone()));
5196 assert!(
5198 tokio::time::timeout(tokio::time::Duration::from_secs(1), return_receiver.recv())
5199 .await
5200 .is_ok()
5201 );
5202 let monitor_handle = tokio::spawn(async move {
5205 while let Ok(Undeliverable::Returned(mut envelope)) = return_receiver.recv().await {
5206 envelope.push_delivery_failure(DeliveryFailure::new(
5207 UndeliverableReason::Transport(TransportFailure::new(
5208 envelope.dest().clone(),
5209 TransportFailureReason::LinkUnavailable(
5210 "returned in unit test".to_string(),
5211 ),
5212 )),
5213 ));
5214 UndeliverableMailboxSender
5215 .post(envelope, monitored_return_handle());
5216 }
5217 });
5218 drop(return_handle);
5219 assert!(
5220 tokio::time::timeout(tokio::time::Duration::from_secs(1), monitor_handle)
5221 .await
5222 .is_ok()
5223 );
5224 }
5225
5226 async fn verify_receiver(coalesce: bool, drop_sender: bool) {
5227 fn create_receiver<M>(
5228 coalesce: bool,
5229 ) -> (mpsc::UnboundedSender<SequencedEnvelope<M>>, PortReceiver<M>) {
5230 let dummy_actor_ref: ActorAddr = test_actor_id("world_0", "actor");
5233 let dummy_state = State::new(dummy_actor_ref.clone());
5234 let dummy_port_id = dummy_actor_ref.port_addr(Port::from(0));
5235 let (sender, receiver) = sequenced_unbounded::<SequencedEnvelope<M>>();
5236 let receiver = PortReceiver::new(
5237 receiver,
5238 dummy_port_id,
5239 coalesce,
5240 Mailbox {
5241 inner: Arc::new(dummy_state),
5242 },
5243 );
5244 (sender, receiver)
5245 }
5246
5247 fn send_direct<M>(sender: &mpsc::UnboundedSender<SequencedEnvelope<M>>, message: M) {
5248 sender
5249 .send(SequencedEnvelope::new(SeqInfo::Direct, None, message))
5250 .unwrap();
5251 }
5252
5253 {
5255 let (sender, mut receiver) = create_receiver::<u64>(coalesce);
5256 assert!(receiver.drain().is_empty());
5257
5258 send_direct(&sender, 0);
5259 send_direct(&sender, 1);
5260 send_direct(&sender, 2);
5261 send_direct(&sender, 3);
5262 send_direct(&sender, 4);
5263 send_direct(&sender, 5);
5264 send_direct(&sender, 6);
5265 send_direct(&sender, 7);
5266
5267 if drop_sender {
5268 drop(sender);
5269 }
5270
5271 if !coalesce {
5272 assert_eq!(receiver.drain(), vec![0, 1, 2, 3, 4, 5, 6, 7]);
5273 } else {
5274 assert_eq!(receiver.drain(), vec![7]);
5275 }
5276
5277 assert!(receiver.drain().is_empty());
5278 assert!(receiver.drain().is_empty());
5279 }
5280
5281 {
5283 let (sender, mut receiver) = create_receiver::<u64>(coalesce);
5284 assert!(receiver.try_recv().unwrap().is_none());
5285
5286 send_direct(&sender, 0);
5287 send_direct(&sender, 1);
5288 send_direct(&sender, 2);
5289 send_direct(&sender, 3);
5290
5291 if drop_sender {
5292 drop(sender);
5293 }
5294
5295 if !coalesce {
5296 assert_eq!(receiver.try_recv().unwrap().unwrap(), 0);
5297 assert_eq!(receiver.try_recv().unwrap().unwrap(), 1);
5298 assert_eq!(receiver.try_recv().unwrap().unwrap(), 2);
5299 }
5300 assert_eq!(receiver.try_recv().unwrap().unwrap(), 3);
5301 if drop_sender {
5302 assert_matches!(
5303 receiver.try_recv().unwrap_err().kind(),
5304 MailboxErrorKind::Closed
5305 );
5306 assert_matches!(
5308 receiver.try_recv().unwrap_err().kind(),
5309 MailboxErrorKind::Closed
5310 );
5311 } else {
5312 assert!(receiver.try_recv().unwrap().is_none());
5313 assert!(receiver.try_recv().unwrap().is_none());
5315 }
5316 }
5317 {
5319 let (sender, mut receiver) = create_receiver::<u64>(coalesce);
5320 assert!(
5321 tokio::time::timeout(tokio::time::Duration::from_secs(1), receiver.recv())
5322 .await
5323 .is_err()
5324 );
5325
5326 send_direct(&sender, 4);
5327 send_direct(&sender, 5);
5328 send_direct(&sender, 6);
5329 send_direct(&sender, 7);
5330
5331 if drop_sender {
5332 drop(sender);
5333 }
5334
5335 if !coalesce {
5336 assert_eq!(receiver.recv().await.unwrap(), 4);
5337 assert_eq!(receiver.recv().await.unwrap(), 5);
5338 assert_eq!(receiver.recv().await.unwrap(), 6);
5339 }
5340 assert_eq!(receiver.recv().await.unwrap(), 7);
5341 if drop_sender {
5342 assert_matches!(
5343 receiver.recv().await.unwrap_err().kind(),
5344 MailboxErrorKind::Closed
5345 );
5346 assert_matches!(
5348 receiver.recv().await.unwrap_err().kind(),
5349 MailboxErrorKind::Closed
5350 );
5351 } else {
5352 assert!(
5353 tokio::time::timeout(tokio::time::Duration::from_secs(1), receiver.recv())
5354 .await
5355 .is_err()
5356 );
5357 }
5358 }
5359 }
5360
5361 #[tokio::test]
5362 async fn test_receiver_basic_default() {
5363 verify_receiver(false, false).await
5364 }
5365
5366 #[tokio::test]
5367 async fn test_receiver_basic_latest() {
5368 verify_receiver(true, false).await
5369 }
5370
5371 #[tokio::test]
5372 async fn test_receiver_after_sender_drop_default() {
5373 verify_receiver(false, true).await
5374 }
5375
5376 #[tokio::test]
5377 async fn test_receiver_after_sender_drop_latest() {
5378 verify_receiver(true, true).await
5379 }
5380
5381 struct Setup {
5382 receiver: PortReceiver<u64>,
5383 actor0: crate::Client,
5384 actor1: crate::Client,
5385 port_id: PortAddr,
5386 port_id1: PortAddr,
5387 port_id2: PortAddr,
5388 port_id2_1: PortAddr,
5389 }
5390
5391 async fn setup_split_port_ids(
5392 reducer_spec: Option<ReducerSpec>,
5393 reducer_mode: ReducerMode,
5394 ) -> Setup {
5395 let proc = Proc::isolated();
5396 let actor0 = proc.client("actor0");
5397 let actor1 = proc.client("actor1");
5398
5399 let (port_handle, receiver) = actor0.open_port::<u64>();
5401 let port_id = port_handle.bind().port_addr().clone();
5402
5403 let port_id1 = port_id
5405 .split(&actor1, reducer_spec.clone(), reducer_mode.clone(), true)
5406 .unwrap();
5407 let port_id2 = port_id
5408 .split(&actor1, reducer_spec.clone(), reducer_mode.clone(), true)
5409 .unwrap();
5410
5411 let port_id2_1 = port_id2
5413 .split(&actor1, reducer_spec, reducer_mode.clone(), true)
5414 .unwrap();
5415
5416 Setup {
5417 receiver,
5418 actor0,
5419 actor1,
5420 port_id,
5421 port_id1,
5422 port_id2,
5423 port_id2_1,
5424 }
5425 }
5426
5427 fn post(cx: &impl context::Actor, port_id: PortAddr, msg: u64) {
5428 let serialized = wirevalue::Any::serialize(&msg).unwrap();
5429 port_id.send(cx, serialized);
5430 }
5431
5432 #[async_timed_test(timeout_secs = 30)]
5433 #[cfg_attr(not(fbcode_build), ignore)]
5435 async fn test_split_port_id_no_reducer() {
5436 let Setup {
5437 mut receiver,
5438 actor0,
5439 actor1,
5440 port_id,
5441 port_id1,
5442 port_id2,
5443 port_id2_1,
5444 ..
5445 } = setup_split_port_ids(None, ReducerMode::default()).await;
5446 post(&actor0, port_id.clone(), 1);
5448 assert_eq!(receiver.recv().await.unwrap(), 1);
5449 post(&actor1, port_id1.clone(), 2);
5450 assert_eq!(receiver.recv().await.unwrap(), 2);
5451 post(&actor1, port_id2.clone(), 3);
5452 assert_eq!(receiver.recv().await.unwrap(), 3);
5453 post(&actor1, port_id2_1.clone(), 4);
5454 assert_eq!(receiver.recv().await.unwrap(), 4);
5455
5456 tokio::time::sleep(Duration::from_secs(2)).await;
5458 let msg = receiver.try_recv().unwrap();
5459 assert_eq!(msg, None);
5460 }
5461
5462 async fn wait_for(
5463 receiver: &mut PortReceiver<u64>,
5464 expected_size: usize,
5465 timeout_duration: Duration,
5466 ) -> anyhow::Result<Vec<u64>> {
5467 let mut messeges = vec![];
5468
5469 tokio::time::timeout(timeout_duration, async {
5470 loop {
5471 let msg = receiver.recv().await.unwrap();
5472 messeges.push(msg);
5473 if messeges.len() == expected_size {
5474 break;
5475 }
5476 }
5477 })
5478 .await?;
5479 Ok(messeges)
5480 }
5481
5482 #[async_timed_test(timeout_secs = 30)]
5483 async fn test_split_port_id_sum_reducer() {
5484 let config = hyperactor_config::global::lock();
5485 let _config_guard = config.override_key(crate::config::SPLIT_MAX_BUFFER_SIZE, 1);
5486
5487 let sum_accumulator = accum::sum::<u64>();
5488 let reducer_spec = sum_accumulator.reducer_spec();
5489 let Setup {
5490 mut receiver,
5491 actor0,
5492 actor1,
5493 port_id,
5494 port_id1,
5495 port_id2,
5496 port_id2_1,
5497 ..
5498 } = setup_split_port_ids(reducer_spec, ReducerMode::default()).await;
5499 post(&actor0, port_id.clone(), 4);
5500 post(&actor1, port_id1.clone(), 2);
5501 post(&actor1, port_id2.clone(), 3);
5502 post(&actor1, port_id2_1.clone(), 1);
5503 let mut messages = wait_for(&mut receiver, 4, Duration::from_secs(2))
5504 .await
5505 .unwrap();
5506 messages.sort();
5509 assert_eq!(messages, vec![1, 2, 3, 4]);
5510
5511 tokio::time::sleep(Duration::from_secs(2)).await;
5513 let msg = receiver.try_recv().unwrap();
5514 assert_eq!(msg, None);
5515 }
5516
5517 #[async_timed_test(timeout_secs = 30)]
5518 #[cfg_attr(not(fbcode_build), ignore)]
5520 async fn test_split_port_id_every_n_messages() {
5521 let config = hyperactor_config::global::lock();
5522 let _config_guard =
5523 config.override_key(crate::config::SPLIT_MAX_BUFFER_AGE, Duration::from_mins(10));
5524 let proc = Proc::isolated();
5525 let actor = proc.client("actor");
5526 let (port_handle, mut receiver) = actor.open_port::<u64>();
5527 let port_id = port_handle.bind().port_addr().clone();
5528 let reducer_spec = accum::sum::<u64>().reducer_spec();
5530 let split_port_id = port_id
5531 .split(
5532 &actor,
5533 reducer_spec,
5534 ReducerMode::Streaming(accum::StreamingReducerOpts {
5535 max_update_interval: Some(Duration::from_mins(10)),
5536 initial_update_interval: Some(Duration::from_mins(10)),
5537 }),
5538 true,
5539 )
5540 .unwrap();
5541
5542 for msg in [1, 5, 3, 4, 2, 91, 92, 93, 94] {
5544 post(&actor, split_port_id.clone(), msg);
5545 }
5546 let messages = wait_for(&mut receiver, 1, Duration::from_secs(2))
5549 .await
5550 .unwrap();
5551 assert_eq!(messages, vec![15]);
5552
5553 tokio::time::sleep(Duration::from_secs(2)).await;
5556 let msg = receiver.try_recv().unwrap();
5557 assert_eq!(msg, None);
5558 }
5559
5560 #[async_timed_test(timeout_secs = 30)]
5561 async fn test_split_port_timeout_flush() {
5562 let config = hyperactor_config::global::lock();
5563 let _config_guard = config.override_key(crate::config::SPLIT_MAX_BUFFER_SIZE, 100);
5564
5565 let Setup {
5566 mut receiver,
5567 actor0: _actor0,
5568 actor1,
5569 port_id: _,
5570 port_id1,
5571 port_id2: _,
5572 port_id2_1: _,
5573 ..
5574 } = setup_split_port_ids(
5575 Some(accum::sum::<u64>().reducer_spec().unwrap()),
5576 ReducerMode::Streaming(accum::StreamingReducerOpts {
5577 max_update_interval: Some(Duration::from_millis(50)),
5578 initial_update_interval: Some(Duration::from_millis(50)),
5579 }),
5580 )
5581 .await;
5582
5583 post(&actor1, port_id1.clone(), 10);
5584 post(&actor1, port_id1.clone(), 20);
5585 post(&actor1, port_id1.clone(), 30);
5586
5587 tokio::time::sleep(Duration::from_millis(10)).await;
5589 let msg = receiver.try_recv().unwrap();
5590 assert_eq!(msg, None);
5591
5592 tokio::time::sleep(Duration::from_millis(100)).await;
5594
5595 let msg = receiver.recv().await.unwrap();
5597 assert_eq!(msg, 60); let msg = receiver.try_recv().unwrap();
5601 assert_eq!(msg, None);
5602 }
5603
5604 #[async_timed_test(timeout_secs = 30)]
5605 async fn test_split_port_timeout_and_size_flush() {
5606 let config = hyperactor_config::global::lock();
5607 let _config_guard = config.override_key(crate::config::SPLIT_MAX_BUFFER_SIZE, 3);
5608
5609 let Setup {
5610 mut receiver,
5611 actor0: _actor0,
5612 actor1,
5613 port_id: _,
5614 port_id1,
5615 port_id2: _,
5616 port_id2_1: _,
5617 ..
5618 } = setup_split_port_ids(
5619 Some(accum::sum::<u64>().reducer_spec().unwrap()),
5620 ReducerMode::Streaming(accum::StreamingReducerOpts {
5621 max_update_interval: Some(Duration::from_millis(50)),
5622 initial_update_interval: Some(Duration::from_millis(50)),
5623 }),
5624 )
5625 .await;
5626
5627 post(&actor1, port_id1.clone(), 10);
5628 post(&actor1, port_id1.clone(), 20);
5629 post(&actor1, port_id1.clone(), 30);
5630 post(&actor1, port_id1.clone(), 40);
5631
5632 let msg = receiver.recv().await.unwrap();
5634 assert_eq!(msg, 60);
5635
5636 let msg = receiver.recv().await.unwrap();
5638 assert_eq!(msg, 40);
5639
5640 let msg = receiver.try_recv().unwrap();
5642 assert_eq!(msg, None);
5643 }
5644
5645 #[async_timed_test(timeout_secs = 30)]
5646 async fn test_split_port_once_mode_basic() {
5647 let proc = Proc::isolated();
5648 let actor = proc.client("actor");
5649 let (port_handle, mut receiver) = actor.open_port::<u64>();
5650 let port_id = port_handle.bind().port_addr().clone();
5651
5652 let reducer_spec = accum::sum::<u64>().reducer_spec();
5654 let split_port_id = port_id
5655 .split(&actor, reducer_spec, ReducerMode::Once(3), true)
5656 .unwrap();
5657
5658 post(&actor, split_port_id.clone(), 10);
5660 post(&actor, split_port_id.clone(), 20);
5661 post(&actor, split_port_id.clone(), 30);
5662
5663 let msg = receiver.recv().await.unwrap();
5665 assert_eq!(msg, 60); tokio::time::sleep(Duration::from_millis(100)).await;
5669 let msg = receiver.try_recv().unwrap();
5670 assert_eq!(msg, None);
5671 }
5672
5673 #[derive(Debug)]
5674 #[hyperactor::export(handlers = [u64])]
5675 struct SplitPortReceivingActor {
5676 received: PortRef<String>,
5677 }
5678
5679 impl Actor for SplitPortReceivingActor {}
5680
5681 #[async_trait]
5682 impl Handler<u64> for SplitPortReceivingActor {
5683 async fn handle(&mut self, cx: &crate::Context<Self>, msg: u64) -> anyhow::Result<()> {
5684 let endpoint = cx
5685 .headers()
5686 .get(headers::OPERATION_ENDPOINT)
5687 .unwrap_or_default();
5688 self.received
5689 .post(cx, format!("OPERATION_ENDPOINT={endpoint} sum={msg}"));
5690 Ok(())
5691 }
5692 }
5693
5694 #[async_timed_test(timeout_secs = 30)]
5695 async fn test_split_port_preserves_operation_context_headers() {
5696 let proc = Proc::isolated();
5697 let client = proc.client("client");
5698 let (received_handle, mut observed_rx) = client.open_port::<String>();
5699 let capture_handle = proc.spawn_with_label(
5700 "split_port_receiver",
5701 SplitPortReceivingActor {
5702 received: received_handle.bind(),
5703 },
5704 );
5705 let capture_ref: ActorRef<SplitPortReceivingActor> = capture_handle.bind();
5706 let port_id = capture_ref.port::<u64>().port_addr().clone();
5707
5708 let split_port_id = port_id
5709 .split(
5710 &client,
5711 accum::sum::<u64>().reducer_spec(),
5713 ReducerMode::Once(2),
5714 true,
5715 )
5716 .unwrap();
5717
5718 let mut headers = Flattrs::new();
5719 headers.set(headers::OPERATION_ENDPOINT, "endpoint.call()".to_string());
5720 client.post(
5721 split_port_id.clone(),
5722 headers.clone(),
5723 wirevalue::Any::serialize(&1u64).unwrap(),
5725 true,
5726 crate::context::SeqInfoPolicy::AssignNew,
5727 );
5728 client.post(
5729 split_port_id,
5730 headers,
5731 wirevalue::Any::serialize(&2u64).unwrap(),
5733 true,
5734 crate::context::SeqInfoPolicy::AssignNew,
5735 );
5736
5737 assert_eq!(
5738 observed_rx.recv().await.unwrap(),
5739 "OPERATION_ENDPOINT=endpoint.call() sum=3"
5740 );
5741 }
5742
5743 #[async_timed_test(timeout_secs = 30)]
5744 async fn test_split_port_once_mode_teardown() {
5745 let proc = Proc::isolated();
5746 let actor = proc.client("actor");
5747 let (port_handle, mut receiver) = actor.open_port::<u64>();
5748 let port_id = port_handle.bind().port_addr().clone();
5749
5750 let (undeliverable_handle, mut undeliverable_receiver) =
5752 undeliverable::new_undeliverable_port();
5753
5754 let reducer_spec = accum::sum::<u64>().reducer_spec();
5756 let split_port_id = port_id
5757 .split(&actor, reducer_spec, ReducerMode::Once(3), true)
5758 .unwrap();
5759
5760 post(&actor, split_port_id.clone(), 10);
5762 post(&actor, split_port_id.clone(), 20);
5763 post(&actor, split_port_id.clone(), 30);
5764
5765 let msg = receiver.recv().await.unwrap();
5767 assert_eq!(msg, 60); let serialized = wirevalue::Any::serialize(&100u64).unwrap();
5771 let envelope = MessageEnvelope::new(
5772 actor.mailbox().actor_addr().clone(),
5773 split_port_id.clone(),
5774 serialized,
5775 Flattrs::new(),
5776 );
5777 actor.mailbox().post(envelope, undeliverable_handle);
5778
5779 let undeliverable =
5781 tokio::time::timeout(Duration::from_secs(2), undeliverable_receiver.recv())
5782 .await
5783 .expect("should receive undeliverable message")
5784 .expect("undeliverable receiver closed");
5785
5786 let split_port_ref: PortAddr = split_port_id;
5788 assert_eq!(
5789 undeliverable
5790 .into_message()
5791 .expect("expected returned envelope")
5792 .dest(),
5793 &split_port_ref
5794 );
5795
5796 let msg = receiver.try_recv().unwrap();
5798 assert_eq!(msg, None);
5799 }
5800
5801 #[test]
5802 fn test_dial_mailbox_router_prefixes_empty() {
5803 assert_eq!(DialMailboxRouter::new().prefixes().len(), 0);
5804 }
5805
5806 #[test]
5807 fn test_dial_mailbox_router_prefixes_single_entry() {
5808 let router = DialMailboxRouter::new();
5809 router.bind(test_proc_ref("world0"), "unix!@1".parse().unwrap());
5810
5811 let prefixes: Vec<Addr> = router.prefixes().into_iter().collect();
5812 assert_eq!(prefixes.len(), 1);
5813 assert_eq!(prefixes[0], test_proc_ref("world0"));
5814 }
5815
5816 #[test]
5817 fn test_dial_mailbox_router_prefixes_no_overlap() {
5818 let router = DialMailboxRouter::new();
5819 router.bind(test_proc_ref("world0"), "unix!@1".parse().unwrap());
5820 router.bind(test_proc_ref("world1"), "unix!@2".parse().unwrap());
5821 router.bind(test_proc_ref("world2"), "unix!@3".parse().unwrap());
5822
5823 let mut prefixes: Vec<Addr> = router.prefixes().into_iter().collect();
5824 prefixes.sort();
5825
5826 let mut expected = vec![
5827 test_proc_ref("world0"),
5828 test_proc_ref("world1"),
5829 test_proc_ref("world2"),
5830 ];
5831 expected.sort();
5832
5833 assert_eq!(prefixes, expected);
5834 }
5835
5836 #[test]
5837 fn test_dial_mailbox_router_prefixes_with_overlaps() {
5838 let router = DialMailboxRouter::new();
5839 router.bind(test_proc_ref("world0"), "unix!@1".parse().unwrap());
5841 router.bind(test_proc_ref("world0_0"), "unix!@2".parse().unwrap());
5842 router.bind(test_proc_ref("world0_1"), "unix!@3".parse().unwrap());
5843 router.bind(test_proc_ref("world1"), "unix!@4".parse().unwrap());
5844 router.bind(test_proc_ref("world1_0"), "unix!@5".parse().unwrap());
5845
5846 let mut prefixes: Vec<Addr> = router.prefixes().into_iter().collect();
5847 prefixes.sort();
5848
5849 let mut expected = vec![
5850 test_proc_ref("world0"),
5851 test_proc_ref("world0_0"),
5852 test_proc_ref("world0_1"),
5853 test_proc_ref("world1"),
5854 test_proc_ref("world1_0"),
5855 ];
5856 expected.sort();
5857
5858 assert_eq!(prefixes, expected);
5859 }
5860
5861 #[test]
5862 fn test_dial_mailbox_router_prefixes_complex_hierarchy() {
5863 let router = DialMailboxRouter::new();
5864 router.bind(test_proc_ref("world0"), "unix!@1".parse().unwrap());
5866 router.bind(test_proc_ref("world0_0"), "unix!@2".parse().unwrap());
5867 router.bind(
5868 test_actor_ref("world0_0", "actor1"),
5869 "unix!@3".parse().unwrap(),
5870 );
5871 router.bind(test_proc_ref("world1_0"), "unix!@4".parse().unwrap());
5872 router.bind(test_proc_ref("world1_1"), "unix!@5".parse().unwrap());
5873 router.bind(
5874 test_actor_ref("world2_0", "actor0"),
5875 "unix!@6".parse().unwrap(),
5876 );
5877
5878 let mut prefixes: Vec<Addr> = router.prefixes().into_iter().collect();
5879 prefixes.sort();
5880
5881 let mut expected = vec![
5888 test_proc_ref("world0"),
5889 test_proc_ref("world0_0"),
5890 test_proc_ref("world1_0"),
5891 test_proc_ref("world1_1"),
5892 test_actor_ref("world2_0", "actor0"),
5893 ];
5894 expected.sort();
5895
5896 assert_eq!(prefixes, expected);
5897 }
5898
5899 #[test]
5900 fn test_dial_mailbox_router_prefixes_same_level() {
5901 let router = DialMailboxRouter::new();
5902 router.bind(test_proc_ref("world0_0"), "unix!@1".parse().unwrap());
5903 router.bind(test_proc_ref("world0_1"), "unix!@2".parse().unwrap());
5904 router.bind(test_proc_ref("world0_2"), "unix!@3".parse().unwrap());
5905
5906 let mut prefixes: Vec<Addr> = router.prefixes().into_iter().collect();
5907 prefixes.sort();
5908
5909 let mut expected = vec![
5911 test_proc_ref("world0_0"),
5912 test_proc_ref("world0_1"),
5913 test_proc_ref("world0_2"),
5914 ];
5915 expected.sort();
5916
5917 assert_eq!(prefixes, expected);
5918 }
5919
5920 #[derive(Clone, Debug)]
5924 struct AsyncLoopForwarder;
5925
5926 #[async_trait]
5927 impl MailboxSender for AsyncLoopForwarder {
5928 fn post_unchecked(
5929 &self,
5930 envelope: MessageEnvelope,
5931 return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
5932 ) {
5933 let me = self.clone();
5934 tokio::spawn(async move {
5935 me.post(envelope, return_handle);
5937 });
5938 }
5939 }
5940
5941 #[tokio::test]
5942 async fn message_ttl_expires_in_routing_loop_returns_to_sender() {
5943 let actor_id = test_actor_id("world_0", "ttl_actor");
5944 let (ret_port, mut ret_rx) = undeliverable::new_undeliverable_port();
5945
5946 let remote_actor = test_actor_id("remote_world_1", "remote");
5947 let dest = remote_actor.port_addr(4242.into());
5948
5949 let payload = 1234_u64;
5952 let envelope =
5953 MessageEnvelope::serialize(actor_id.clone(), dest.clone(), &payload, Flattrs::new())
5954 .expect("serialize");
5955
5956 AsyncLoopForwarder.post(envelope, ret_port.clone());
5957
5958 let undelivered = tokio::time::timeout(Duration::from_secs(5), ret_rx.recv())
5960 .await
5961 .expect("timed out waiting for undeliverable")
5962 .expect("channel closed")
5963 .into_message()
5964 .expect("expected returned envelope");
5965
5966 let got: u64 = undelivered.deserialized().expect("deserialize");
5968 assert_eq!(got, payload, "payload preserved");
5969 }
5970
5971 #[tokio::test]
5972 async fn message_ttl_success_local_delivery() {
5973 let actor_id = test_actor_id("world_0", "ttl_actor");
5974 let mailbox = Mailbox::new(actor_id.clone());
5975 let (_undeliverable_tx, mut undeliverable_rx) =
5976 mailbox.bind_handler_port::<Undeliverable<MessageEnvelope>>();
5977
5978 let (user_port, mut user_rx) = mailbox.open_port::<u64>();
5980
5981 let payload = 0xC0FFEE_u64;
5983 let envelope = MessageEnvelope::serialize(
5984 actor_id.clone(),
5985 user_port.bind().port_addr().clone(),
5986 &payload,
5987 Flattrs::new(),
5988 )
5989 .expect("serialize");
5990
5991 let return_handle = mailbox
5994 .bound_return_handle()
5995 .unwrap_or(monitored_return_handle());
5996 mailbox.post(envelope, return_handle);
5997
5998 let got = tokio::time::timeout(Duration::from_secs(1), user_rx.recv())
6000 .await
6001 .expect("timed out waiting for local delivery")
6002 .expect("user port closed");
6003 assert_eq!(got, payload);
6004
6005 let no_undeliverable =
6007 tokio::time::timeout(Duration::from_millis(100), undeliverable_rx.recv()).await;
6008 assert!(
6009 no_undeliverable.is_err(),
6010 "unexpected undeliverable returned on successful local delivery"
6011 );
6012 }
6013
6014 #[tokio::test]
6015 async fn test_port_contramap() {
6016 let proc = Proc::isolated();
6017 let client = proc.client("client");
6018 let (handle, mut rx) = client.open_port();
6019
6020 handle
6021 .contramap(|m| (1, m))
6022 .post(&client, "hello".to_string());
6023 assert_eq!(rx.recv().await.unwrap(), (1, "hello".to_string()));
6024 }
6025
6026 #[test]
6027 fn test_bind_open_port_uses_ephemeral_port() {
6028 let mbox = Mailbox::new(test_actor_id("0", "test"));
6029 let (handle, _rx) = mbox.open_port::<String>();
6030 let ephemeral_port = mbox
6031 .actor_addr()
6032 .port_addr(Port::from(handle.inner.bind_target.ephemeral_index()));
6033 let handler_port = mbox.actor_addr().port_addr(Port::handler::<String>());
6034
6035 let port_ref = handle.bind();
6036
6037 assert_eq!(port_ref.port_addr(), &ephemeral_port);
6038 assert_ne!(port_ref.port_addr(), &handler_port);
6039 }
6040
6041 #[test]
6042 fn test_bind_handler_port_handle_twice_is_idempotent() {
6043 let mbox = Mailbox::new(test_actor_id("0", "test"));
6044 let default_port = mbox.actor_addr().port_addr(Port::handler::<String>());
6045 let handle = mbox.open_handler_enqueue_port(|_, _message: String| Ok(()));
6046 assert_matches!(handle.inner.bind_target, PortBindTarget::Handler);
6047
6048 let first = handle.bind();
6049 let second = handle.bind();
6050
6051 assert_eq!(first.port_addr(), &default_port);
6052 assert_eq!(second.port_addr(), first.port_addr());
6053 assert_matches!(handle.location(), PortLocation::Bound(port) if port == default_port);
6054 }
6055
6056 #[test]
6057 fn test_bind_handler_port_helper_returns_handler_bound_handle() {
6058 let mbox = Mailbox::new(test_actor_id("0", "test"));
6059 let default_port = mbox.actor_addr().port_addr(Port::handler::<String>());
6060 let (handle, _rx) = mbox.bind_handler_port::<String>();
6061 assert_matches!(handle.inner.bind_target, PortBindTarget::Handler);
6062
6063 let port_ref = handle.bind();
6064
6065 assert_eq!(port_ref.port_addr(), &default_port);
6066 assert_matches!(handle.location(), PortLocation::Bound(port) if port == default_port);
6067 }
6068
6069 #[test]
6070 #[should_panic(expected = "already bound")]
6071 fn test_bind_port_handle_to_handler_port_when_already_bound() {
6072 let mbox = Mailbox::new(test_actor_id("0", "test"));
6073 let (handle, _rx) = mbox.open_port::<String>();
6074 handle.bind();
6076 assert_matches!(handle.location(), PortLocation::Bound(port) if port.index() == handle.inner.bind_target.ephemeral_index());
6077 handle.bind_handler_port();
6079 }
6080
6081 #[tokio::test]
6082 async fn test_mailbox_post_fails_when_actor_stopped() {
6083 let actor_id = test_actor_id("0", "stopped_actor");
6084
6085 let mailbox = Mailbox::new(actor_id.clone());
6086
6087 mailbox.close(ActorStatus::Stopped("test stop".to_string()));
6088
6089 let (user_port, _user_rx) = mailbox.open_port::<u64>();
6090
6091 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
6094
6095 let envelope = MessageEnvelope::serialize(
6096 actor_id.clone(),
6097 user_port.bind().port_addr().clone(),
6098 &42u64,
6099 Flattrs::new(),
6100 )
6101 .expect("serialize");
6102
6103 mailbox.post(envelope, return_handle);
6104
6105 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
6106 .await
6107 .expect("timed out waiting for undeliverable")
6108 .expect("return port closed")
6109 .into_message()
6110 .expect("expected returned envelope");
6111
6112 let err = undelivered.error_msg().expect("expected error");
6113 assert!(
6114 err.contains("actor stopped"),
6115 "error should indicate actor stopped: {}",
6116 err
6117 );
6118 let root_failure = undelivered
6119 .root_delivery_failure()
6120 .expect("expected root delivery failure");
6121 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
6122 panic!("expected invalid reference, got {root_failure}");
6123 };
6124 assert_eq!(
6125 invalid_reference.reason,
6126 InvalidReferenceReason::ActorStopped
6127 );
6128 }
6129
6130 #[tokio::test]
6131 async fn test_mailbox_post_fails_when_actor_failed() {
6132 use crate::actor::ActorErrorKind;
6133
6134 let actor_id = test_actor_id("0", "failed_actor");
6135
6136 let mailbox = Mailbox::new(actor_id.clone());
6137
6138 let (user_port, _user_rx) = mailbox.open_port::<u64>();
6139
6140 mailbox.close(ActorStatus::Failed(ActorErrorKind::Generic(
6141 "test failure".to_string(),
6142 )));
6143
6144 let (return_handle, mut return_rx) = undeliverable::new_undeliverable_port();
6147
6148 let envelope = MessageEnvelope::serialize(
6149 actor_id.clone(),
6150 user_port.bind().port_addr().clone(),
6151 &42u64,
6152 Flattrs::new(),
6153 )
6154 .expect("serialize");
6155
6156 mailbox.post(envelope, return_handle);
6157
6158 let undelivered = tokio::time::timeout(Duration::from_secs(1), return_rx.recv())
6159 .await
6160 .expect("timed out waiting for undeliverable")
6161 .expect("return port closed")
6162 .into_message()
6163 .expect("expected returned envelope");
6164
6165 let err = undelivered.error_msg().expect("expected error");
6166 assert!(
6167 err.contains("actor failed"),
6168 "error should indicate actor failed: {}",
6169 err
6170 );
6171 let root_failure = undelivered
6172 .root_delivery_failure()
6173 .expect("expected root delivery failure");
6174 let DeliveryFailureKind::InvalidReference(invalid_reference) = &root_failure.kind else {
6175 panic!("expected invalid reference, got {root_failure}");
6176 };
6177 assert_eq!(
6178 invalid_reference.reason,
6179 InvalidReferenceReason::ActorFailed
6180 );
6181 }
6182
6183 #[tokio::test]
6184 async fn test_port_handle_send_fails_when_actor_stopped() {
6185 let actor_id = test_actor_id("0", "stopped_actor");
6186
6187 let mailbox = Mailbox::new(actor_id.clone());
6188
6189 let (port_handle, _rx) = mailbox.open_port::<u64>();
6190 let proc = Proc::isolated();
6191 let client = proc.client("client");
6192
6193 mailbox.close(ActorStatus::Stopped("test stop".to_string()));
6194
6195 let err = port_handle.try_post(&client, 42u64).unwrap_err();
6196 assert_matches!(
6197 err.kind(),
6198 MailboxSenderErrorKind::Mailbox(mailbox_err)
6199 if matches!(mailbox_err.kind(), MailboxErrorKind::OwnerTerminated(ActorStatus::Stopped(reason)) if reason == "test stop")
6200 );
6201 }
6202
6203 #[tokio::test]
6204 async fn test_port_handle_send_fails_when_actor_failed() {
6205 use crate::actor::ActorErrorKind;
6206
6207 let actor_id = test_actor_id("0", "failed_actor");
6208
6209 let mailbox = Mailbox::new(actor_id.clone());
6210
6211 let (port_handle, _rx) = mailbox.open_port::<u64>();
6212 let proc = Proc::isolated();
6213 let client = proc.client("client");
6214
6215 mailbox.close(ActorStatus::Failed(ActorErrorKind::Generic(
6216 "test failure".to_string(),
6217 )));
6218
6219 let err = port_handle.try_post(&client, 42u64).unwrap_err();
6220 assert_matches!(
6221 err.kind(),
6222 MailboxSenderErrorKind::Mailbox(mailbox_err)
6223 if matches!(mailbox_err.kind(), MailboxErrorKind::OwnerTerminated(ActorStatus::Failed(ActorErrorKind::Generic(msg))) if msg == "test failure")
6224 );
6225 }
6226
6227 #[async_timed_test(timeout_secs = 30)]
6228 async fn test_open_reduce_port() {
6229 let proc = Proc::isolated();
6230 let client = proc.client("client");
6231
6232 let (port_handle, receiver) = client.mailbox().open_reduce_port(accum::sum::<u64>());
6234
6235 let port_ref = port_handle.bind();
6237 assert!(port_ref.reducer_spec().is_some());
6238
6239 port_ref.post(&client, 42);
6241
6242 let result = receiver.recv().await.unwrap();
6244 assert_eq!(result, 42);
6245 }
6246
6247 #[async_timed_test(timeout_secs = 30)]
6248 async fn test_open_reduce_port_reducer_spec_preserved() {
6249 let proc = Proc::isolated();
6250 let client = proc.client("client");
6251
6252 let (sum_handle, _) = client.mailbox().open_reduce_port(accum::sum::<u64>());
6254 let sum_ref = sum_handle.bind();
6255 let sum_typehash = sum_ref.reducer_spec().as_ref().unwrap().typehash;
6256
6257 let (max_handle, _) = client
6258 .mailbox()
6259 .open_reduce_port(accum::join_semilattice::<accum::Max<u64>>());
6260 let max_ref = max_handle.bind();
6261 let max_typehash = max_ref.reducer_spec().as_ref().unwrap().typehash;
6262
6263 assert_ne!(sum_typehash, max_typehash);
6265 }
6266
6267 #[tokio::test]
6272 async fn test_flush_over_unix_channel() {
6273 let mbox = Mailbox::new(test_actor_id("0", "actor0"));
6274
6275 let (addr, rx) = channel::serve(ChannelAddr::any(ChannelTransport::Unix)).unwrap();
6277 let serve_handle = mbox.clone().serve(rx);
6278
6279 let client = MailboxClient::dial(addr).unwrap();
6281
6282 let (port, mut receiver) = mbox.open_port::<u64>();
6284 let port = port.bind();
6285
6286 for i in 0..10u64 {
6288 client
6289 .serialize_and_send(&port, i, monitored_return_handle())
6290 .unwrap();
6291 }
6292
6293 client.flush().await.unwrap();
6296
6297 for i in 0..10u64 {
6299 let msg = receiver
6300 .try_recv()
6301 .expect("message should be available after flush")
6302 .expect("receiver should not be empty after flush");
6303 assert_eq!(msg, i);
6304 }
6305
6306 serve_handle.stop("test done");
6307 serve_handle.await.unwrap().unwrap();
6308 }
6309
6310 #[test]
6311 fn test_drain_waits_for_active_handler_enqueue() {
6312 let mailbox = Mailbox::new(test_actor_id("drain", "actor"));
6313 let (entered_tx, entered_rx) = std::sync::mpsc::channel();
6314 let release = Arc::new((std::sync::Mutex::new(false), std::sync::Condvar::new()));
6315 let delivered = Arc::new(AtomicUsize::new(0));
6316
6317 let port = mailbox.open_handler_enqueue_port({
6318 let release = Arc::clone(&release);
6319 let delivered = Arc::clone(&delivered);
6320 move |_headers, _message: u64| {
6321 entered_tx.send(()).unwrap();
6322 let (lock, cvar) = &*release;
6323 let mut released = lock.lock().unwrap();
6324 while !*released {
6325 released = cvar.wait(released).unwrap();
6326 }
6327 delivered.fetch_add(1, Ordering::SeqCst);
6328 Ok(())
6329 }
6330 });
6331
6332 let sender = port.inner.sender.clone();
6333 let sender_thread = std::thread::spawn(move || sender.send(Flattrs::new(), 1u64).unwrap());
6334 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
6335
6336 let (drained_tx, drained_rx) = std::sync::mpsc::channel();
6337 let drain_thread = std::thread::spawn({
6338 let mailbox = mailbox.clone();
6339 move || {
6340 mailbox.drain();
6341 drained_tx.send(()).unwrap();
6342 }
6343 });
6344
6345 let deadline = std::time::Instant::now() + Duration::from_secs(1);
6346 while mailbox.inner.handler_ingress.state.load(Ordering::Acquire) & HANDLER_INGRESS_DRAINING
6347 == 0
6348 {
6349 assert!(std::time::Instant::now() < deadline, "drain did not start");
6350 std::thread::yield_now();
6351 }
6352 assert_matches!(
6353 drained_rx.try_recv(),
6354 Err(std::sync::mpsc::TryRecvError::Empty)
6355 );
6356
6357 let (lock, cvar) = &*release;
6358 *lock.lock().unwrap() = true;
6359 cvar.notify_all();
6360
6361 sender_thread.join().unwrap();
6362 drained_rx.recv_timeout(Duration::from_secs(1)).unwrap();
6363 drain_thread.join().unwrap();
6364 assert_eq!(delivered.load(Ordering::SeqCst), 1);
6365
6366 let err = port.inner.sender.send(Flattrs::new(), 2u64).unwrap_err();
6367 assert!(err.is::<HandlerPortClosedError>());
6368 }
6369
6370 fn drive_abandonment_log(payload_sentinel: &str) -> (crate::ActorAddr, crate::PortAddr) {
6376 use hyperactor_config::declare_attrs;
6377
6378 declare_attrs! {
6379 attr UM_TEST_HEADER: u64;
6382 }
6383
6384 let sender = test_actor_id("um_proc", "um_sender");
6385 let dest = test_port_id("um_dest_proc", "um_dest", 42);
6386
6387 let mut headers = Flattrs::new();
6388 headers.set(UM_TEST_HEADER, 0xC0FFEEu64);
6389
6390 let envelope = MessageEnvelope::new(
6391 sender.clone(),
6392 dest.clone(),
6393 wirevalue::Any::serialize(&payload_sentinel.to_string()).unwrap(),
6394 headers,
6395 );
6396
6397 let (return_handle, _rx) = crate::mailbox::undeliverable::new_undeliverable_port();
6398 UndeliverableMailboxSender.post_unchecked(envelope, return_handle);
6399 (sender, dest)
6400 }
6401
6402 #[tracing_test::traced_test]
6406 #[test]
6407 fn test_um1_bounded_fields() {
6408 let payload_sentinel = "um1_payload_sentinel_5b7a9c3d";
6409 let (_sender, _dest) = drive_abandonment_log(payload_sentinel);
6410
6411 let buf = tracing_test::internal::global_buf().lock().unwrap();
6412 let logs = std::str::from_utf8(&buf).expect("logs are utf-8");
6413
6414 assert!(
6416 logs.contains("message_type="),
6417 "UM-1: expected message_type field, got:\n{logs}"
6418 );
6419 assert!(
6420 logs.contains("data_len="),
6421 "UM-1: expected data_len field, got:\n{logs}"
6422 );
6423 assert!(
6426 !logs.contains(payload_sentinel),
6427 "UM-1: payload body leaked into the log:\n{logs}"
6428 );
6429 assert!(
6433 !logs.contains(" headers="),
6434 "UM-1: unbounded headers field leaked into the log:\n{logs}"
6435 );
6436 }
6437
6438 #[tracing_test::traced_test]
6442 #[test]
6443 fn test_um2_compat_fields_preserved() {
6444 let (sender, _dest) = drive_abandonment_log("um2_payload");
6445
6446 let buf = tracing_test::internal::global_buf().lock().unwrap();
6447 let logs = std::str::from_utf8(&buf).expect("logs are utf-8");
6448
6449 let actor_name = sender.log_name();
6452 let actor_id = sender.to_string();
6453 assert!(
6454 logs.contains("actor_name=") && logs.contains(actor_name),
6455 "UM-2: expected actor_name={actor_name} on the log, got:\n{logs}"
6456 );
6457 assert!(
6458 logs.contains("actor_id=") && logs.contains(&actor_id),
6459 "UM-2: expected actor_id={actor_id} on the log, got:\n{logs}"
6460 );
6461 }
6462
6463 #[tracing_test::traced_test]
6465 #[test]
6466 fn test_um3_destination_format() {
6467 let (_sender, dest) = drive_abandonment_log("um3_payload");
6468
6469 let buf = tracing_test::internal::global_buf().lock().unwrap();
6470 let logs = std::str::from_utf8(&buf).expect("logs are utf-8");
6471
6472 assert!(
6473 logs.contains(&format!("message not delivered to {}", dest)),
6474 "UM-3: expected destination-naming format string, got:\n{logs}"
6475 );
6476 }
6477
6478 #[tracing_test::traced_test]
6482 #[test]
6483 fn test_um3b_operation_format_with_operation_context() {
6484 let sender = test_actor_id("um_proc", "um_sender");
6485 let dest = test_port_id("um_dest_proc", "um_dest", 42);
6486
6487 let mut headers = Flattrs::new();
6488 headers.set(
6489 headers::OPERATION_ENDPOINT,
6490 "training.buffer.sample()".to_string(),
6491 );
6492 headers.set(headers::OPERATION_ADVERB, "call_one".to_string());
6493
6494 let envelope = MessageEnvelope::new(
6495 sender,
6496 dest,
6497 wirevalue::Any::serialize(&"um3b_payload".to_string()).unwrap(),
6498 headers,
6499 );
6500
6501 let (return_handle, _rx) = crate::mailbox::undeliverable::new_undeliverable_port();
6502 UndeliverableMailboxSender.post_unchecked(envelope, return_handle);
6503
6504 let buf = tracing_test::internal::global_buf().lock().unwrap();
6505 let logs = std::str::from_utf8(&buf).expect("logs are utf-8");
6506
6507 assert!(
6508 logs.contains("abandoned message for training.buffer.sample()"),
6509 "UM-3b: expected operation-naming format string, got:\n{logs}"
6510 );
6511 assert!(
6512 logs.contains("endpoint=") && logs.contains("training.buffer.sample()"),
6513 "UM-3b: expected endpoint field with the caller's operation, got:\n{logs}"
6514 );
6515 assert!(
6516 logs.contains("adverb=") && logs.contains("call_one"),
6517 "UM-3b: expected adverb field, got:\n{logs}"
6518 );
6519 assert!(
6522 !logs.contains("message not delivered to"),
6523 "UM-3b: unexpected destination-naming format string:\n{logs}"
6524 );
6525 }
6526}