• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::os::unix::io::AsRawFd;
6 use std::time::Duration;
7 
8 use crate::{wrap_descriptor, AsRawDescriptor, RawDescriptor, Result};
9 use smallvec::SmallVec;
10 use sys_util::{PollContext, PollToken, WatchingEvents};
11 
12 // Typedef PollToken as EventToken for better adherance to base naming.
13 // As actual typdefing is experimental, define a new trait with the mirrored
14 // attributes.
15 pub trait EventToken: PollToken {}
16 impl<T: PollToken> EventToken for T {}
17 
18 /// Represents an event that has been signaled and waited for via a wait function.
19 #[derive(Copy, Clone, Debug)]
20 pub struct TriggeredEvent<T: EventToken> {
21     pub token: T,
22     pub is_readable: bool,
23     pub is_writable: bool,
24     pub is_hungup: bool,
25 }
26 
27 /// Represents types of events to watch for.
28 pub enum EventType {
29     // Used to to temporarily stop waiting for events without
30     // removing the associated descriptor from the WaitContext.
31     // In most cases if a descriptor no longer needs to be
32     // waited on, prefer removing it entirely with
33     // WaitContext#delete
34     None,
35     Read,
36     Write,
37     ReadWrite,
38 }
39 
40 /// Used to wait for multiple objects which are eligible for waiting.
41 ///
42 /// # Example
43 ///
44 /// ```
45 /// # use base::{
46 ///     Result, Event, WaitContext,
47 /// };
48 /// # fn test() -> Result<()> {
49 ///     let evt1 = Event::new()?;
50 ///     let evt2 = Event::new()?;
51 ///     evt2.write(1)?;
52 ///
53 ///     let ctx: WaitContext<u32> = WaitContext::new()?;
54 ///     ctx.add(&evt1, 1)?;
55 ///     ctx.add(&evt2, 2)?;
56 ///
57 ///     let events = ctx.wait()?;
58 ///     let tokens: Vec<u32> = events.iter().filter(|e| e.is_readable)
59 ///         .map(|e| e.token).collect();
60 ///     assert_eq!(tokens, [2]);
61 /// #   Ok(())
62 /// # }
63 /// ```
64 pub struct WaitContext<T: EventToken>(PollContext<T>);
65 
66 impl<T: EventToken> WaitContext<T> {
67     /// Creates a new WaitContext.
new() -> Result<WaitContext<T>>68     pub fn new() -> Result<WaitContext<T>> {
69         PollContext::new().map(WaitContext)
70     }
71 
72     /// Creates a new WaitContext with the the associated triggers.
build_with(triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<WaitContext<T>>73     pub fn build_with(triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<WaitContext<T>> {
74         let ctx = WaitContext::new()?;
75         ctx.add_many(triggers)?;
76         Ok(ctx)
77     }
78 
79     /// Adds a trigger to the WaitContext.
add(&self, descriptor: &dyn AsRawDescriptor, token: T) -> Result<()>80     pub fn add(&self, descriptor: &dyn AsRawDescriptor, token: T) -> Result<()> {
81         self.add_for_event(descriptor, EventType::Read, token)
82     }
83 
84     /// Adds a trigger to the WaitContext watching for a specific type of event
add_for_event( &self, descriptor: &dyn AsRawDescriptor, event_type: EventType, token: T, ) -> Result<()>85     pub fn add_for_event(
86         &self,
87         descriptor: &dyn AsRawDescriptor,
88         event_type: EventType,
89         token: T,
90     ) -> Result<()> {
91         self.0.add_fd_with_events(
92             &wrap_descriptor(descriptor),
93             convert_to_watching_events(event_type),
94             token,
95         )
96     }
97 
98     /// Adds multiple triggers to the WaitContext.
add_many(&self, triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<()>99     pub fn add_many(&self, triggers: &[(&dyn AsRawDescriptor, T)]) -> Result<()> {
100         for trigger in triggers {
101             self.add(trigger.0, T::from_raw_token(trigger.1.as_raw_token()))?
102         }
103         Ok(())
104     }
105 
106     /// Modifies a trigger already added to the WaitContext. If the descriptor is
107     /// already registered, its associated token will be updated.
modify( &self, descriptor: &dyn AsRawDescriptor, event_type: EventType, token: T, ) -> Result<()>108     pub fn modify(
109         &self,
110         descriptor: &dyn AsRawDescriptor,
111         event_type: EventType,
112         token: T,
113     ) -> Result<()> {
114         self.0.modify(
115             &wrap_descriptor(descriptor),
116             convert_to_watching_events(event_type),
117             token,
118         )
119     }
120 
121     /// Removes the given handle from triggers registered in the WaitContext if
122     /// present.
delete(&self, descriptor: &dyn AsRawDescriptor) -> Result<()>123     pub fn delete(&self, descriptor: &dyn AsRawDescriptor) -> Result<()> {
124         self.0.delete(&wrap_descriptor(descriptor))
125     }
126 
127     /// Waits for one or more of the registered triggers to become signaled.
wait(&self) -> Result<SmallVec<[TriggeredEvent<T>; 16]>>128     pub fn wait(&self) -> Result<SmallVec<[TriggeredEvent<T>; 16]>> {
129         self.wait_timeout(Duration::new(i64::MAX as u64, 0))
130     }
131     /// Waits for one or more of the registered triggers to become signaled, failing if no triggers
132     /// are signaled before the designated timeout has elapsed.
wait_timeout(&self, timeout: Duration) -> Result<SmallVec<[TriggeredEvent<T>; 16]>>133     pub fn wait_timeout(&self, timeout: Duration) -> Result<SmallVec<[TriggeredEvent<T>; 16]>> {
134         let events = self.0.wait_timeout(timeout)?;
135         Ok(events
136             .iter()
137             .map(|event| TriggeredEvent {
138                 token: event.token(),
139                 is_readable: event.readable(),
140                 is_writable: event.writable(),
141                 is_hungup: event.hungup(),
142             })
143             .collect())
144     }
145 }
146 
147 impl<T: PollToken> AsRawDescriptor for WaitContext<T> {
as_raw_descriptor(&self) -> RawDescriptor148     fn as_raw_descriptor(&self) -> RawDescriptor {
149         self.0.as_raw_fd()
150     }
151 }
152 
convert_to_watching_events(event_type: EventType) -> WatchingEvents153 fn convert_to_watching_events(event_type: EventType) -> WatchingEvents {
154     match event_type {
155         EventType::None => WatchingEvents::empty(),
156         EventType::Read => WatchingEvents::empty().set_read(),
157         EventType::Write => WatchingEvents::empty().set_write(),
158         EventType::ReadWrite => WatchingEvents::empty().set_read().set_write(),
159     }
160 }
161