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