Added desgin doc and goals. Got a reference implementation that kimi spat out, looks like something from hardware/embedded guys, which is a great sign.
164 lines
No EOL
3.8 KiB
Rust
164 lines
No EOL
3.8 KiB
Rust
pub mod error;
|
|
|
|
use std::{
|
|
marker::PhantomData,
|
|
sync::{Mutex, mpsc::TryRecvError},
|
|
};
|
|
|
|
use crate::error::{Error, Result, convert_err};
|
|
|
|
use std::sync::mpsc;
|
|
|
|
use bytemuck::{Pod, Zeroable};
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
|
struct GreeterState {
|
|
pub num_greeted: usize,
|
|
}
|
|
|
|
enum GreetMessage {
|
|
Name(String),
|
|
}
|
|
|
|
enum GreetResponse {
|
|
Greeting(String),
|
|
}
|
|
|
|
type GreeterId = u64;
|
|
|
|
struct Greeter {
|
|
id: GreeterId,
|
|
inbox: mpsc::Receiver<GreetMessage>,
|
|
outbox: mpsc::Sender<GreetResponse>,
|
|
state: GreeterState,
|
|
}
|
|
|
|
impl Greeter {
|
|
pub fn new(
|
|
id: GreeterId,
|
|
inbox: mpsc::Receiver<GreetMessage>,
|
|
outbox: mpsc::Sender<GreetResponse>,
|
|
) -> Self {
|
|
Self {
|
|
id,
|
|
inbox,
|
|
outbox,
|
|
state: GreeterState { num_greeted: 0 },
|
|
}
|
|
}
|
|
|
|
pub fn id(&self) -> GreeterId {
|
|
self.id
|
|
}
|
|
|
|
pub fn process_message(&mut self) -> Result<()> {
|
|
match self.inbox.try_recv() {
|
|
Ok(m) => {
|
|
let GreetMessage::Name(n) = m;
|
|
self.outbox
|
|
.send(GreetResponse::Greeting(format!("Hello, {n}!")))
|
|
.map_err(convert_err)?;
|
|
self.state.num_greeted += 1;
|
|
}
|
|
Err(e) => match e {
|
|
TryRecvError::Empty => return Ok(()),
|
|
TryRecvError::Disconnected => {
|
|
return Err("Outbox has been disconnected, actor in an improper state".into());
|
|
}
|
|
},
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
use std::collections::HashMap;
|
|
|
|
struct Router {
|
|
address_book: HashMap<GreeterId, mpsc::Sender<GreetMessage>>,
|
|
next_id: GreeterId,
|
|
}
|
|
|
|
impl Router {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
address_book: HashMap::new(),
|
|
next_id: 0,
|
|
}
|
|
}
|
|
|
|
pub fn register(&mut self, sender: mpsc::Sender<GreetMessage>) -> GreeterId {
|
|
let id = self.next_id;
|
|
self.next_id += 1;
|
|
self.address_book.insert(id, sender);
|
|
id
|
|
}
|
|
|
|
pub fn unregister(&mut self, id: GreeterId) -> Option<mpsc::Sender<GreetMessage>> {
|
|
self.address_book.remove(&id)
|
|
}
|
|
|
|
pub fn get_sender(&self, id: GreeterId) -> Option<&mpsc::Sender<GreetMessage>> {
|
|
self.address_book.get(&id)
|
|
}
|
|
|
|
pub fn send(&self, id: GreeterId, message: GreetMessage) -> Result<()> {
|
|
match self.address_book.get(&id) {
|
|
Some(sender) => sender.send(message).map_err(convert_err),
|
|
None => Err(format!("No sender found for id {}", id).into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Runtime {
|
|
router: Router,
|
|
greeters: Vec<Greeter>,
|
|
response_rx: mpsc::Receiver<GreetResponse>,
|
|
response_tx: mpsc::Sender<GreetResponse>,
|
|
}
|
|
|
|
impl Runtime {
|
|
pub fn new() -> Self {
|
|
let (response_tx, response_rx) = mpsc::channel();
|
|
Self {
|
|
router: Router::new(),
|
|
greeters: Vec::new(),
|
|
response_rx,
|
|
response_tx,
|
|
}
|
|
}
|
|
|
|
pub fn spawn_greeter(&mut self) -> GreeterId {
|
|
let (inbox_tx, inbox_rx) = mpsc::channel();
|
|
let id = self.router.register(inbox_tx);
|
|
let greeter = Greeter::new(id, inbox_rx, self.response_tx.clone());
|
|
self.greeters.push(greeter);
|
|
id
|
|
}
|
|
|
|
pub fn send_message(&self, id: GreeterId, message: GreetMessage) -> Result<()> {
|
|
self.router.send(id, message)
|
|
}
|
|
|
|
pub fn tick(&mut self) -> Result<()> {
|
|
for greeter in &mut self.greeters {
|
|
greeter.process_message()?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn try_recv_response(&self) -> Option<GreetResponse> {
|
|
self.response_rx.try_recv().ok()
|
|
}
|
|
|
|
pub fn run_until_idle(&mut self) -> Result<()> {
|
|
loop {
|
|
self.tick()?;
|
|
if self.response_rx.try_recv().is_err() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
} |