diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/windows-core/src/event.rs | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/windows-core/src/event.rs')
| -rw-r--r-- | vendor/windows-core/src/event.rs | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/vendor/windows-core/src/event.rs b/vendor/windows-core/src/event.rs new file mode 100644 index 00000000..3951ef2c --- /dev/null +++ b/vendor/windows-core/src/event.rs @@ -0,0 +1,144 @@ +use super::*; +use core::{iter::once, mem::transmute_copy}; +use std::sync::{Arc, RwLock}; + +/// A type that you can use to declare and implement an event of a specified delegate type. +/// +/// The implementation is thread-safe and designed to avoid contention between events being +/// raised and delegates being added or removed. +pub struct Event<T: Interface> { + delegates: RwLock<Option<Arc<[Delegate<T>]>>>, +} + +unsafe impl<T: Interface> Send for Event<T> {} +unsafe impl<T: Interface> Sync for Event<T> {} + +impl<T: Interface> Default for Event<T> { + fn default() -> Self { + Self::new() + } +} + +impl<T: Interface> Event<T> { + /// Creates a new, empty `Event<T>`. + pub const fn new() -> Self { + Self { + delegates: RwLock::new(None), + } + } + + /// Registers a delegate with the event object. + pub fn add(&self, delegate: &T) -> Result<i64> { + let new_delegate = Delegate::new(delegate)?; + let token = new_delegate.to_token(); + let new_iter = once(new_delegate); + let mut guard = self.delegates.write().unwrap(); + + let new_list = if let Some(old_delegates) = guard.as_ref() { + Arc::from_iter(old_delegates.iter().cloned().chain(new_iter)) + } else { + Arc::from_iter(new_iter) + }; + + let old_list = guard.replace(new_list); + drop(guard); + drop(old_list); // drop the old delegates _after_ releasing lock + + Ok(token) + } + + /// Revokes a delegate's registration from the event object. + pub fn remove(&self, token: i64) { + let mut guard = self.delegates.write().unwrap(); + let mut old_list = None; + if let Some(old_delegates) = guard.as_ref() { + // `self.delegates` is only modified if the token is found. + if let Some(i) = old_delegates + .iter() + .position(|old_delegate| old_delegate.to_token() == token) + { + let new_list = Arc::from_iter( + old_delegates[..i] + .iter() + .chain(old_delegates[i + 1..].iter()) + .cloned(), + ); + + old_list = guard.replace(new_list); + } + } + drop(guard); + drop(old_list); // drop the old delegates _after_ releasing lock + } + + /// Clears the event, removing all delegates. + pub fn clear(&self) { + let mut guard = self.delegates.write().unwrap(); + let old_list = guard.take(); + drop(guard); + drop(old_list); // drop the old delegates _after_ releasing lock + } + + /// Invokes all of the event object's registered delegates with the provided callback. + pub fn call<F: FnMut(&T) -> Result<()>>(&self, mut callback: F) { + let delegates = { + let guard = self.delegates.read().unwrap(); + if let Some(delegates) = guard.as_ref() { + delegates.clone() + } else { + // No delegates to call. + return; + } + // <-- lock is released here + }; + + for delegate in delegates.iter() { + if let Err(error) = delegate.call(&mut callback) { + const RPC_E_SERVER_UNAVAILABLE: HRESULT = HRESULT(-2147023174); // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) + if matches!( + error.code(), + imp::RPC_E_DISCONNECTED | imp::JSCRIPT_E_CANTEXECUTE | RPC_E_SERVER_UNAVAILABLE + ) { + self.remove(delegate.to_token()); + } + } + } + } +} + +/// Holds either a direct or indirect reference to a delegate. A direct reference is typically +/// agile while an indirect reference is an agile wrapper. +#[derive(Clone)] +enum Delegate<T> { + Direct(T), + Indirect(AgileReference<T>), +} + +impl<T: Interface> Delegate<T> { + /// Creates a new `Delegate<T>`, containing a suitable reference to the specified delegate. + fn new(delegate: &T) -> Result<Self> { + if delegate.cast::<imp::IAgileObject>().is_ok() { + Ok(Self::Direct(delegate.clone())) + } else { + Ok(Self::Indirect(AgileReference::new(delegate)?)) + } + } + + /// Returns an encoded token to identify the delegate. + fn to_token(&self) -> i64 { + unsafe { + match self { + Self::Direct(delegate) => imp::EncodePointer(transmute_copy(delegate)) as i64, + Self::Indirect(delegate) => imp::EncodePointer(transmute_copy(delegate)) as i64, + } + } + } + + /// Invokes the delegates with the provided callback. + fn call<F: FnMut(&T) -> Result<()>>(&self, mut callback: F) -> Result<()> { + match self { + Self::Direct(delegate) => callback(delegate), + Self::Indirect(delegate) => callback(&delegate.resolve()?), + } + } +} |
