tuwunel_service/service.rs
1use std::{any::Any, fmt::Write, sync::Arc};
2
3use async_trait::async_trait;
4use tuwunel_core::{Result, Server, utils::string::SplitInfallible};
5use tuwunel_database::Database;
6
7use crate::services::OnceServices;
8
9/// Abstract interface for a Service
10#[async_trait]
11pub(crate) trait Service: Any + Send + Sync {
12 /// Implement the construction of the service instance. Services are
13 /// generally singletons so expect this to only be called once for a
14 /// service type. Note that it may be called again after a server reload,
15 /// but the prior instance will have been dropped first. Failure will
16 /// shutdown the server with an error.
17 fn build(args: &Args<'_>) -> Result<Arc<impl Service>>
18 where
19 Self: Sized;
20
21 /// Implement the service's worker loop. The service manager spawns a
22 /// task and calls this function after all services have been built.
23 async fn worker(self: Arc<Self>) -> Result { Ok(()) }
24
25 /// Interrupt the service. This is sent to initiate a graceful shutdown.
26 /// The service worker should return from its work loop.
27 async fn interrupt(&self) {}
28
29 /// Clear any caches or similar runtime state.
30 async fn clear_cache(&self) {}
31
32 /// Memory usage report in a markdown string.
33 async fn memory_usage(&self, _out: &mut (dyn Write + Send)) -> Result { Ok(()) }
34
35 /// Return the name of the service.
36 /// i.e. `crate::service::make_name(std::module_path!())`
37 fn name(&self) -> &str;
38
39 /// Return true if the service worker opts out of the tokio cooperative
40 /// budgeting. This can reduce tail latency at the risk of event loop
41 /// starvation.
42 fn unconstrained(&self) -> bool { false }
43}
44
45/// Args are passed to `Service::build` when a service is constructed. This
46/// allows for arguments to change with limited impact to the many services.
47pub(crate) struct Args<'a> {
48 pub(crate) server: &'a Arc<Server>,
49 pub(crate) db: &'a Arc<Database>,
50 pub(crate) services: &'a Arc<OnceServices>,
51}
52
53/// Utility for service implementations; see Service::name() in the trait.
54#[inline]
55pub(crate) fn make_name(module_path: &str) -> &str { module_path.split_once_infallible("::").1 }