blob: f3afb6ae4e83b2ce774fc81470c5acaf5184d1ba [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::os::unix::io::AsRawFd;
use std::time::Duration;
use crate::{wrap_descriptor, AsRawDescriptor, RawDescriptor, Result};
use smallvec::SmallVec;
use sys_util::{PollContext, PollToken, WatchingEvents};
// Typedef PollToken as EventToken for better adherance to base naming.
// As actual typdefing is experimental, define a new trait with the mirrored
// attributes.
pub trait EventToken: PollToken {}
impl<T: PollToken> EventToken for T {}
/// 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.
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::{
/// Result, Event, WaitContext,
/// };
/// # fn test() -> Result<()> {
/// let evt1 = Event::new()?;
/// let evt2 = Event::new()?;
/// evt2.write(1)?;
///
/// let ctx: WaitContext<u32> = WaitContext::new()?;
/// ctx.add(&evt1, 1)?;
/// ctx.add(&evt2, 2)?;
///
/// let events = ctx.wait()?;
/// let tokens: Vec<u32> = events.iter().filter(|e| e.is_readable)
/// .map(|e| e.token).collect();
/// assert_eq!(tokens, [2]);
/// # Ok(())
/// # }
/// ```
pub struct WaitContext<T: EventToken>(PollContext<T>);
impl<T: EventToken> WaitContext<T> {
/// Creates a new WaitContext.
pub fn new() -> Result<WaitContext<T>> {
PollContext::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_fd_with_events(
&wrap_descriptor(descriptor),
convert_to_watching_events(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(
&wrap_descriptor(descriptor),
convert_to_watching_events(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(&wrap_descriptor(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]>> {
let events = self.0.wait_timeout(timeout)?;
Ok(events
.iter()
.map(|event| TriggeredEvent {
token: event.token(),
is_readable: event.readable(),
is_writable: event.writable(),
is_hungup: event.hungup(),
})
.collect())
}
}
impl<T: PollToken> AsRawDescriptor for WaitContext<T> {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.0.as_raw_fd()
}
}
fn convert_to_watching_events(event_type: EventType) -> WatchingEvents {
match event_type {
EventType::None => WatchingEvents::empty(),
EventType::Read => WatchingEvents::empty().set_read(),
EventType::Write => WatchingEvents::empty().set_write(),
EventType::ReadWrite => WatchingEvents::empty().set_read().set_write(),
}
}