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