near_async/multithread/
runtime_handle.rs1use crate::instrumentation::queue::InstrumentedQueue;
2use crate::instrumentation::writer::InstrumentedThreadWriterSharedPart;
3use crate::messaging::Actor;
4use crate::pretty_type_name;
5use std::sync::Arc;
6use std::thread;
7use std::time::Duration;
8
9pub(super) struct MultithreadRuntimeMessage<A> {
12 pub(super) seq: u64,
13 pub(super) enqueued_time_ns: u64,
14 pub(super) name: &'static str,
15 pub(super) function: Box<dyn FnOnce(&mut A) + Send>,
16}
17
18pub struct MultithreadRuntimeHandle<A> {
21 pub(super) sender: crossbeam_channel::Sender<MultithreadRuntimeMessage<A>>,
22 cancellation_signal_holder: Option<crossbeam_channel::Sender<()>>,
26 pub(super) instrumentation: Arc<InstrumentedThreadWriterSharedPart>,
27}
28
29impl<A> Clone for MultithreadRuntimeHandle<A> {
30 fn clone(&self) -> Self {
31 Self {
32 sender: self.sender.clone(),
33 cancellation_signal_holder: self.cancellation_signal_holder.clone(),
34 instrumentation: self.instrumentation.clone(),
35 }
36 }
37}
38
39impl<A> MultithreadRuntimeHandle<A>
40where
41 A: 'static,
42{
43 pub fn sender(&self) -> Arc<MultithreadRuntimeHandle<A>> {
44 Arc::new(self.clone())
45 }
46}
47
48impl<A> MultithreadRuntimeHandle<A> {
49 pub(super) fn send_message(
50 &self,
51 message: MultithreadRuntimeMessage<A>,
52 ) -> Result<(), crossbeam_channel::SendError<MultithreadRuntimeMessage<A>>> {
53 let name = message.name;
54 self.sender.send(message).map(|_| {
55 self.instrumentation.queue().enqueue(name);
57 })
58 }
59}
60
61pub(crate) fn spawn_multithread_actor<A>(
67 num_threads: usize,
68 make_actor_fn: impl Fn() -> A + Sync + Send + 'static,
69 cancellation_signal: crossbeam_channel::Receiver<()>,
70 cancellation_signal_holder: Option<crossbeam_channel::Sender<()>>,
71) -> MultithreadRuntimeHandle<A>
72where
73 A: Actor + Send + 'static,
74{
75 let actor_name = pretty_type_name::<A>();
76 tracing::debug!(
77 target: "multithread_runtime",
78 actor_name,
79 num_threads,
80 "starting multithread actor",
81 );
82 let (sender, receiver) = crossbeam_channel::unbounded::<MultithreadRuntimeMessage<A>>();
83 let instrumented_queue = InstrumentedQueue::new(actor_name);
84 let shared_instrumentation =
85 InstrumentedThreadWriterSharedPart::new(actor_name.to_string(), instrumented_queue.clone());
86 let handle = MultithreadRuntimeHandle {
87 sender,
88 cancellation_signal_holder,
89 instrumentation: shared_instrumentation,
90 };
91 let make_actor_fn = Arc::new(make_actor_fn);
92
93 for thread_id in 0..num_threads {
95 let receiver = receiver.clone();
96 let cancellation_signal = cancellation_signal.clone();
97 let instrumented_queue = instrumented_queue.clone();
98 let handle = handle.clone();
99 let make_actor_fn = make_actor_fn.clone();
100
101 thread::spawn(move || {
102 let mut instrumentation =
103 handle.instrumentation.new_writer_with_global_registration(Some(thread_id));
104 let mut actor = make_actor_fn();
105 let window_update_ticker = crossbeam_channel::tick(Duration::from_secs(1));
106 loop {
107 crossbeam_channel::select! {
108 recv(cancellation_signal) -> _ => {
109 tracing::info!(target: "multithread_runtime", actor_name, thread_id, "cancellation received, exiting loop");
110 return;
111 }
112 recv(window_update_ticker) -> _ => {
113 tracing::trace!(target: "multithread_runtime", actor_name, thread_id, "updating instrumentation window");
114 instrumentation.advance_window_if_needed();
115 }
116 recv(receiver) -> message => {
117 let Ok(message) = message else {
118 tracing::warn!(target: "multithread_runtime", actor_name, thread_id, "message queue closed, exiting event loop");
119 return;
120 };
121 instrumented_queue.dequeue(message.name);
122 let seq = message.seq;
123 let dequeue_time_ns = handle.instrumentation.current_time().saturating_sub(message.enqueued_time_ns);
124 instrumentation.start_event(message.name, dequeue_time_ns);
125 tracing::trace!(target: "multithread_runtime", seq, "executing message");
126 (message.function)(&mut actor);
127 instrumentation.end_event(message.name);
128 }
129 }
130 }
131 });
132 }
133 handle
134}