Skip to main content

dope_core/driver/
control.rs

1use std::io;
2use std::os::fd::BorrowedFd;
3
4use crate::backend::Backend;
5use crate::backend::ops::raw::control::{ControlBackend, RawQuiesce};
6use crate::io::fd::Fd;
7use crate::io::socket::option::SocketOption;
8
9use super::token::Token;
10use super::{DriverContext, OutboundReservation, PushError};
11
12#[doc(hidden)]
13#[must_use]
14pub struct Quiesce<'a> {
15    backend: &'a mut Backend,
16    state: RawQuiesce,
17    targets: bool,
18}
19
20#[doc(hidden)]
21#[must_use]
22pub struct QuiesceOutcome {
23    targets: bool,
24    poison: bool,
25}
26
27impl QuiesceOutcome {
28    pub fn has_targets(&self) -> bool {
29        self.targets
30    }
31
32    pub fn needs_poison(&self) -> bool {
33        self.poison
34    }
35}
36
37impl<'a> Quiesce<'a> {
38    fn new(backend: &'a mut Backend) -> Self {
39        Self {
40            backend,
41            state: <Backend as ControlBackend>::begin_quiesce(),
42            targets: false,
43        }
44    }
45
46    /// Synchronously revokes backend access to `target`.
47    ///
48    /// Once this returns, the owner may release memory retained by the
49    /// submission. [`Self::finish`] performs any remaining backend-owned
50    /// result reclamation for the complete batch.
51    pub fn cancel(&mut self, target: Token) {
52        <Backend as ControlBackend>::quiesce_target(self.backend, &mut self.state, target);
53        self.targets = true;
54    }
55
56    pub fn finish(self) -> QuiesceOutcome {
57        QuiesceOutcome {
58            targets: self.targets,
59            poison: <Backend as ControlBackend>::finish_quiesce(self.backend, self.state),
60        }
61    }
62}
63
64pub trait ContextControl {
65    fn prepare_drop(&mut self);
66    fn register_shutdown_fd(&mut self, fd: BorrowedFd<'_>) -> io::Result<()>;
67    fn reserve_route(&mut self, id: u8) -> bool;
68    fn release_route(&mut self, id: u8);
69    fn poison_route(&mut self, id: u8);
70    fn quiesce(&mut self, targets: &[Token]) -> bool;
71    /// Queues an option without waiting for its kernel completion.
72    fn submit_option(
73        &mut self,
74        fd: &Fd<'_>,
75        option: impl Into<SocketOption>,
76    ) -> Result<(), PushError>;
77}
78
79impl ContextControl for DriverContext<'_, '_> {
80    fn prepare_drop(&mut self) {
81        <Backend as ControlBackend>::prepare_drop(self.backend());
82    }
83
84    fn register_shutdown_fd(&mut self, fd: BorrowedFd<'_>) -> io::Result<()> {
85        <Backend as ControlBackend>::register_shutdown_fd(self.backend(), fd)
86    }
87
88    fn reserve_route(&mut self, id: u8) -> bool {
89        <Backend as ControlBackend>::reserve_route(self.backend(), id)
90    }
91
92    fn release_route(&mut self, id: u8) {
93        <Backend as ControlBackend>::release_route(self.backend(), id);
94    }
95
96    fn poison_route(&mut self, id: u8) {
97        <Backend as ControlBackend>::poison_route(self.backend(), id);
98    }
99
100    fn quiesce(&mut self, targets: &[Token]) -> bool {
101        let mut quiesce = Quiesce::new(self.backend());
102        for target in targets {
103            quiesce.cancel(*target);
104        }
105        quiesce.finish().needs_poison()
106    }
107
108    fn submit_option(
109        &mut self,
110        fd: &Fd<'_>,
111        option: impl Into<SocketOption>,
112    ) -> Result<(), PushError> {
113        let option = option.into();
114        <Backend as ControlBackend>::submit_option(
115            self.backend(),
116            fd.slot(),
117            option.level(),
118            option.name(),
119            option.value(),
120        )
121    }
122}
123
124impl<'d> DriverContext<'_, 'd> {
125    #[doc(hidden)]
126    pub fn quiesce_batch(&mut self) -> Quiesce<'_> {
127        Quiesce::new(self.backend())
128    }
129
130    pub fn reserve_outbound(&mut self, count: u32) -> io::Result<OutboundReservation<'d>> {
131        let base = <Backend as ControlBackend>::reserve_outbound(self.backend(), count)?;
132        Ok(OutboundReservation::new(base, count))
133    }
134
135    #[doc(hidden)]
136    pub fn retire_outbound(&mut self, reservation: OutboundReservation<'d>) {
137        let (base, count) = reservation.into_range();
138        <Backend as ControlBackend>::retire_fixed(self.backend(), base, count);
139    }
140
141    #[doc(hidden)]
142    pub fn retire_fixed_fd(&mut self, fd: &mut Fd<'d>) {
143        let Some(slot) = fd.retire_slot(self.driver_ref()) else {
144            return;
145        };
146        self.backend().close_fd(slot);
147        <Backend as ControlBackend>::retire_fixed(self.backend(), slot.raw(), 1);
148    }
149}