Skip to main content

dope_core/driver/
control.rs

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