| // 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, time::Duration}; |
| |
| use crate::{ |
| platform::{PollContext, PollToken, WatchingEvents}, |
| wrap_descriptor, AsRawDescriptor, RawDescriptor, Result, |
| }; |
| use smallvec::SmallVec; |
| |
| // 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(), |
| } |
| } |