Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-event/src/hook.rs')
| -rw-r--r-- | helix-event/src/hook.rs | 91 |
1 files changed, 0 insertions, 91 deletions
diff --git a/helix-event/src/hook.rs b/helix-event/src/hook.rs deleted file mode 100644 index 7fb68148..00000000 --- a/helix-event/src/hook.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! rust dynamic dispatch is extremely limited so we have to build our -//! own vtable implementation. Otherwise implementing the event system would not be possible. -//! A nice bonus of this approach is that we can optimize the vtable a bit more. Normally -//! a dyn Trait fat pointer contains two pointers: A pointer to the data itself and a -//! pointer to a global (static) vtable entry which itself contains multiple other pointers -//! (the various functions of the trait, drop, size and align). That makes dynamic -//! dispatch pretty slow (double pointer indirections). However, we only have a single function -//! in the hook trait and don't need a drop implementation (event system is global anyway -//! and never dropped) so we can just store the entire vtable inline. - -use anyhow::Result; -use std::ptr::{self, NonNull}; - -use crate::Event; - -/// Opaque handle type that represents an erased type parameter. -/// -/// If extern types were stable, this could be implemented as `extern { pub type Opaque; }` but -/// until then we can use this. -/// -/// Care should be taken that we don't use a concrete instance of this. It should only be used -/// through a reference, so we can maintain something else's lifetime. -struct Opaque(()); - -pub(crate) struct ErasedHook { - data: NonNull<Opaque>, - call: unsafe fn(NonNull<Opaque>, NonNull<Opaque>, NonNull<Opaque>), -} - -impl ErasedHook { - pub(crate) fn new_dynamic<H: Fn() -> Result<()> + 'static + Send + Sync>( - hook: H, - ) -> ErasedHook { - unsafe fn call<F: Fn() -> Result<()> + 'static + Send + Sync>( - hook: NonNull<Opaque>, - _event: NonNull<Opaque>, - result: NonNull<Opaque>, - ) { - let hook: NonNull<F> = hook.cast(); - let result: NonNull<Result<()>> = result.cast(); - let hook: &F = hook.as_ref(); - let res = hook(); - ptr::write(result.as_ptr(), res) - } - - unsafe { - ErasedHook { - data: NonNull::new_unchecked(Box::into_raw(Box::new(hook)) as *mut Opaque), - call: call::<H>, - } - } - } - - pub(crate) fn new<E: Event, F: Fn(&mut E) -> Result<()>>(hook: F) -> ErasedHook { - unsafe fn call<E: Event, F: Fn(&mut E) -> Result<()>>( - hook: NonNull<Opaque>, - event: NonNull<Opaque>, - result: NonNull<Opaque>, - ) { - let hook: NonNull<F> = hook.cast(); - let mut event: NonNull<E> = event.cast(); - let result: NonNull<Result<()>> = result.cast(); - let hook: &F = hook.as_ref(); - let res = hook(event.as_mut()); - ptr::write(result.as_ptr(), res) - } - - unsafe { - ErasedHook { - data: NonNull::new_unchecked(Box::into_raw(Box::new(hook)) as *mut Opaque), - call: call::<E, F>, - } - } - } - - pub(crate) unsafe fn call<E: Event>(&self, event: &mut E) -> Result<()> { - let mut res = Ok(()); - - unsafe { - (self.call)( - self.data, - NonNull::from(event).cast(), - NonNull::from(&mut res).cast(), - ); - } - res - } -} - -unsafe impl Sync for ErasedHook {} -unsafe impl Send for ErasedHook {} |