use std::fmt::Debug;
use futures::FutureExt;
use futures::future::BoxFuture;
use crate::futures::{DelayedActionRunner, FutureSpawner};
use crate::instrumentation::InstrumentedThreadWriterSharedPart;
use crate::messaging::{AsyncSendError, CanSend, CanSendAsync, HandlerWithContext};
use crate::tokio::runtime_handle::{TokioRuntimeHandle, TokioRuntimeMessage};
use crate::{next_message_sequence_num, pretty_type_name};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
impl<A, M> CanSend<M> for TokioRuntimeHandle<A>
where
A: HandlerWithContext<M> + 'static,
M: Debug + Send + 'static,
{
fn send(&self, message: M) {
let seq = next_message_sequence_num();
let message_type = pretty_type_name::<M>();
tracing::trace!(target: "tokio_runtime", seq, message_type, ?message, "sending sync message");
let function = |actor: &mut A, ctx: &mut dyn DelayedActionRunner<A>| {
actor.handle(message, ctx);
};
let message = TokioRuntimeMessage {
seq,
enqueued_time_ns: self.instrumentation.current_time(),
name: message_type,
function: Box::new(function),
};
if let Err(_) = self.send_message(message) {
tracing::info!(target: "tokio_runtime", seq, "Ignoring sync message, receiving actor is being shut down");
}
}
}
impl<A, M, R> CanSendAsync<M, R> for TokioRuntimeHandle<A>
where
A: HandlerWithContext<M, R> + 'static,
M: Debug + Send + 'static,
R: Send + 'static,
{
fn send_async(&self, message: M) -> BoxFuture<'static, Result<R, AsyncSendError>> {
let seq = next_message_sequence_num();
let message_type = pretty_type_name::<M>();
tracing::trace!(target: "tokio_runtime", seq, message_type, ?message, "sending async message");
let (sender, receiver) = tokio::sync::oneshot::channel();
let future = async move { receiver.await.map_err(|_| AsyncSendError::Dropped) };
let function = move |actor: &mut A, ctx: &mut dyn DelayedActionRunner<A>| {
let result = actor.handle(message, ctx);
sender.send(result).ok(); };
let message = TokioRuntimeMessage {
seq,
enqueued_time_ns: self.instrumentation.current_time(),
name: message_type,
function: Box::new(function),
};
if let Err(_) = self.send_message(message) {
async { Err(AsyncSendError::Dropped) }.boxed()
} else {
future.boxed()
}
}
}
impl<A> FutureSpawner for TokioRuntimeHandle<A> {
fn spawn_boxed(&self, description: &'static str, f: BoxFuture<'static, ()>) {
tracing::debug!(target: "tokio_runtime", description, "spawning future");
self.runtime_handle.spawn(InstrumentingFuture::new(
description,
f,
self.instrumentation.clone(),
));
}
}
impl<A> DelayedActionRunner<A> for TokioRuntimeHandle<A>
where
A: 'static,
{
fn run_later_boxed(
&mut self,
name: &'static str,
dur: near_time::Duration,
f: Box<dyn FnOnce(&mut A, &mut dyn DelayedActionRunner<A>) + Send + 'static>,
) {
let seq = next_message_sequence_num();
tracing::debug!(target: "tokio_runtime", seq, name, "sending delayed action");
let handle = self.clone();
self.runtime_handle.spawn(async move {
tokio::time::sleep(dur.unsigned_abs()).await;
let function = move |actor: &mut A, ctx: &mut dyn DelayedActionRunner<A>| f(actor, ctx);
let message = TokioRuntimeMessage {
seq,
enqueued_time_ns: handle.instrumentation.current_time(),
name,
function: Box::new(function),
};
handle.send_message(message).ok();
});
}
}
struct InstrumentingFuture {
description: &'static str,
future: futures::future::BoxFuture<'static, ()>,
instrumentation: Arc<InstrumentedThreadWriterSharedPart>,
}
impl InstrumentingFuture {
pub fn new(
description: &'static str,
future: futures::future::BoxFuture<'static, ()>,
shared_instrumentation: Arc<InstrumentedThreadWriterSharedPart>,
) -> Self {
shared_instrumentation.queue().enqueue(description);
Self { description, future, instrumentation: shared_instrumentation }
}
}
impl Future for InstrumentingFuture {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.instrumentation.with_thread_local_writer(|writer| {
writer.start_event(
self.description,
0,
)
});
let result = Pin::new(&mut self.future).poll(cx);
self.instrumentation.with_thread_local_writer(|writer| writer.end_event(&self.description));
if let Poll::Ready(()) = result {
self.instrumentation.queue().dequeue(self.description);
}
result
}
}