Skip to main content

compio_driver/
key.rs

1#![allow(dead_code)]
2
3use std::{
4    fmt::{self, Debug},
5    hash::Hash,
6    io,
7    mem::ManuallyDrop,
8    ops::{Deref, DerefMut},
9    pin::Pin,
10    task::Waker,
11};
12
13use compio_buf::BufResult;
14use thin_cell::{Ref, ThinCell};
15
16use crate::{Extra, OpCode, PushEntry};
17
18/// An operation with other needed information.
19///
20/// You should not use `RawOp` directly. Instead, use [`Key`] to manage the
21/// reference-counted pointer to it.
22#[repr(C)]
23pub(crate) struct RawOp<T: ?Sized> {
24    // Platform-specific extra data.
25    //
26    // - On Windows, it holds the `OVERLAPPED` buffer and a pointer to the driver.
27    // - On Linux with `io_uring`, it holds the flags returned by kernel.
28    // - On other platforms, it stores tracker for multi-fd `OpCode`s.
29    //
30    // Extra MUST be the first field to guarantee the layout for casting on windows. An invariant
31    // on IOCP driver is that `RawOp` pointer is the same as `OVERLAPPED` pointer.
32    extra: Extra,
33    // The cancelled flag indicates the op has been cancelled.
34    cancelled: bool,
35    result: PushEntry<Option<Waker>, io::Result<usize>>,
36    pub(crate) op: T,
37}
38
39impl<T: ?Sized> RawOp<T> {
40    pub fn extra(&self) -> &Extra {
41        &self.extra
42    }
43
44    pub fn extra_mut(&mut self) -> &mut Extra {
45        &mut self.extra
46    }
47
48    fn pinned_op(&mut self) -> Pin<&mut T> {
49        // SAFETY: inner is always pinned with ThinCell.
50        unsafe { Pin::new_unchecked(&mut self.op) }
51    }
52}
53
54#[cfg(windows)]
55impl<T: OpCode + ?Sized> RawOp<T> {
56    /// Call [`OpCode::operate`] and assume that it is not an overlapped op,
57    /// which means it never returns [`Poll::Pending`].
58    ///
59    /// [`Poll::Pending`]: std::task::Poll::Pending
60    pub fn operate_blocking(&mut self) -> io::Result<usize> {
61        use std::task::Poll;
62
63        let optr = self.extra_mut().optr();
64        let op = self.pinned_op();
65        let res = unsafe { op.operate(optr.cast()) };
66        match res {
67            Poll::Pending => unreachable!("this operation is not overlapped"),
68            Poll::Ready(res) => res,
69        }
70    }
71}
72
73impl<T: ?Sized> Debug for RawOp<T> {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        f.debug_struct("RawOp")
76            .field("extra", &self.extra)
77            .field("cancelled", &self.cancelled)
78            .field("result", &self.result)
79            .field("op", &"<...>")
80            .finish()
81    }
82}
83
84/// A typed wrapper for key of Ops submitted into driver.
85#[repr(transparent)]
86pub struct Key<T> {
87    erased: ErasedKey,
88    _p: std::marker::PhantomData<T>,
89}
90
91impl<T> Unpin for Key<T> {}
92
93impl<T> Clone for Key<T> {
94    fn clone(&self) -> Self {
95        Self {
96            erased: self.erased.clone(),
97            _p: std::marker::PhantomData,
98        }
99    }
100}
101
102impl<T> Debug for Key<T> {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(f, "Key({})", self.erased.inner.as_ptr() as usize)
105    }
106}
107
108impl<T> Key<T> {
109    pub(crate) fn into_raw(self) -> usize {
110        self.erased.into_raw()
111    }
112
113    pub(crate) fn erase(self) -> ErasedKey {
114        self.erased
115    }
116
117    pub(crate) fn take_result(self) -> BufResult<usize, T> {
118        // SAFETY: `Key` invariant guarantees that `T` is the actual concrete type.
119        unsafe { self.erased.take_result::<T>() }
120    }
121}
122
123impl<T: OpCode + 'static> Key<T> {
124    /// Create [`RawOp`] and get the [`Key`] to it.
125    pub(crate) fn new(op: T, extra: impl Into<Extra>) -> Self {
126        let erased = ErasedKey::new(op, extra.into());
127
128        Self {
129            erased,
130            _p: std::marker::PhantomData,
131        }
132    }
133
134    pub(crate) fn set_extra(&self, extra: impl Into<Extra>) {
135        self.borrow().extra = extra.into();
136    }
137}
138
139impl<T> Deref for Key<T> {
140    type Target = ErasedKey;
141
142    fn deref(&self) -> &Self::Target {
143        &self.erased
144    }
145}
146
147impl<T> DerefMut for Key<T> {
148    fn deref_mut(&mut self) -> &mut Self::Target {
149        &mut self.erased
150    }
151}
152
153/// A type-erased reference-counted pointer to an operation.
154///
155/// Internally, it uses [`ThinCell`] to manage the reference count and borrowing
156/// state. It provides methods to manipulate the underlying operation, such as
157/// setting results, checking completion status, and cancelling the operation.
158#[derive(Clone)]
159#[repr(transparent)]
160pub struct ErasedKey {
161    inner: ThinCell<RawOp<dyn OpCode>>,
162}
163
164impl PartialEq for ErasedKey {
165    fn eq(&self, other: &Self) -> bool {
166        self.inner.ptr_eq(&other.inner)
167    }
168}
169
170impl Eq for ErasedKey {}
171
172impl Hash for ErasedKey {
173    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
174        (self.inner.as_ptr() as usize).hash(state)
175    }
176}
177
178impl Unpin for ErasedKey {}
179
180impl ErasedKey {
181    /// Create [`RawOp`] and get the [`ErasedKey`] to it.
182    pub(crate) fn new<T: OpCode + 'static>(op: T, extra: Extra) -> Self {
183        let raw_op = RawOp {
184            extra,
185            cancelled: false,
186            result: PushEntry::Pending(None),
187            op,
188        };
189        // SAFETY: Unsize coersion from `RawOp<T>` to `RawOp<dyn OpCode>`
190        let inner = unsafe { ThinCell::new_unsize(raw_op, |p| p as _) };
191        Self { inner }
192    }
193
194    /// Create from `user_data` pointer.
195    ///
196    /// # Safety
197    ///
198    /// `user_data` must be a valid pointer to `RawOp<dyn OpCode>` previously
199    /// created by [`Key::into_raw`].
200    pub(crate) unsafe fn from_raw(user_data: usize) -> Self {
201        let inner = unsafe { ThinCell::from_raw(user_data as *mut ()) };
202        Self { inner }
203    }
204
205    /// Create from `Overlapped` pointer.
206    ///
207    /// # Safety
208    ///
209    /// `optr` must be a valid pointer to `Overlapped` stored in `Extra` of
210    /// `RawOp<dyn OpCode>`.
211    #[cfg(windows)]
212    pub(crate) unsafe fn from_optr(optr: *mut crate::sys::Overlapped) -> Self {
213        let ptr = unsafe { optr.cast::<usize>().offset(-2).cast() };
214        let inner = unsafe { ThinCell::from_raw(ptr) };
215        Self { inner }
216    }
217
218    /// Leak self into a pointer to `Overlapped`.
219    #[cfg(windows)]
220    pub(crate) fn into_optr(self) -> *mut crate::sys::Overlapped {
221        unsafe { self.inner.leak().cast::<usize>().add(2).cast() }
222    }
223
224    /// Get the pointer as `user_data`.
225    ///
226    /// **Do not** call [`from_raw`](Self::from_raw) on the returned value of
227    /// this method.
228    pub(crate) fn as_raw(&self) -> usize {
229        self.inner.as_ptr() as _
230    }
231
232    /// Leak self and get the pointer as `user_data`.
233    pub(crate) fn into_raw(self) -> usize {
234        self.inner.leak() as _
235    }
236
237    #[inline]
238    pub(crate) fn borrow(&self) -> Ref<'_, RawOp<dyn OpCode>> {
239        self.inner.borrow()
240    }
241
242    /// Cancel the op.
243    pub(crate) fn set_cancelled(&self) {
244        self.borrow().cancelled = true;
245    }
246
247    /// Whether the op is completed.
248    pub(crate) fn has_result(&self) -> bool {
249        self.borrow().result.is_ready()
250    }
251
252    /// Complete the op and wake up the future if a waker is set.
253    pub(crate) fn set_result(&self, res: io::Result<usize>) {
254        let mut this = self.borrow();
255        #[cfg(io_uring)]
256        if let Ok(res) = res {
257            unsafe {
258                Pin::new_unchecked(&mut this.op).set_result(res);
259            }
260        }
261        if let PushEntry::Pending(Some(w)) =
262            std::mem::replace(&mut this.result, PushEntry::Ready(res))
263        {
264            w.wake();
265        }
266    }
267
268    /// Swap the inner [`Extra`] with the provided one, returning the previous
269    /// value.
270    pub(crate) fn swap_extra(&self, extra: Extra) -> Extra {
271        std::mem::replace(&mut self.borrow().extra, extra)
272    }
273
274    /// Set waker of the current future.
275    pub(crate) fn set_waker(&self, waker: &Waker) {
276        let PushEntry::Pending(w) = &mut self.borrow().result else {
277            return;
278        };
279
280        if w.as_ref().is_some_and(|w| w.will_wake(waker)) {
281            return;
282        }
283
284        *w = Some(waker.clone());
285    }
286
287    /// Take the inner result if it is completed.
288    ///
289    /// # Safety
290    ///
291    /// `T` must be the actual concrete type of the `Key`.
292    ///
293    /// # Panics
294    ///
295    /// Panics if the result is not ready or the `Key` is not unique (multiple
296    /// references or borrowed).
297    unsafe fn take_result<T>(self) -> BufResult<usize, T> {
298        // SAFETY: Caller guarantees that `T` is the actual concrete type.
299        let this = unsafe { self.inner.downcast_unchecked::<RawOp<T>>() };
300        let op = this.try_unwrap().map_err(|_| ()).expect("Key not unique");
301        let res = op.result.take_ready().expect("Result not ready");
302        BufResult(res, op.op)
303    }
304
305    /// Unsafely freeze the `Key` by bypassing borrow flag of [`ThinCell`],
306    /// preventing it from being dropped and unconditionally expose the
307    /// underlying `RawOp<dyn OpCode>`.
308    ///
309    /// # Safety
310    /// - During the time the [`FrozenKey`] is alive, no other references to the
311    ///   underlying `RawOp<dyn OpCode>` is used.
312    /// - One must not touch [`ThinCell`]'s internal state at all, as `Cell` is
313    ///   strictly single-threaded. This means no borrowing, no cloning, no
314    ///   dropping, etc.
315    pub(crate) unsafe fn freeze(self) -> FrozenKey {
316        FrozenKey {
317            inner: ManuallyDrop::new(self),
318        }
319    }
320}
321
322impl Debug for ErasedKey {
323    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324        write!(f, "ErasedKey({})", self.inner.as_ptr() as usize)
325    }
326}
327
328/// A frozen view into a [`Key`].
329pub(crate) struct FrozenKey {
330    inner: ManuallyDrop<ErasedKey>,
331}
332
333impl FrozenKey {
334    pub fn as_mut(&mut self) -> &mut RawOp<dyn OpCode> {
335        unsafe { self.inner.inner.borrow_unchecked() }
336    }
337
338    pub fn pinned_op(&mut self) -> Pin<&mut dyn OpCode> {
339        self.as_mut().pinned_op()
340    }
341
342    pub fn into_inner(self) -> ErasedKey {
343        ManuallyDrop::into_inner(self.inner)
344    }
345}
346
347unsafe impl Send for FrozenKey {}
348unsafe impl Sync for FrozenKey {}
349
350/// A temporary view into a [`Key`].
351///
352/// It is mainly used in the driver to avoid accidentally decreasing the
353/// reference count of the `Key` when the driver is not completed and may still
354/// emit event with the `user_data`.
355pub(crate) struct BorrowedKey(ManuallyDrop<ErasedKey>);
356
357impl BorrowedKey {
358    pub unsafe fn from_raw(user_data: usize) -> Self {
359        let key = unsafe { ErasedKey::from_raw(user_data) };
360        Self(ManuallyDrop::new(key))
361    }
362
363    pub fn upgrade(self) -> ErasedKey {
364        ManuallyDrop::into_inner(self.0)
365    }
366}
367
368impl Deref for BorrowedKey {
369    type Target = ErasedKey;
370
371    fn deref(&self) -> &Self::Target {
372        &self.0
373    }
374}
375
376pub trait RefExt {
377    fn pinned_op(&mut self) -> Pin<&mut dyn OpCode>;
378}
379
380impl RefExt for Ref<'_, RawOp<dyn OpCode>> {
381    fn pinned_op(&mut self) -> Pin<&mut dyn OpCode> {
382        self.deref_mut().pinned_op()
383    }
384}