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