1use std::collections::{BTreeMap, HashMap, HashSet};
2use std::time::Duration;
3
4use futures::FutureExt;
5use mm1_address::address::Address;
6use mm1_address::address_range::AddressRange;
7use mm1_address::pool::Lease;
8use mm1_address::subnet::{NetAddress, NetMask};
9use mm1_common::errors::chain::{ExactTypeDisplayChainExt, StdErrorDisplayChainExt};
10use mm1_common::errors::error_of::ErrorOf;
11use mm1_common::futures::timeout::FutureTimeoutExt;
12use mm1_common::log;
13use mm1_common::types::{AnyError, Never};
14use mm1_core::context::{
15 Bind, BindArgs, BindErrorKind, Fork, ForkErrorKind, InitDone, Linking, Messaging, Now, Ping,
16 PingErrorKind, Quit, RecvErrorKind, SendErrorKind, Start, Stop, Watching,
17};
18use mm1_core::envelope::{Envelope, EnvelopeHeader, dispatch};
19use mm1_core::tap;
20use mm1_core::tracing::{TraceId, WithTraceIdExt};
21use mm1_proto_system as sys;
22use mm1_runnable::local::BoxedRunnable;
23use rand::RngCore;
24use tokio::sync::oneshot;
25use tokio::time::Instant;
26use tracing::{trace, warn};
27
28use crate::config::EffectiveActorConfig;
29use crate::registry::{self, MessageWithPermit, MessageWithoutPermit};
30use crate::runtime::container;
31use crate::runtime::context::{ActorContext, ForkEntry, SubnetContext};
32use crate::runtime::rt_api::RtApi;
33use crate::runtime::sys_call::SysCall;
34use crate::runtime::sys_msg::{ExitReason, SysLink, SysMsg};
35
36impl Quit for ActorContext {
37 async fn quit_ok(&mut self) -> Never {
38 self.call.invoke(SysCall::Exit(Ok(()))).await;
39 std::future::pending().await
40 }
41
42 async fn quit_err<E>(&mut self, reason: E) -> Never
43 where
44 E: std::error::Error + Send + Sync + 'static,
45 {
46 self.call.invoke(SysCall::Exit(Err(reason.into()))).await;
47 std::future::pending().await
48 }
49}
50
51impl Fork for ActorContext {
52 async fn fork(&mut self) -> Result<Self, ErrorOf<ForkErrorKind>> {
53 let Self {
54 fork_address: this_address,
55 call,
56 subnet_context,
57 ..
58 } = self;
59
60 let fork_lease = {
61 let mut subnet_context_locked = subnet_context
62 .try_lock()
63 .expect("could not lock subnet_context");
64 let SubnetContext {
65 subnet_pool,
66 fork_entries,
67 ..
68 } = &mut *subnet_context_locked;
69 let fork_lease = subnet_pool.lease(NetMask::MAX).map_err(|lease_error| {
70 ErrorOf::new(ForkErrorKind::ResourceConstraint, lease_error.to_string())
71 })?;
72 let should_be_none = fork_entries.insert(fork_lease.address, Default::default());
73 assert!(should_be_none.is_none());
74 fork_lease
75 };
76 let fork_address = fork_lease.address;
77 trace!(parent = %this_address, child = %fork_address, "forking");
78
79 let context = Self {
86 subnet_context: subnet_context.clone(),
87 fork_address,
88 fork_lease: Some(fork_lease),
89 ack_to: None,
90 call: call.clone(),
91 };
92
93 call.invoke(SysCall::ForkAdded(fork_address)).await;
94
95 Ok(context)
96 }
97
98 async fn run<F, Fut>(self, fun: F)
99 where
100 F: FnOnce(Self) -> Fut,
101 F: Send + 'static,
102 Fut: Future + Send + 'static,
103 {
104 let call = self.call.clone();
105 let fut = fun(self)
106 .map(|_| ())
107 .with_trace_id(TraceId::current())
108 .boxed();
109 call.invoke(SysCall::Spawn(fut)).await;
110 }
111}
112
113impl Now for ActorContext {
114 type Instant = Instant;
115
116 fn now(&self) -> Self::Instant {
117 Instant::now()
118 }
119}
120
121impl Start<BoxedRunnable<Self>> for ActorContext {
122 fn spawn(
123 &mut self,
124 runnable: BoxedRunnable<Self>,
125 link: bool,
126 ) -> impl Future<Output = Result<Address, ErrorOf<sys::SpawnErrorKind>>> + Send {
127 do_spawn(self, runnable, None, link.then_some(self.fork_address))
128 }
129
130 fn start(
131 &mut self,
132 runnable: BoxedRunnable<Self>,
133 link: bool,
134 start_timeout: Duration,
135 ) -> impl Future<Output = Result<Address, ErrorOf<sys::StartErrorKind>>> + Send {
136 do_start(self, runnable, link, start_timeout)
137 }
138}
139
140impl Stop for ActorContext {
141 fn exit(&mut self, peer: Address) -> impl Future<Output = bool> + Send {
142 let this = self.fork_address;
143 let out = do_exit(self, this, peer).is_ok();
144 std::future::ready(out)
145 }
146
147 fn kill(&mut self, peer: Address) -> impl Future<Output = bool> + Send {
148 let out = do_kill(self, peer).is_ok();
149 std::future::ready(out)
150 }
151}
152
153impl Linking for ActorContext {
154 fn link(&mut self, peer: Address) -> impl Future<Output = ()> + Send {
155 let this = self.fork_address;
156 do_link(self, this, peer)
157 }
158
159 fn unlink(&mut self, peer: Address) -> impl Future<Output = ()> + Send {
160 let this = self.fork_address;
161 do_unlink(self, this, peer)
162 }
163
164 fn set_trap_exit(&mut self, enable: bool) -> impl Future<Output = ()> + Send {
165 do_set_trap_exit(self, enable)
166 }
167}
168
169impl Watching for ActorContext {
170 fn watch(&mut self, peer: Address) -> impl Future<Output = mm1_proto_system::WatchRef> + Send {
171 do_watch(self, peer)
172 }
173
174 fn unwatch(
175 &mut self,
176 watch_ref: mm1_proto_system::WatchRef,
177 ) -> impl Future<Output = ()> + Send {
178 do_unwatch(self, watch_ref)
179 }
180}
181
182impl InitDone for ActorContext {
183 fn init_done(&mut self, address: Address) -> impl Future<Output = ()> + Send {
184 do_init_done(self, address)
185 }
186}
187
188impl Messaging for ActorContext {
189 fn address(&self) -> Address {
190 self.fork_address
191 }
192
193 async fn recv(&mut self) -> Result<Envelope, ErrorOf<RecvErrorKind>> {
194 let ActorContext {
195 fork_address,
196 subnet_context,
197 ..
198 } = self;
199
200 loop {
201 let (inbound_envelope_opt, subnet_notify, fork_notify) = {
202 let mut subnet_context_locked = subnet_context
203 .try_lock()
204 .expect("could not lock subnet_context");
205 let SubnetContext {
206 rt_api,
207 actor_key,
208 subnet_address,
209 subnet_notify,
210 rx_priority,
211 rx_regular,
212 fork_entries,
213 bound_subnets,
214 message_tap,
215 ..
216 } = &mut *subnet_context_locked;
217
218 process_inlets(rt_api, bound_subnets, rx_priority, rx_regular, fork_entries)?;
219
220 let this_fork_entry = fork_entries
221 .get_mut(fork_address)
222 .unwrap_or_else(|| panic!("no fork-entry for {}", fork_address));
223 let inbound_envelope_opt = if let Some(priority_message) =
224 this_fork_entry.inbox_priority.pop_front()
225 {
226 Some(priority_message.message)
227 } else if let Some(regular_message) = this_fork_entry.inbox_regular.pop_front() {
228 Some(regular_message.message)
229 } else {
230 None
231 };
232 if let Some(envelope) = inbound_envelope_opt.as_ref() {
233 message_tap.on_recv(tap::OnRecv {
234 recv_by_addr: *fork_address,
235 recv_by_net: *subnet_address,
236 recv_by_key: actor_key,
237 envelope,
238 });
239 }
240 (
241 inbound_envelope_opt,
242 subnet_notify.clone(),
243 this_fork_entry.fork_notifiy.clone(),
244 )
245 };
246
247 if let Some(inbound_envelope) = inbound_envelope_opt {
248 break Ok(inbound_envelope)
249 } else {
250 let subnet_notified = subnet_notify.notified();
251 let fork_notified = fork_notify.notified();
252
253 tokio::select! {
254 _ = subnet_notified => (),
255 _ = fork_notified => (),
256 }
257 }
258 }
259 }
260
261 async fn close(&mut self) {
262 todo!()
265 }
266
267 fn send(
268 &mut self,
269 envelope: Envelope,
270 ) -> impl Future<Output = Result<(), ErrorOf<SendErrorKind>>> + Send {
271 std::future::ready(do_send(self, envelope))
272 }
273
274 fn forward(
275 &mut self,
276 to: Address,
277 envelope: Envelope,
278 ) -> impl Future<Output = Result<(), ErrorOf<SendErrorKind>>> + Send {
279 std::future::ready(do_forward(self, to, envelope))
280 }
281}
282
283impl Bind<NetAddress> for ActorContext {
284 async fn bind(&mut self, args: BindArgs<NetAddress>) -> Result<(), ErrorOf<BindErrorKind>> {
285 use std::collections::btree_map::Entry::*;
286
287 let BindArgs {
288 bind_to,
289 inbox_size,
290 } = args;
291 let address_range = AddressRange::from(bind_to);
292
293 log::debug!(%bind_to, %inbox_size, "binding");
294
295 let Self {
296 fork_address,
297 subnet_context,
298 ..
299 } = self;
300
301 {
302 let mut subnet_context_locked = subnet_context
303 .try_lock()
304 .expect("could not lock subnet_context");
305
306 let SubnetContext {
307 rt_api,
308 subnet_mailbox_tx,
309 bound_subnets,
310 ..
311 } = &mut *subnet_context_locked;
312
313 let bound_subnet_entry = match bound_subnets.entry(address_range) {
314 Vacant(v) => v,
315 Occupied(o) => {
316 let previously_bound_to = NetAddress::from(*o.key());
317 return Err(ErrorOf::new(
318 BindErrorKind::Conflict,
319 format!("conflict [requested: {bind_to}; existing: {previously_bound_to}]"),
320 ))
321 },
322 };
323 let subnet_lease = Lease::trusted(bind_to);
324
325 let subnet_mailbox_tx = subnet_mailbox_tx.upgrade().ok_or(ErrorOf::new(
326 BindErrorKind::Closed,
327 "the actor subnet is probably unregistered",
328 ))?;
329 let bound_subnet_node =
330 registry::Node::new(subnet_lease, inbox_size, subnet_mailbox_tx);
331
332 rt_api
333 .registry()
334 .register(bind_to, bound_subnet_node)
335 .map_err(|_| {
336 ErrorOf::new(
337 BindErrorKind::Conflict,
338 "could not register the subnet-node",
339 )
340 })?;
341
342 bound_subnet_entry.insert(*fork_address);
343 }
344
345 log::info!(%bind_to, %inbox_size, "bound");
346
347 Ok(())
348 }
349}
350
351impl Ping for ActorContext {
352 async fn ping(
353 &mut self,
354 address: Address,
355 timeout: Duration,
356 ) -> Result<Duration, ErrorOf<PingErrorKind>> {
357 let ping_id = rand::rng().next_u64();
358 let now = Instant::now();
359 let deadline = now.checked_add(timeout).unwrap_or(now);
360
361 let Self {
362 fork_address,
363 subnet_context,
364 ..
365 } = self;
366
367 let (subnet_notify, fork_notify) = {
368 let mut subnet_context_locked = subnet_context
369 .try_lock()
370 .expect("could not lock subnet_context");
371 let SubnetContext {
372 rt_api,
373 fork_entries,
374 subnet_notify,
375 ..
376 } = &mut *subnet_context_locked;
377 let ForkEntry { fork_notifiy, .. } = fork_entries
378 .get_mut(fork_address)
379 .expect("fork_entry missing");
380
381 let ping_msg = sys::Ping {
382 reply_to: Some(*fork_address),
383 id: ping_id,
384 };
385
386 let ping_header = EnvelopeHeader::to_address(address).with_priority(true);
387 let ping_envelope = Envelope::new(ping_header, ping_msg).into_erased();
388 rt_api
389 .send_to(address, true, ping_envelope)
390 .map_err(|e| ErrorOf::new(PingErrorKind::Send, e.to_string()))?;
391
392 (subnet_notify.clone(), fork_notifiy.clone())
393 };
394
395 loop {
396 let timeout = tokio::time::sleep_until(deadline);
397 tokio::select! {
398 _ = timeout => { return Err(ErrorOf::new(PingErrorKind::Timeout, "timeout elapsed")) },
399 _ = subnet_notify.notified() => (),
400 _ = fork_notify.notified() => (),
401 };
402
403 let mut subnet_context_locked = subnet_context
404 .try_lock()
405 .expect("could not lock subnet_context");
406 let SubnetContext {
407 rt_api,
408 fork_entries,
409 bound_subnets,
410 rx_priority,
411 rx_regular,
412 ..
413 } = &mut *subnet_context_locked;
414
415 process_inlets(rt_api, bound_subnets, rx_priority, rx_regular, fork_entries)
416 .map_err(|e| ErrorOf::new(PingErrorKind::Recv, e.to_string()))?;
417
418 if fork_entries
419 .get(fork_address)
420 .expect("fork_entry_missing")
421 .last_ping_received
422 == Some(ping_id)
423 {
424 break
425 }
426 }
427 Ok(now.elapsed())
428 }
429}
430
431fn process_inlets(
432 rt_api: &RtApi,
433 bound_subnets: &BTreeMap<AddressRange, Address>,
434 rx_priority: &mut kanal::Receiver<MessageWithoutPermit<Envelope>>,
435 rx_regular: &mut kanal::Receiver<MessageWithPermit<Envelope>>,
436 fork_entries: &mut HashMap<Address, ForkEntry>,
437) -> Result<(), ErrorOf<RecvErrorKind>> {
438 let mut notified_forks = HashSet::new();
439 while let Some(message) = rx_priority
440 .try_recv_realtime()
441 .map_err(|e| ErrorOf::new(RecvErrorKind::Closed, e.to_string()))?
442 {
443 let message_to = bound_subnets
444 .get(&AddressRange::from(message.to))
445 .copied()
446 .unwrap_or(message.to);
447 let Some(fork_entry) = fork_entries.get_mut(&message_to) else {
448 warn!(dst = %message_to, "no such fork");
449 continue
450 };
451 let should_notify = if let Some(ping_message) = message.message.peek::<sys::Ping>() {
452 let sys::Ping { reply_to, id } = *ping_message;
453 if let Some(reply_to) = reply_to {
454 trace!(%id, %reply_to, "received a ping request");
455 let pong_header = EnvelopeHeader::to_address(reply_to).with_priority(true);
456 let pong_envelope =
457 Envelope::new(pong_header, sys::Ping { reply_to: None, id }).into_erased();
458 rt_api
459 .send_to(reply_to, true, pong_envelope)
460 .inspect_err(
461 |e| warn!(reason = %e.as_display_chain(), "can't send ping-response"),
462 )
463 .ok();
464 trace!(%id, %reply_to, "send a ping response");
465 false
466 } else {
467 trace!(%id, "received a ping response");
468 fork_entry.last_ping_received = Some(id);
469 true
470 }
471 } else {
472 fork_entry.inbox_priority.push_back(message);
473 true
474 };
475
476 if should_notify && notified_forks.insert(message_to) {
477 trace!(dst = %message_to, "notifying fork");
478 fork_entry.fork_notifiy.notify_one();
479 }
480 }
481
482 while let Some(message) = rx_regular
483 .try_recv_realtime()
484 .map_err(|e| ErrorOf::new(RecvErrorKind::Closed, e.to_string()))?
485 {
486 let message_to = bound_subnets
487 .get(&AddressRange::from(message.to))
488 .copied()
489 .unwrap_or(message.to);
490 let Some(fork_entry) = fork_entries.get_mut(&message_to) else {
491 warn!(dst = %message_to, "no such fork");
492 continue
493 };
494 fork_entry.inbox_regular.push_back(message);
495 trace!(dst = %message_to, "subnet received regular message");
496 if notified_forks.insert(message_to) {
497 trace!(dst = %message_to, "notifying fork");
498 fork_entry.fork_notifiy.notify_one();
499 }
500 }
501
502 Ok(())
503}
504
505async fn do_start(
506 context: &mut ActorContext,
507 runnable: BoxedRunnable<ActorContext>,
508 link: bool,
509 timeout: Duration,
510) -> Result<Address, ErrorOf<sys::StartErrorKind>> {
511 let this_address = context.fork_address;
512
513 let mut fork = context
514 .fork()
515 .await
516 .map_err(|e| ErrorOf::new(sys::StartErrorKind::InternalError, e.to_string()))?;
517
518 let fork_address = fork.fork_address;
519 let spawned_address = do_spawn(&mut fork, runnable, Some(fork_address), Some(fork_address))
520 .await
521 .map_err(|e| e.map_kind(sys::StartErrorKind::Spawn))?;
522
523 let envelope = match fork.recv().timeout(timeout).await {
524 Err(_elapsed) => {
525 do_kill(context, spawned_address)
526 .map_err(|e| ErrorOf::new(sys::StartErrorKind::InternalError, e.to_string()))?;
527
528 return Err(ErrorOf::new(
531 sys::StartErrorKind::Timeout,
532 "no init-ack within timeout",
533 ))
534 },
535 Ok(recv_result) => {
536 recv_result
537 .map_err(|e| ErrorOf::new(sys::StartErrorKind::InternalError, e.to_string()))?
538 },
539 };
540
541 dispatch!(match envelope {
542 sys::InitAck { address } => {
543 do_unlink(&mut fork, fork_address, address).await;
548 if link {
549 do_link(context, this_address, address).await;
550 }
551 Ok(address)
552 },
553
554 sys::Exited { .. } => {
555 Err(ErrorOf::new(
556 sys::StartErrorKind::Exited,
557 "exited before init-ack",
558 ))
559 },
560
561 unexpected @ _ => {
562 Err(ErrorOf::new(
563 sys::StartErrorKind::InternalError,
564 format!("unexpected message: {unexpected:?}"),
565 ))
566 },
567 })
568}
569
570async fn do_spawn(
571 context: &mut ActorContext,
572 runnable: BoxedRunnable<ActorContext>,
573 ack_to: Option<Address>,
574 link_to: impl IntoIterator<Item = Address>,
575) -> Result<Address, ErrorOf<sys::SpawnErrorKind>> {
576 let ActorContext { subnet_context, .. } = context;
577
578 let (actor_key, rt_config, rt_api, tx_actor_failure) = {
579 let subnet_context_locked = subnet_context
580 .try_lock()
581 .expect("could not lock subnet_context");
582 let SubnetContext {
583 rt_api,
584 rt_config,
585 actor_key,
586 tx_actor_failure,
587 ..
588 } = &*subnet_context_locked;
589
590 (
591 actor_key.child(runnable.func_name()),
592 rt_config.clone(),
593 rt_api.clone(),
594 tx_actor_failure.clone(),
595 )
596 };
597
598 let actor_config = rt_config.actor_config(&actor_key);
599 let execute_on = rt_api.choose_executor(actor_config.runtime_key());
600 let message_tap = rt_api.message_tap(actor_config.message_tap_key());
601 let spawn_permit = rt_api.reserve_spawn().ok_or_else(|| {
602 ErrorOf::new(
603 sys::SpawnErrorKind::ResourceConstraint,
604 "node shutdown is in progress",
605 )
606 })?;
607
608 trace!(?ack_to, "starting");
609
610 let subnet_lease = rt_api
611 .request_address(actor_config.netmask())
612 .await
613 .inspect_err(|e| log::error!("lease-error: {}", e))
614 .map_err(|e| ErrorOf::new(sys::SpawnErrorKind::ResourceConstraint, e.to_string()))?;
615
616 trace!(subnet_lease = %subnet_lease.net_address(), "subnet leased");
617
618 let rt_api = rt_api.clone();
619 let rt_config = rt_config.clone();
620 let container = container::Container::create(
621 container::ContainerArgs {
622 ack_to,
623 link_to: link_to.into_iter().collect(),
625 actor_key,
626 trace_id: TraceId::current(),
627
628 subnet_lease,
629 rt_api,
630 rt_config,
631 message_tap,
632 tx_actor_failure: tx_actor_failure.clone(),
633 },
634 runnable,
635 )
636 .map_err(|e| ErrorOf::new(sys::SpawnErrorKind::InternalError, e.to_string()))?;
637 let actor_address = container.actor_address();
638
639 trace!(spawned_address = %actor_address, "about to run spawned actor");
640
641 let tx_actor_failure = tx_actor_failure.clone();
642 spawn_permit
643 .spawn_on(actor_address, execute_on, async move {
644 match container.run().await {
645 Ok(Ok(())) => (),
646 Ok(Err(actor_failure)) => {
647 let _ = tx_actor_failure.send((actor_address, actor_failure));
648 },
649 Err(container_failure) => {
650 let report = AnyError::from(container_failure);
651 log::error!(
652 err = %report.as_display_chain(), %actor_address,
653 "actor container failure"
654 );
655 },
656 }
657 })
658 .map_err(|_| {
659 ErrorOf::new(
660 sys::SpawnErrorKind::ResourceConstraint,
661 "node shutdown is in progress",
662 )
663 })?;
664
665 Ok(actor_address)
666}
667
668fn do_exit(context: &mut ActorContext, this: Address, peer: Address) -> Result<(), SendErrorKind> {
669 let ActorContext { subnet_context, .. } = context;
670 let subnet_context_locked = subnet_context
671 .try_lock()
672 .expect("could not lock subnet_context");
673 let SubnetContext { rt_api, .. } = &*subnet_context_locked;
674
675 rt_api.sys_send(
676 peer,
677 SysMsg::Link(SysLink::Exit {
678 sender: this,
679 receiver: peer,
680 reason: ExitReason::Terminate,
681 }),
682 )
683}
684
685fn do_kill(context: &mut ActorContext, peer: Address) -> Result<(), SendErrorKind> {
686 let ActorContext { subnet_context, .. } = context;
687 let subnet_context_locked = subnet_context
688 .try_lock()
689 .expect("could not lock subnet_context");
690 let SubnetContext { rt_api, .. } = &*subnet_context_locked;
691
692 rt_api.sys_send(peer, SysMsg::Kill)
693}
694
695async fn do_link(context: &mut ActorContext, this: Address, peer: Address) {
696 let ActorContext { call, .. } = context;
697
698 call.invoke(SysCall::Link {
699 sender: this,
700 receiver: peer,
701 })
702 .await
703}
704
705async fn do_unlink(context: &mut ActorContext, this: Address, peer: Address) {
706 let ActorContext { call, .. } = context;
707 call.invoke(SysCall::Unlink {
708 sender: this,
709 receiver: peer,
710 })
711 .await
712}
713
714async fn do_set_trap_exit(context: &mut ActorContext, enable: bool) {
715 let ActorContext { call, .. } = context;
716 call.invoke(SysCall::TrapExit(enable)).await;
717}
718
719async fn do_watch(context: &mut ActorContext, peer: Address) -> sys::WatchRef {
720 let ActorContext {
721 fork_address: this,
722 call,
723 ..
724 } = context;
725 let (reply_tx, reply_rx) = oneshot::channel();
726 call.invoke(SysCall::Watch {
727 sender: *this,
728 receiver: peer,
729 reply_tx,
730 })
731 .await;
732 reply_rx.await.expect("sys-call remained unanswered")
733}
734
735async fn do_unwatch(context: &mut ActorContext, watch_ref: sys::WatchRef) {
736 let ActorContext {
737 fork_address: this,
738 call,
739 ..
740 } = context;
741 call.invoke(SysCall::Unwatch {
742 sender: *this,
743 watch_ref,
744 })
745 .await;
746}
747
748async fn do_init_done(context: &mut ActorContext, address: Address) {
749 let ActorContext {
750 ack_to,
751 subnet_context,
752 ..
753 } = context;
754 let message = sys::InitAck { address };
755 let Some(ack_to_address) = ack_to.take() else {
756 return;
757 };
758 let envelope = Envelope::new(EnvelopeHeader::to_address(ack_to_address), message);
759 let subnet_context_locked = subnet_context
760 .try_lock()
761 .expect("could not lock subnet_context");
762 let SubnetContext { rt_api, .. } = &*subnet_context_locked;
763 let _ = rt_api.send_to(envelope.header().to, true, envelope.into_erased());
764}
765
766fn do_send(context: &mut ActorContext, outbound: Envelope) -> Result<(), ErrorOf<SendErrorKind>> {
767 let sender = context.fork_address;
768 let to = outbound.header().to;
769 {
770 let subnet_context_locked = context
771 .subnet_context
772 .try_lock()
773 .expect("could not lock subnet_context");
774 let SubnetContext {
775 subnet_address,
776 actor_key,
777 message_tap,
778 ..
779 } = &*subnet_context_locked;
780 message_tap.on_send(tap::OnSend {
781 sent_by_addr: sender,
782 sent_by_net: *subnet_address,
783 sent_by_key: actor_key,
784 sent_to_addr: to,
785 envelope: &outbound,
786 });
787 }
788
789 let (message, empty_envelope) = outbound.take();
790 let mut header: EnvelopeHeader = empty_envelope.into();
791
792 let new_ttl = header.ttl.checked_sub(1).ok_or_else(|| {
793 warn!(
794 envelope_header = ?header,
795 "TTL exhausted, dropping message"
796 );
797 ErrorOf::new(SendErrorKind::TtlExhausted, "TTL exhausted")
798 })?;
799 header.ttl = new_ttl;
800
801 let outbound = Envelope::new(header, message);
802
803 trace!(envelope = ?outbound, "sending");
804 let ActorContext { subnet_context, .. } = context;
805 let subnet_context_locked = subnet_context
806 .try_lock()
807 .expect("could not lock subnet_context");
808 let SubnetContext { rt_api, .. } = &*subnet_context_locked;
809 rt_api
810 .send_to(outbound.header().to, outbound.header().priority, outbound)
811 .map_err(|k| ErrorOf::new(k, ""))
812}
813
814fn do_forward(
815 context: &mut ActorContext,
816 to: Address,
817 outbound: Envelope,
818) -> Result<(), ErrorOf<SendErrorKind>> {
819 let sender = context.fork_address;
820 {
821 let subnet_context_locked = context
822 .subnet_context
823 .try_lock()
824 .expect("could not lock subnet_context");
825 let SubnetContext {
826 subnet_address,
827 actor_key,
828 message_tap,
829 ..
830 } = &*subnet_context_locked;
831 message_tap.on_send(tap::OnSend {
832 sent_by_addr: sender,
833 sent_by_net: *subnet_address,
834 sent_by_key: actor_key,
835 sent_to_addr: to,
836 envelope: &outbound,
837 });
838 }
839
840 let (message, empty_envelope) = outbound.take();
841 let mut header: EnvelopeHeader = empty_envelope.into();
842
843 let new_ttl = header.ttl.checked_sub(1).ok_or_else(|| {
844 warn!(
845 forward_to = %to,
846 envelope_header = ?header,
847 "TTL exhausted, dropping message"
848 );
849 ErrorOf::new(SendErrorKind::TtlExhausted, "TTL exhausted")
850 })?;
851 header.ttl = new_ttl;
852
853 let outbound = Envelope::new(header, message);
854
855 trace!(forward_to = %to, envelope = ?outbound, "forwarding");
856 let ActorContext { subnet_context, .. } = context;
857 let subnet_context_locked = subnet_context
858 .try_lock()
859 .expect("could not lock subnet_context");
860 let SubnetContext { rt_api, .. } = &*subnet_context_locked;
861 rt_api
862 .send_to(to, outbound.header().priority, outbound)
863 .map_err(|k| ErrorOf::new(k, ""))
864}