// Copyright 2020 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::time::Duration; pub use base_event_token_derive::*; use smallvec::SmallVec; use crate::descriptor::AsRawDescriptor; use crate::platform::EventContext; use crate::RawDescriptor; use crate::Result; /// Trait that can be used to associate events with arbitrary enums when using /// `WaitContext`. /// /// Simple enums that have no or primitive variant data data can use the `#[derive(EventToken)]` /// custom derive to implement this trait. See /// [event_token_derive::event_token](../base_event_token_derive/fn.event_token.html) for details. pub trait EventToken { /// Converts this token into a u64 that can be turned back into a token via `from_raw_token`. fn as_raw_token(&self) -> u64; /// Converts a raw token as returned from `as_raw_token` back into a token. /// /// It is invalid to give a raw token that was not returned via `as_raw_token` from the same /// `Self`. The implementation can expect that this will never happen as a result of its usage /// in `WaitContext`. fn from_raw_token(data: u64) -> Self; } impl EventToken for usize { fn as_raw_token(&self) -> u64 { *self as u64 } fn from_raw_token(data: u64) -> Self { data as Self } } impl EventToken for u64 { fn as_raw_token(&self) -> u64 { *self } fn from_raw_token(data: u64) -> Self { data as Self } } impl EventToken for u32 { fn as_raw_token(&self) -> u64 { u64::from(*self) } fn from_raw_token(data: u64) -> Self { data as Self } } impl EventToken for u16 { fn as_raw_token(&self) -> u64 { u64::from(*self) } fn from_raw_token(data: u64) -> Self { data as Self } } impl EventToken for u8 { fn as_raw_token(&self) -> u64 { u64::from(*self) } fn from_raw_token(data: u64) -> Self { data as Self } } impl EventToken for () { fn as_raw_token(&self) -> u64 { 0 } fn from_raw_token(_data: u64) -> Self {} } /// Represents an event that has been signaled and waited for via a wait function. #[derive(Copy, Clone, Debug)] pub struct TriggeredEvent { pub token: T, pub is_readable: bool, pub is_writable: bool, pub is_hungup: bool, } /// Represents types of events to watch for. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum EventType { // Used to to temporarily stop waiting for events without // removing the associated descriptor from the WaitContext. // In most cases if a descriptor no longer needs to be // waited on, prefer removing it entirely with // WaitContext#delete None, Read, Write, ReadWrite, } /// Used to wait for multiple objects which are eligible for waiting. /// /// # Example /// /// ``` /// use base::{Event, EventToken, Result, WaitContext}; /// /// #[derive(EventToken, Copy, Clone, Debug, PartialEq, Eq)] /// enum ExampleToken { /// SomeEvent(u32), /// AnotherEvent, /// } /// /// let evt1 = Event::new()?; /// let evt2 = Event::new()?; /// let another_evt = Event::new()?; /// /// let ctx: WaitContext = WaitContext::build_with(&[ /// (&evt1, ExampleToken::SomeEvent(1)), /// (&evt2, ExampleToken::SomeEvent(2)), /// (&another_evt, ExampleToken::AnotherEvent), /// ])?; /// /// // Trigger one of the `SomeEvent` events. /// evt2.signal()?; /// /// // Wait for an event to fire. `wait()` will return immediately in this example because `evt2` /// // has already been triggered, but in normal use, `wait()` will block until at least one event /// // is signaled by another thread or process. /// let events = ctx.wait()?; /// let tokens: Vec = events.iter().filter(|e| e.is_readable) /// .map(|e| e.token).collect(); /// assert_eq!(tokens, [ExampleToken::SomeEvent(2)]); /// /// // Reset evt2 so it doesn't trigger again in the next `wait()` call. /// let _ = evt2.reset()?; /// /// // Trigger a different event. /// another_evt.signal()?; /// /// let events = ctx.wait()?; /// let tokens: Vec = events.iter().filter(|e| e.is_readable) /// .map(|e| e.token).collect(); /// assert_eq!(tokens, [ExampleToken::AnotherEvent]); /// /// let _ = another_evt.reset()?; /// # Ok::<(), base::Error>(()) /// ``` pub struct WaitContext(pub(crate) EventContext); impl WaitContext { /// Creates a new WaitContext. pub fn new() -> Result> { EventContext::new().map(WaitContext) } /// Creates a new WaitContext with the the associated triggers. pub fn build_with(triggers: &[(&dyn AsRawDescriptor, T)]) -> Result> { let ctx = WaitContext::new()?; ctx.add_many(triggers)?; Ok(ctx) } /// Adds a trigger to the WaitContext. pub fn add(&self, descriptor: &dyn AsRawDescriptor, token: T) -> Result<()> { self.add_for_event(descriptor, EventType::Read, token) } /// Adds a trigger to the WaitContext watching for a specific type of event pub fn add_for_event( &self, descriptor: &dyn AsRawDescriptor, event_type: EventType, token: T, ) -> Result<()> { self.0.add_for_event(descriptor, event_type, token) } /// Adds multiple triggers to the WaitContext. pub fn add_many(&self, triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<()> { for trigger in triggers { self.add(trigger.0, T::from_raw_token(trigger.1.as_raw_token()))? } Ok(()) } /// Modifies a trigger already added to the WaitContext. If the descriptor is /// already registered, its associated token will be updated. pub fn modify( &self, descriptor: &dyn AsRawDescriptor, event_type: EventType, token: T, ) -> Result<()> { self.0.modify(descriptor, event_type, token) } /// Removes the given handle from triggers registered in the WaitContext if /// present. pub fn delete(&self, descriptor: &dyn AsRawDescriptor) -> Result<()> { self.0.delete(descriptor) } /// Waits for one or more of the registered triggers to become signaled. pub fn wait(&self) -> Result; 16]>> { self.wait_timeout(Duration::new(i64::MAX as u64, 0)) } /// Waits for one or more of the registered triggers to become signaled, failing if no triggers /// are signaled before the designated timeout has elapsed. pub fn wait_timeout(&self, timeout: Duration) -> Result; 16]>> { self.0.wait_timeout(timeout) } } impl AsRawDescriptor for WaitContext { fn as_raw_descriptor(&self) -> RawDescriptor { self.0.as_raw_descriptor() } } #[cfg(test)] mod tests { use base_event_token_derive::EventToken; use super::*; #[test] #[allow(dead_code)] fn event_token_derive() { #[derive(EventToken)] enum EmptyToken {} #[derive(PartialEq, Debug, EventToken)] enum Token { Alpha, Beta, // comments Gamma(u32), Delta { index: usize }, Omega, } assert_eq!( Token::from_raw_token(Token::Alpha.as_raw_token()), Token::Alpha ); assert_eq!( Token::from_raw_token(Token::Beta.as_raw_token()), Token::Beta ); assert_eq!( Token::from_raw_token(Token::Gamma(55).as_raw_token()), Token::Gamma(55) ); assert_eq!( Token::from_raw_token(Token::Delta { index: 100 }.as_raw_token()), Token::Delta { index: 100 } ); assert_eq!( Token::from_raw_token(Token::Omega.as_raw_token()), Token::Omega ); } }