tuwunel_api/router/state.rs
1use std::{ops::Deref, sync::Arc};
2
3use tuwunel_service::Services;
4
5#[derive(Clone, Copy, Debug)]
6pub struct State {
7 services: *const Services,
8}
9
10#[clippy::has_significant_drop]
11pub struct Guard {
12 services: Arc<Services>,
13}
14
15pub fn create(services: Arc<Services>) -> (State, Guard) {
16 let state = State {
17 services: Arc::into_raw(services.clone()),
18 };
19
20 let guard = Guard { services };
21
22 (state, guard)
23}
24
25impl Drop for Guard {
26 fn drop(&mut self) {
27 let ptr = Arc::as_ptr(&self.services);
28 // SAFETY: Parity with Arc::into_raw() called in create(). This revivifies the
29 // Arc lost to State so it can be dropped, otherwise Services will leak.
30 let arc = unsafe { Arc::from_raw(ptr) };
31 debug_assert!(
32 Arc::strong_count(&arc) > 1,
33 "Services usually has more than one reference and is not dropped here"
34 );
35 }
36}
37
38impl Deref for State {
39 type Target = Services;
40
41 fn deref(&self) -> &Self::Target {
42 deref(&self.services).expect("dereferenced Services pointer in State must not be null")
43 }
44}
45
46/// SAFETY: State is a thin wrapper containing a raw const pointer to Services
47/// in lieu of an Arc. Services is internally threadsafe. If State contains
48/// additional fields this notice should be reevaluated.
49unsafe impl Send for State {}
50
51/// SAFETY: State is a thin wrapper containing a raw const pointer to Services
52/// in lieu of an Arc. Services is internally threadsafe. If State contains
53/// additional fields this notice should be reevaluated.
54unsafe impl Sync for State {}
55
56fn deref(services: &*const Services) -> Option<&Services> {
57 // SAFETY: We replaced Arc<Services> with *const Services in State. This is
58 // worth about 10 clones (20 reference count updates) for each request handled.
59 // Though this is not an incredibly large quantity, it's woefully unnecessary
60 // given the context as explained below; though it is not currently known to be
61 // a performance bottleneck, the front-line position justifies preempting it.
62 //
63 // Services is created prior to the axum/tower stack and Router, and prior
64 // to serving any requests through the handlers in this crate. It is then
65 // dropped only after all requests have completed, the listening sockets
66 // have been closed, axum/tower has been dropped. Thus Services is
67 // expected to live at least as long as any instance of State, making the
68 // constant updates to the prior Arc unnecessary to keep Services alive.
69 //
70 // Nevertheless if it is possible to accomplish this by annotating State
71 // with a lifetime to hold a reference (and be aware I have made a
72 // significant effort trying to make this work) this unsafety may not be
73 // necessary. It is either very difficult or impossible to get a
74 // lifetime'ed reference through Router / RumaHandler; though it is
75 // possible to pass a reference through axum's `with_state()` in trivial
76 // configurations as the only requirement of a State is Clone.
77 unsafe { services.as_ref() }
78}