use crate::{acto_cell::ActoCell, ActoHandle, ActoId, ActoRef, Receiver, Sender, SupervisionRef};
use smol_str::SmolStr;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
pub trait ActoRuntime: Clone + Send + Sync + 'static {
type ActoHandle<O: Send + 'static>: ActoHandle<Output = O>;
type Sender<M: Send + 'static>: Sender<M>;
type Receiver<M: Send + 'static>: Receiver<M>;
fn name(&self) -> &str;
fn mailbox<M: Send + 'static>(&self) -> (Self::Sender<M>, Self::Receiver<M>);
fn spawn_task<T>(&self, id: ActoId, name: SmolStr, task: T) -> Self::ActoHandle<T::Output>
where
T: Future + Send + 'static,
T::Output: Send + 'static;
fn spawn_actor<M, F, Fut, S>(
&self,
name: &str,
actor: F,
) -> SupervisionRef<M, Self::ActoHandle<Fut::Output>>
where
M: Send + 'static,
F: FnOnce(ActoCell<M, Self, S>) -> Fut,
Fut: Future + Send + 'static,
Fut::Output: Send + 'static,
S: Send + 'static,
{
let (sender, recv) = self.mailbox();
let id = ActoId::next();
let mut id_str = [0u8; 16];
let id_str = write_id(&mut id_str, id);
let name = [name, "(", self.name(), "/", id_str, ")"]
.into_iter()
.collect::<SmolStr>();
let me = ActoRef::new(id, name.clone(), sender);
let ctx = ActoCell::new(me.clone(), self.clone(), recv);
let _span = tracing::debug_span!("creating", actor = %name).entered();
tracing::trace!("create");
let task = LoggingTask::new(name.clone(), (actor)(ctx));
let join = self.spawn_task(id, name, task);
SupervisionRef { me, handle: join }
}
}
pin_project_lite::pin_project! {
struct LoggingTask<F> {
name: SmolStr,
#[pin]
future: F,
}
}
impl<F> LoggingTask<F> {
pub fn new(name: SmolStr, future: F) -> Self {
Self { name, future }
}
}
impl<F> Future for LoggingTask<F>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let _span = tracing::debug_span!("poll", actor = %this.name).entered();
this.future.poll(cx)
}
}
fn write_id(buf: &mut [u8; 16], id: ActoId) -> &str {
let id = id.0;
if id == 0 {
return "0";
}
let mut written = 0;
let mut shift = (id.ilog2() & !3) + 4;
while shift != 0 {
shift -= 4;
const HEX: [u8; 16] = *b"0123456789abcdef";
buf[written] = HEX[(id >> shift) & 15];
written += 1;
}
unsafe { std::str::from_utf8_unchecked(&buf[..written]) }
}
#[test]
fn test_write_id() {
let mut buf = [0u8; 16];
for i in 0..1000000 {
assert_eq!(write_id(&mut buf, ActoId(i)), &format!("{:x}", i));
}
#[cfg(target_pointer_width = "64")]
assert_eq!(write_id(&mut buf, ActoId(usize::MAX)), "ffffffffffffffff");
#[cfg(target_pointer_width = "32")]
assert_eq!(write_id(&mut buf, ActoId(usize::MAX)), "ffffffff");
}
pub trait MailboxSize: ActoRuntime {
type Output: ActoRuntime;
fn with_mailbox_size(&self, mailbox_size: usize) -> Self::Output;
}