Skip to main content

dope_core/io/socket/option/
mod.rs

1use std::{mem, num::NonZero, time};
2
3use dope_sys::io::socket::option::{Kind, Value, tuning};
4
5use crate::{
6    Engine, driver,
7    driver::{life::native::Apply, retained::flight, route, route::kind},
8    io::{fd::handles, socket::pending},
9};
10
11mod kinds;
12
13pub(super) use kinds::Mask;
14pub(crate) use kinds::NonEmpty;
15
16/// Maximum number of distinct options represented by a stream-option set.
17pub(crate) const MAX_STREAM_OPTIONS: usize = 7;
18
19/// An explicit binary socket-option command.
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21#[repr(u8)]
22pub enum Switch {
23    Off,
24    On,
25}
26
27/// A socket-buffer request representable by the kernel ABI.
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29#[repr(transparent)]
30pub struct Buffer(Value);
31
32/// A positive, whole-second keep-alive period representable by the kernel ABI.
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34#[repr(transparent)]
35pub struct Period(NonZero<u32>);
36
37/// A positive keep-alive retry count representable by the kernel ABI.
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39#[repr(transparent)]
40pub struct Retries(NonZero<u32>);
41
42impl Switch {
43    pub(crate) const fn value(self) -> Value {
44        match self {
45            Self::Off => Value::ZERO,
46            Self::On => Value::from_enabled(true),
47        }
48    }
49}
50
51impl Buffer {
52    const fn value(self) -> Value {
53        self.0
54    }
55}
56
57impl Period {
58    const fn value(self) -> Value {
59        Value::new(self.0.get() as i32)
60    }
61}
62
63impl Retries {
64    const fn value(self) -> Value {
65        Value::new(self.0.get() as i32)
66    }
67}
68
69/// Validated, allocation-free stream options owning every scalar retained by
70/// an asynchronous kernel submission.
71#[derive(Clone, Copy)]
72pub struct Set {
73    recv_buffer: Value,
74    send_buffer: Value,
75    keep_alive_idle: Value,
76    keep_alive_interval: Value,
77    keep_alive_retries: Value,
78    present: Mask,
79    enabled: Mask,
80}
81
82/// Invalid allocation-free socket-option configuration.
83#[derive(Clone, Copy, PartialEq, Eq, Debug)]
84#[repr(u8)]
85pub enum Error {
86    Buffer {
87        actual: usize,
88        maximum: usize,
89    },
90    Period {
91        actual: time::Duration,
92        maximum: time::Duration,
93    },
94    Retries {
95        actual: u32,
96        maximum: u32,
97    },
98}
99
100impl Default for Set {
101    fn default() -> Self {
102        Self::EMPTY
103    }
104}
105
106/// Result of submitting a strongly ordered socket-tuning plan.
107pub enum Tuning<'d> {
108    /// All options were applied synchronously.
109    Applied(handles::Descriptor<'d>),
110    /// The descriptor and its exact completion/cancellation authority remain
111    /// indivisible until the operation resolves.
112    Pending(pending::Tuning<'d>),
113}
114
115impl Set {
116    /// Admits already-validated, structurally distinct stream options.
117    pub fn new(
118        recv: Option<Buffer>,
119        send: Option<Buffer>,
120        no_delay: Option<Switch>,
121        alive: Option<Switch>,
122        idle: Option<Period>,
123        interval: Option<Period>,
124        retries: Option<Retries>,
125    ) -> Self {
126        let mut set = Self::EMPTY;
127        if let Some(value) = recv {
128            set.set(Kind::RecvBuffer, value.value());
129        }
130        if let Some(value) = send {
131            set.set(Kind::SendBuffer, value.value());
132        }
133        if let Some(value) = no_delay {
134            set.set(Kind::NoDelay, value.value());
135        }
136        if let Some(value) = alive {
137            set.set(Kind::KeepAlive, value.value());
138        }
139        if let Some(value) = idle {
140            set.set(Kind::KeepAliveIdle, value.value());
141        }
142        if let Some(value) = interval {
143            set.set(Kind::KeepAliveInterval, value.value());
144        }
145        if let Some(value) = retries {
146            set.set(Kind::KeepAliveRetries, value.value());
147        }
148        set
149    }
150
151    pub const EMPTY: Self = Self {
152        recv_buffer: Value::ZERO,
153        send_buffer: Value::ZERO,
154        keep_alive_idle: Value::ZERO,
155        keep_alive_interval: Value::ZERO,
156        keep_alive_retries: Value::ZERO,
157        present: Mask::EMPTY,
158        enabled: Mask::EMPTY,
159    };
160
161    pub(crate) fn non_empty(&self) -> Option<NonEmpty<'_>> {
162        NonEmpty::new(self, self.present)
163    }
164
165    fn value(&self, kind: Kind) -> Value {
166        match kind {
167            Kind::NoDelay | Kind::KeepAlive => Value::from_enabled(self.enabled.contains(kind)),
168            Kind::RecvBuffer => self.recv_buffer,
169            Kind::SendBuffer => self.send_buffer,
170            Kind::KeepAliveIdle => self.keep_alive_idle,
171            Kind::KeepAliveInterval => self.keep_alive_interval,
172            Kind::KeepAliveRetries => self.keep_alive_retries,
173        }
174    }
175
176    fn set(&mut self, kind: Kind, value: Value) {
177        self.present.insert(kind);
178        match kind {
179            Kind::NoDelay | Kind::KeepAlive if !value.is_zero() => self.enabled.insert(kind),
180            Kind::NoDelay | Kind::KeepAlive => {}
181            Kind::RecvBuffer => self.recv_buffer = value,
182            Kind::SendBuffer => self.send_buffer = value,
183            Kind::KeepAliveIdle => self.keep_alive_idle = value,
184            Kind::KeepAliveInterval => self.keep_alive_interval = value,
185            Kind::KeepAliveRetries => self.keep_alive_retries = value,
186        }
187    }
188}
189
190impl TryFrom<usize> for Buffer {
191    type Error = Error;
192
193    fn try_from(actual: usize) -> Result<Self, Self::Error> {
194        let maximum = i32::MAX as usize;
195        if actual <= maximum {
196            Ok(Self(Value::new(actual as i32)))
197        } else {
198            Err(Error::Buffer { actual, maximum })
199        }
200    }
201}
202
203impl TryFrom<time::Duration> for Period {
204    type Error = Error;
205
206    fn try_from(actual: time::Duration) -> Result<Self, Self::Error> {
207        let maximum = time::Duration::from_secs(i32::MAX as u64);
208        if actual.is_zero() || actual > maximum {
209            return Err(Error::Period { actual, maximum });
210        }
211        let seconds = actual.as_secs() + u64::from(actual.subsec_nanos() != 0);
212        Ok(Self(NonZero::<u32>::MIN.saturating_add(seconds as u32 - 1)))
213    }
214}
215
216impl TryFrom<u32> for Retries {
217    type Error = Error;
218
219    fn try_from(actual: u32) -> Result<Self, Self::Error> {
220        let maximum = i32::MAX as u32;
221        if actual == 0 || actual > maximum {
222            return Err(Error::Retries { actual, maximum });
223        }
224        Ok(Self(NonZero::<u32>::MIN.saturating_add(actual - 1)))
225    }
226}
227
228const _: () = {
229    assert!(mem::size_of::<Switch>() == mem::size_of::<bool>());
230    assert!(mem::align_of::<Switch>() == mem::align_of::<bool>());
231    assert!(mem::size_of::<Buffer>() == mem::size_of::<Value>());
232    assert!(mem::size_of::<Period>() == mem::size_of::<Value>());
233    assert!(mem::size_of::<Retries>() == mem::size_of::<Value>());
234    assert!(mem::size_of::<Option<Period>>() == mem::size_of::<Period>());
235    assert!(mem::size_of::<Option<Retries>>() == mem::size_of::<Retries>());
236    assert!(mem::size_of::<Set>() == 24);
237};
238
239impl<'d> Tuning<'d> {
240    pub fn try_new<Tag: route::Tag + route::Select<kind::Tuning>>(
241        driver: &mut driver::Context<'_, 'd>,
242        slots: &flight::Slots<'d, Tag>,
243        target: route::Target<'d, Tag>,
244        fd: handles::Descriptor<'d>,
245        opts: Set,
246    ) -> Result<Self, pending::Rejection<'d>> {
247        use tuning::device::{Owner as _, Source};
248        let owner = Apply::new(driver.native(), slots, target, fd, opts);
249        let request = <crate::Backend as Source>::Socket::new(owner);
250        // SAFETY: the native borrow and affine descriptor share this driver's
251        // slot table; the owner retains every option scalar until completion.
252        unsafe { tuning::raw::Apply::<Engine, _, { MAX_STREAM_OPTIONS }>::try_apply(request) }
253    }
254}