xref: /third_party/rust/crates/rustix/src/io/context.rs (revision b8a62b91)
1//! Context types for polling systems, e.g. kqueue and epoll.
2
3#![allow(unsafe_code)]
4
5use crate::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
6
7use core::fmt;
8use core::marker::PhantomData;
9use core::ops::Deref;
10
11/// A reference to a `T`.
12pub struct Ref<'a, T> {
13    t: T,
14    _phantom: PhantomData<&'a T>,
15}
16
17impl<'a, T> Ref<'a, T> {
18    #[inline]
19    fn new(t: T) -> Self {
20        Self {
21            t,
22            _phantom: PhantomData,
23        }
24    }
25
26    #[inline]
27    fn consume(self) -> T {
28        self.t
29    }
30}
31
32impl<'a, T> Deref for Ref<'a, T> {
33    type Target = T;
34
35    #[inline]
36    fn deref(&self) -> &T {
37        &self.t
38    }
39}
40
41impl<'a, T: fmt::Debug> fmt::Debug for Ref<'a, T> {
42    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
43        self.t.fmt(fmt)
44    }
45}
46
47/// A trait for data stored within an [`Epoll`] instance.
48///
49/// [`Epoll`]: crate::io::epoll::Epoll
50pub trait Context {
51    /// The type of an element owned by this context.
52    type Data;
53
54    /// The type of a value used to refer to an element owned by this context.
55    type Target: AsFd;
56
57    /// Assume ownership of `data`, and returning a `Target`.
58    fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target>;
59
60    /// Encode `target` as a `u64`. The only requirement on this value is that
61    /// it be decodable by `decode`.
62    fn encode(&self, target: Ref<'_, Self::Target>) -> u64;
63
64    /// Decode `raw`, which is a value encoded by `encode`, into a `Target`.
65    ///
66    /// # Safety
67    ///
68    /// `raw` must be a `u64` value returned from `encode`, from the same
69    /// context, and within the context's lifetime.
70    unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target>;
71
72    /// Release ownership of the value referred to by `target` and return it.
73    fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data;
74}
75
76/// A type implementing [`Context`] where the `Data` type is `BorrowedFd<'a>`.
77pub struct Borrowing<'a> {
78    _phantom: PhantomData<BorrowedFd<'a>>,
79}
80
81impl<'a> Context for Borrowing<'a> {
82    type Data = BorrowedFd<'a>;
83    type Target = BorrowedFd<'a>;
84
85    #[inline]
86    fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
87        Ref::new(data)
88    }
89
90    #[inline]
91    fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
92        target.as_raw_fd() as u64
93    }
94
95    #[inline]
96    unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
97        Ref::new(BorrowedFd::<'a>::borrow_raw(raw as RawFd))
98    }
99
100    #[inline]
101    fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
102        target.consume()
103    }
104}
105
106/// A type implementing [`Context`] where the `Data` type is `T`, a type
107/// implementing `From<OwnedFd>` and `From<T> for OwnedFd`.
108///
109/// This may be used with [`OwnedFd`], or higher-level types like
110/// [`std::fs::File`] or [`std::net::TcpStream`].
111#[cfg(not(feature = "rustc-dep-of-std"))]
112pub struct Owning<'context, T: Into<OwnedFd> + From<OwnedFd>> {
113    _phantom: PhantomData<&'context T>,
114}
115
116#[cfg(not(feature = "rustc-dep-of-std"))]
117impl<'context, T: Into<OwnedFd> + From<OwnedFd>> Owning<'context, T> {
118    /// Creates a new empty `Owning`.
119    #[allow(clippy::new_without_default)] // This is a specialized type that doesn't need to be generically constructible.
120    #[inline]
121    pub fn new() -> Self {
122        Self {
123            _phantom: PhantomData,
124        }
125    }
126}
127
128#[cfg(not(feature = "rustc-dep-of-std"))]
129impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> Context for Owning<'context, T> {
130    type Data = T;
131    type Target = BorrowedFd<'context>;
132
133    #[inline]
134    fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
135        let fd: OwnedFd = data.into();
136        let raw_fd = fd.into_raw_fd();
137        // Safety: `epoll` will assign ownership of the file descriptor to the
138        // kernel epoll object. We use `Into<OwnedFd>`+`IntoRawFd` to consume
139        // the `Data` and extract the raw file descriptor and then "borrow" it
140        // with `borrow_raw` knowing that the borrow won't outlive the
141        // kernel epoll object.
142        unsafe { Ref::new(BorrowedFd::<'context>::borrow_raw(raw_fd)) }
143    }
144
145    #[inline]
146    fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
147        target.as_fd().as_raw_fd() as u64
148    }
149
150    #[inline]
151    unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
152        Ref::new(BorrowedFd::<'context>::borrow_raw(raw as RawFd))
153    }
154
155    #[inline]
156    fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
157        // The file descriptor was held by the kernel epoll object and is now
158        // being released, so we can create a new `OwnedFd` that assumes
159        // ownership.
160        let raw_fd = target.consume().as_raw_fd();
161        unsafe { T::from(OwnedFd::from_raw_fd(raw_fd).into()) }
162    }
163}
164