Crates.io | with_daemon |
lib.rs | with_daemon |
version | 0.2.0 |
source | src |
created_at | 2024-09-09 17:27:54.219543 |
updated_at | 2024-09-14 21:33:09.597539 |
description | An async client-daemon abstraction framework |
homepage | |
repository | https://github.com/komar007/with_daemon |
max_upload_size | |
id | 1369546 |
size | 83,380 |
with_daemon
- An async client-daemon abstraction frameworkThis crate abstracts away the spawning of and connecting to a daemon required to optimize tasks performed by multiple client instances that are separate processes.
The daemon runs in a separate detached process from the first time the client is used and provides functionality to multiple instances of the client, taking advantage of the ability to have a common state shared between client handlers.
An example is worth more than a hundred words:
//! `with_daemon` example: a simple global counter
//!
//! This example demonstrates how to use `with_daemon` in the most basic way.
//!
//! The daemon keeps track of a counter and each call to the client returns the current counter
//! value and increments the counter.
//!
//! When the compiled binary is executed, it spawns the daemon if it's not running yet, and
//! receives the current value from the daemon. This way each execution of the program provides a
//! different integer, until the daemon is manually killed.
use std::{
error::Error,
sync::atomic::{AtomicU32, Ordering},
};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use with_daemon::with_daemon;
fn main() -> Result<(), Box<dyn Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("off")).init();
let result = with_daemon(
"/tmp/with_daemon__example_counter.pid",
"/tmp/with_daemon__example_counter.sock",
// In this example the state is just an integer starting from 0.
//
// `with_daemon` expects a future that resolves to the initial value of the state here.
// It awaits that future in the daemon process and passes it to each handler.
|_| async { Result::<_, String>::Ok(AtomicU32::new(0)) },
// The handler is given an `Arc` holding the state above and a stream connected
// bi-directionally to the client it is handling.
//
// Here, it increments the state atomically and writes the value back to the client.
|state, mut stream| async move {
let previous = state.fetch_add(1, Ordering::SeqCst);
let _ = stream.write_u32(previous).await;
},
// The client is given a stream connected bi-directionally to an instance of handler.
//
// Here, it only needs to read the result sent to it by the handler.
|mut stream| async move { stream.read_u32().await },
)?; // An error above signifies an internal error in `with_daemon`, for example inability to fork,
// so in the example we just fail when that happens.
// `result` here is just what our client closure returns, so an I/O error reading from stream.
println!("result: {}", result?);
Ok(())
}
You can see another, more complicated example in the examples/
directory.