Files
smarm/examples/named_genserver.rs

70 lines
2.2 KiB
Rust

//! Addressing a gen_server by a durable name.
//!
//! A gen_server is multi-message (call / cast over one inbox), so it is named
//! by the *server* type rather than by a single message type. Registering it
//! under a [`GenServerName`] lets clients `call` and `cast` by name, resolving on
//! every use — so the address keeps working across a supervised restart, with
//! no stale [`GenServerRef`] to refresh.
use smarm::{call, cast, run, whereis_server, GenServer, GenServerBuilder, GenServerName, GenServerRef};
/// A counter server: synchronous `Get`, asynchronous `Inc` / `Add`.
struct Counter {
n: u64,
}
enum Query {
Get,
}
enum Update {
Inc,
Add(u64),
}
impl GenServer for Counter {
type Call = Query;
type Reply = u64;
type Cast = Update;
type Info = ();
type Timer = ();
fn handle_call(&mut self, q: Query) -> u64 {
match q {
Query::Get => self.n,
}
}
fn handle_cast(&mut self, u: Update) {
match u {
Update::Inc => self.n += 1,
Update::Add(k) => self.n += k,
}
}
}
/// A durable name typed by the server, so by-name `call` / `cast` check against
/// `Counter`'s `Call` / `Cast` / `Reply`.
const COUNTER: GenServerName<Counter> = GenServerName::new("counter");
fn main() {
run(|| {
// Start the server and bind its name in one step. A named start is
// fallible: the name may already be held by another live server.
GenServerBuilder::new(Counter { n: 0 })
.named(COUNTER)
.start()
.unwrap();
// Address it purely by name. Each call/cast resolves through the
// registry, so a server restarted under the same name is reached
// transparently.
cast(COUNTER, Update::Inc).unwrap();
cast(COUNTER, Update::Add(41)).unwrap();
assert_eq!(call(COUNTER, Query::Get).unwrap(), 42);
// When you want a handle to hold or pass on rather than resolve per
// call, recover a typed `GenServerRef` from the name.
let svc: Option<GenServerRef<Counter>> = whereis_server(COUNTER);
if let Some(svc) = svc {
let _ = svc.call(Query::Get);
}
});
}