use anyhow::Context;
use std::{convert::Infallible, num::NonZeroUsize, time::Duration};
use tellus::{
Actor, ActorConfig, ActorContext, ActorSystem, Control, Incoming, MailboxCapacity, ReplyTo,
};
const FLOOD_MESSAGES: usize = 100_000;
const ASK_REQUESTS: usize = 10_000;
const ASK_TIMEOUT: Duration = Duration::from_secs(1);
#[tokio::main]
#[cfg_attr(feature = "hotpath", hotpath::main)]
async fn main() -> anyhow::Result<()> {
flood().await?;
ask_round_trips().await
}
async fn flood() -> anyhow::Result<()> {
let capacity = NonZeroUsize::new(FLOOD_MESSAGES).context("flood message count is zero")?;
let config = ActorConfig::default().with_mailbox_capacity(MailboxCapacity::Bounded(capacity));
let system = ActorSystem::with_config(
Countdown {
messages: FLOOD_MESSAGES,
},
config,
);
for _ in 0..FLOOD_MESSAGES {
system.root().tell(Tick);
}
system
.terminated()
.await
.context("awaiting flood termination")
}
async fn ask_round_trips() -> anyhow::Result<()> {
let system = ActorSystem::new(Echo {
requests: ASK_REQUESTS,
});
for _ in 0..ASK_REQUESTS {
system
.root()
.ask(ASK_TIMEOUT, Request)
.await
.context("asking echo actor")?;
}
system
.terminated()
.await
.context("awaiting echo termination")
}
fn next_control(remaining: usize) -> Control<usize> {
remaining
.checked_sub(1)
.filter(|n| *n > 0)
.map_or(Control::Stop, Control::Continue)
}
struct Tick;
struct Countdown {
messages: usize,
}
impl Actor for Countdown {
type Message = Tick;
type State = usize;
type Error = Infallible;
fn init(&self, _: &ActorContext<Self::Message>) -> Result<Self::State, Self::Error> {
Ok(self.messages)
}
fn receive(
&self,
_: &ActorContext<Self::Message>,
_: Incoming<Self::Message>,
remaining: Self::State,
) -> Result<Control<Self::State>, Self::Error> {
Ok(next_control(remaining))
}
}
struct Request(ReplyTo<usize>);
struct Echo {
requests: usize,
}
impl Actor for Echo {
type Message = Request;
type State = usize;
type Error = Infallible;
fn init(&self, _: &ActorContext<Self::Message>) -> Result<Self::State, Self::Error> {
Ok(self.requests)
}
fn receive(
&self,
_: &ActorContext<Self::Message>,
incoming: Incoming<Self::Message>,
remaining: Self::State,
) -> Result<Control<Self::State>, Self::Error> {
if let Incoming::Message(Request(reply_to)) = incoming {
reply_to.reply(remaining);
}
Ok(next_control(remaining))
}
}