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