| // 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<T: EventToken> { | |
| 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<ExampleToken> = 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<ExampleToken> = 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<ExampleToken> = 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<T: EventToken>(pub(crate) EventContext<T>); | |
| impl<T: EventToken> WaitContext<T> { | |
| /// Creates a new WaitContext. | |
| pub fn new() -> Result<WaitContext<T>> { | |
| EventContext::new().map(WaitContext) | |
| } | |
| /// Creates a new WaitContext with the the associated triggers. | |
| pub fn build_with(triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<WaitContext<T>> { | |
| 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<SmallVec<[TriggeredEvent<T>; 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<SmallVec<[TriggeredEvent<T>; 16]>> { | |
| self.0.wait_timeout(timeout) | |
| } | |
| } | |
| impl<T: EventToken> AsRawDescriptor for WaitContext<T> { | |
| 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 | |
| ); | |
| } | |
| } |