blob: d18220cb32afd9c90b018047c230e78ecc7583e1 [file] [log] [blame]
// 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
);
}
}