// 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 EventToken for T {} /// 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. 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 = WaitContext::new()?; /// ctx.add(&evt1, 1)?; /// ctx.add(&evt2, 2)?; /// /// let events = ctx.wait()?; /// let tokens: Vec = events.iter().filter(|e| e.is_readable) /// .map(|e| e.token).collect(); /// assert_eq!(tokens, [2]); /// # Ok(()) /// # } /// ``` pub struct WaitContext(PollContext); impl WaitContext { /// Creates a new WaitContext. pub fn new() -> Result> { PollContext::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_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; 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]>> { 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 AsRawDescriptor for WaitContext { 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(), } }