]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | // Copyright 2017 The Fuchsia 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 | //! Type-safe bindings for Magenta port objects. | |
6 | ||
7 | use std::mem; | |
8 | ||
9 | use {HandleBase, Handle, HandleRef, Signals, Status, Time}; | |
10 | use {sys, into_result}; | |
11 | ||
12 | /// An object representing a Magenta | |
13 | /// [port](https://fuchsia.googlesource.com/magenta/+/master/docs/objects/port.md). | |
14 | /// | |
15 | /// As essentially a subtype of `Handle`, it can be freely interconverted. | |
16 | #[derive(Debug, Eq, PartialEq)] | |
17 | pub struct Port(Handle); | |
18 | ||
19 | impl HandleBase for Port { | |
20 | fn get_ref(&self) -> HandleRef { | |
21 | self.0.get_ref() | |
22 | } | |
23 | ||
24 | fn from_handle(handle: Handle) -> Self { | |
25 | Port(handle) | |
26 | } | |
27 | } | |
28 | ||
29 | /// A packet sent through a port. This is a type-safe wrapper for | |
30 | /// [mx_port_packet_t](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/port_wait2.md). | |
31 | #[derive(PartialEq, Eq, Debug)] | |
32 | pub struct Packet(sys::mx_port_packet_t); | |
33 | ||
34 | /// The contents of a `Packet`. | |
35 | #[derive(Debug, Copy, Clone)] | |
36 | pub enum PacketContents { | |
37 | /// A user-generated packet. | |
38 | User(UserPacket), | |
39 | /// A one-shot signal packet generated via `object_wait_async`. | |
40 | SignalOne(SignalPacket), | |
41 | /// A repeating signal packet generated via `object_wait_async`. | |
42 | SignalRep(SignalPacket), | |
43 | } | |
44 | ||
45 | /// Contents of a user packet (one sent by `port_queue`). This is a type-safe wrapper for | |
46 | /// [mx_packet_user_t](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/port_wait2.md). | |
47 | #[derive(Debug, Copy, Clone)] | |
48 | pub struct UserPacket(sys::mx_packet_user_t); | |
49 | ||
50 | /// Contents of a signal packet (one generated by the kernel). This is a type-safe wrapper for | |
51 | /// [mx_packet_signal_t](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/port_wait2.md). | |
52 | #[derive(Debug, Copy, Clone)] | |
53 | pub struct SignalPacket(sys::mx_packet_signal_t); | |
54 | ||
55 | impl Packet { | |
56 | /// Creates a new packet with `UserPacket` data. | |
57 | pub fn from_user_packet(key: u64, status: i32, user: UserPacket) -> Packet { | |
58 | Packet( | |
59 | sys::mx_port_packet_t { | |
60 | key: key, | |
61 | packet_type: sys::mx_packet_type_t::MX_PKT_TYPE_USER, | |
62 | status: status, | |
63 | union: user.0, | |
64 | } | |
65 | ) | |
66 | } | |
67 | ||
68 | /// The packet's key. | |
69 | pub fn key(&self) -> u64 { | |
70 | self.0.key | |
71 | } | |
72 | ||
73 | /// The packet's status. | |
74 | // TODO: should this type be wrapped? | |
75 | pub fn status(&self) -> i32 { | |
76 | self.0.status | |
77 | } | |
78 | ||
79 | /// The contents of the packet. | |
80 | pub fn contents(&self) -> PacketContents { | |
81 | if self.0.packet_type == sys::mx_packet_type_t::MX_PKT_TYPE_USER { | |
82 | PacketContents::User(UserPacket(self.0.union)) | |
83 | } else if self.0.packet_type == sys::mx_packet_type_t::MX_PKT_TYPE_SIGNAL_ONE { | |
84 | PacketContents::SignalOne(SignalPacket(unsafe { mem::transmute_copy(&self.0.union) })) | |
85 | } else if self.0.packet_type == sys::mx_packet_type_t::MX_PKT_TYPE_SIGNAL_REP { | |
86 | PacketContents::SignalRep(SignalPacket(unsafe { mem::transmute_copy(&self.0.union) })) | |
87 | } else { | |
88 | panic!("unexpected packet type"); | |
89 | } | |
90 | } | |
91 | } | |
92 | ||
93 | impl UserPacket { | |
94 | pub fn from_u8_array(val: [u8; 32]) -> UserPacket { | |
95 | UserPacket(val) | |
96 | } | |
97 | ||
98 | pub fn as_u8_array(&self) -> &[u8; 32] { | |
99 | &self.0 | |
100 | } | |
101 | ||
102 | pub fn as_mut_u8_array(&mut self) -> &mut [u8; 32] { | |
103 | &mut self.0 | |
104 | } | |
105 | } | |
106 | ||
107 | impl SignalPacket { | |
108 | /// The signals used in the call to `object_wait_async`. | |
109 | pub fn trigger(&self) -> Signals { | |
110 | self.0.trigger | |
111 | } | |
112 | ||
113 | /// The observed signals. | |
114 | pub fn observed(&self) -> Signals { | |
115 | self.0.observed | |
116 | } | |
117 | ||
118 | /// A per object count of pending operations. | |
119 | pub fn count(&self) -> u64 { | |
120 | self.0.count | |
121 | } | |
122 | } | |
123 | ||
124 | impl Port { | |
125 | /// Create an IO port, allowing IO packets to be read and enqueued. | |
126 | /// | |
127 | /// Wraps the | |
128 | /// [mx_port_create](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/port_create.md) | |
129 | /// syscall. | |
130 | pub fn create(opts: PortOpts) -> Result<Port, Status> { | |
131 | unsafe { | |
132 | let mut handle = 0; | |
133 | let status = sys::mx_port_create(opts as u32, &mut handle); | |
134 | into_result(status, || Self::from_handle(Handle(handle))) | |
135 | } | |
136 | } | |
137 | ||
138 | /// Attempt to queue a user packet to the IO port. | |
139 | /// | |
140 | /// Wraps the | |
141 | /// [mx_port_queue](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/port_queue.md) | |
142 | /// syscall. | |
143 | pub fn queue(&self, packet: &Packet) -> Result<(), Status> { | |
144 | let status = unsafe { | |
145 | sys::mx_port_queue(self.raw_handle(), | |
146 | &packet.0 as *const sys::mx_port_packet_t as *const u8, 0) | |
147 | }; | |
148 | into_result(status, || ()) | |
149 | } | |
150 | ||
151 | /// Wait for a packet to arrive on a (V2) port. | |
152 | /// | |
153 | /// Wraps the | |
154 | /// [mx_port_wait](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/port_wait2.md) | |
155 | /// syscall. | |
156 | pub fn wait(&self, deadline: Time) -> Result<Packet, Status> { | |
157 | let mut packet = Default::default(); | |
158 | let status = unsafe { | |
159 | sys::mx_port_wait(self.raw_handle(), deadline, | |
160 | &mut packet as *mut sys::mx_port_packet_t as *mut u8, 0) | |
161 | }; | |
162 | into_result(status, || Packet(packet)) | |
163 | } | |
164 | ||
165 | /// Cancel pending wait_async calls for an object with the given key. | |
166 | /// | |
167 | /// Wraps the | |
168 | /// [mx_port_cancel](https://fuchsia.googlesource.com/magenta/+/HEAD/docs/syscalls/port_cancel.md) | |
169 | /// syscall. | |
170 | pub fn cancel<H>(&self, source: &H, key: u64) -> Result<(), Status> where H: HandleBase { | |
171 | let status = unsafe { | |
172 | sys::mx_port_cancel(self.raw_handle(), source.raw_handle(), key) | |
173 | }; | |
174 | into_result(status, || ()) | |
175 | } | |
176 | } | |
177 | ||
178 | /// Options for creating a port. | |
179 | #[repr(u32)] | |
180 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | |
181 | pub enum PortOpts { | |
182 | /// Default options. | |
183 | Default = 0, | |
184 | } | |
185 | ||
186 | impl Default for PortOpts { | |
187 | fn default() -> Self { | |
188 | PortOpts::Default | |
189 | } | |
190 | } | |
191 | ||
192 | /// Options for wait_async. | |
193 | #[repr(u32)] | |
194 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | |
195 | pub enum WaitAsyncOpts { | |
196 | Once = sys::MX_WAIT_ASYNC_ONCE, | |
197 | Repeating = sys::MX_WAIT_ASYNC_REPEATING, | |
198 | } | |
199 | ||
200 | #[cfg(test)] | |
201 | mod tests { | |
202 | use super::*; | |
203 | use {Duration, Event, EventOpts}; | |
204 | use {MX_SIGNAL_LAST_HANDLE, MX_SIGNAL_NONE, MX_USER_SIGNAL_0, MX_USER_SIGNAL_1}; | |
205 | use deadline_after; | |
206 | ||
207 | #[test] | |
208 | fn port_basic() { | |
209 | let ten_ms: Duration = 10_000_000; | |
210 | ||
211 | let port = Port::create(PortOpts::Default).unwrap(); | |
212 | ||
213 | // Waiting now should time out. | |
214 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
215 | ||
216 | // Send a valid packet. | |
217 | let packet = Packet::from_user_packet( | |
218 | 42, | |
219 | 123, | |
220 | UserPacket::from_u8_array([13; 32]), | |
221 | ); | |
222 | assert!(port.queue(&packet).is_ok()); | |
223 | ||
224 | // Waiting should succeed this time. We should get back the packet we sent. | |
225 | let read_packet = port.wait(deadline_after(ten_ms)).unwrap(); | |
226 | assert_eq!(read_packet, packet); | |
227 | } | |
228 | ||
229 | #[test] | |
230 | fn wait_async_once() { | |
231 | let ten_ms: Duration = 10_000_000; | |
232 | let key = 42; | |
233 | ||
234 | let port = Port::create(PortOpts::Default).unwrap(); | |
235 | let event = Event::create(EventOpts::Default).unwrap(); | |
236 | ||
237 | assert!(event.wait_async(&port, key, MX_USER_SIGNAL_0 | MX_USER_SIGNAL_1, | |
238 | WaitAsyncOpts::Once).is_ok()); | |
239 | ||
240 | // Waiting without setting any signal should time out. | |
241 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
242 | ||
243 | // If we set a signal, we should be able to wait for it. | |
244 | assert!(event.signal(MX_SIGNAL_NONE, MX_USER_SIGNAL_0).is_ok()); | |
245 | let read_packet = port.wait(deadline_after(ten_ms)).unwrap(); | |
246 | assert_eq!(read_packet.key(), key); | |
247 | assert_eq!(read_packet.status(), 0); | |
248 | match read_packet.contents() { | |
249 | PacketContents::SignalOne(sig) => { | |
250 | assert_eq!(sig.trigger(), MX_USER_SIGNAL_0 | MX_USER_SIGNAL_1); | |
251 | assert_eq!(sig.observed(), MX_USER_SIGNAL_0 | MX_SIGNAL_LAST_HANDLE); | |
252 | assert_eq!(sig.count(), 1); | |
253 | } | |
254 | _ => panic!("wrong packet type"), | |
255 | } | |
256 | ||
257 | // Shouldn't get any more packets. | |
258 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
259 | ||
260 | // Calling wait_async again should result in another packet. | |
261 | assert!(event.wait_async(&port, key, MX_USER_SIGNAL_0, WaitAsyncOpts::Once).is_ok()); | |
262 | let read_packet = port.wait(deadline_after(ten_ms)).unwrap(); | |
263 | assert_eq!(read_packet.key(), key); | |
264 | assert_eq!(read_packet.status(), 0); | |
265 | match read_packet.contents() { | |
266 | PacketContents::SignalOne(sig) => { | |
267 | assert_eq!(sig.trigger(), MX_USER_SIGNAL_0); | |
268 | assert_eq!(sig.observed(), MX_USER_SIGNAL_0 | MX_SIGNAL_LAST_HANDLE); | |
269 | assert_eq!(sig.count(), 1); | |
270 | } | |
271 | _ => panic!("wrong packet type"), | |
272 | } | |
273 | ||
274 | // Calling wait_async then cancel, we should not get a packet as cancel will remove it from | |
275 | // the queue. | |
276 | assert!(event.wait_async(&port, key, MX_USER_SIGNAL_0, WaitAsyncOpts::Once).is_ok()); | |
277 | assert!(port.cancel(&event, key).is_ok()); | |
278 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
279 | ||
280 | // If the event is signalled after the cancel, we also shouldn't get a packet. | |
281 | assert!(event.signal(MX_USER_SIGNAL_0, MX_SIGNAL_NONE).is_ok()); // clear signal | |
282 | assert!(event.wait_async(&port, key, MX_USER_SIGNAL_0, WaitAsyncOpts::Once).is_ok()); | |
283 | assert!(port.cancel(&event, key).is_ok()); | |
284 | assert!(event.signal(MX_SIGNAL_NONE, MX_USER_SIGNAL_0).is_ok()); | |
285 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
286 | } | |
287 | ||
288 | #[test] | |
289 | fn wait_async_repeating() { | |
290 | let ten_ms: Duration = 10_000_000; | |
291 | let key = 42; | |
292 | ||
293 | let port = Port::create(PortOpts::Default).unwrap(); | |
294 | let event = Event::create(EventOpts::Default).unwrap(); | |
295 | ||
296 | assert!(event.wait_async(&port, key, MX_USER_SIGNAL_0 | MX_USER_SIGNAL_1, | |
297 | WaitAsyncOpts::Repeating).is_ok()); | |
298 | ||
299 | // Waiting without setting any signal should time out. | |
300 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
301 | ||
302 | // If we set a signal, we should be able to wait for it. | |
303 | assert!(event.signal(MX_SIGNAL_NONE, MX_USER_SIGNAL_0).is_ok()); | |
304 | let read_packet = port.wait(deadline_after(ten_ms)).unwrap(); | |
305 | assert_eq!(read_packet.key(), key); | |
306 | assert_eq!(read_packet.status(), 0); | |
307 | match read_packet.contents() { | |
308 | PacketContents::SignalRep(sig) => { | |
309 | assert_eq!(sig.trigger(), MX_USER_SIGNAL_0 | MX_USER_SIGNAL_1); | |
310 | assert_eq!(sig.observed(), MX_USER_SIGNAL_0 | MX_SIGNAL_LAST_HANDLE); | |
311 | assert_eq!(sig.count(), 1); | |
312 | } | |
313 | _ => panic!("wrong packet type"), | |
314 | } | |
315 | ||
316 | // Should not get any more packets, as MX_WAIT_ASYNC_REPEATING is edge triggered rather than | |
317 | // level triggered. | |
318 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
319 | ||
320 | // If we clear and resignal, we should get the same packet again, | |
321 | // even though we didn't call event.wait_async again. | |
322 | assert!(event.signal(MX_USER_SIGNAL_0, MX_SIGNAL_NONE).is_ok()); // clear signal | |
323 | assert!(event.signal(MX_SIGNAL_NONE, MX_USER_SIGNAL_0).is_ok()); | |
324 | let read_packet = port.wait(deadline_after(ten_ms)).unwrap(); | |
325 | assert_eq!(read_packet.key(), key); | |
326 | assert_eq!(read_packet.status(), 0); | |
327 | match read_packet.contents() { | |
328 | PacketContents::SignalRep(sig) => { | |
329 | assert_eq!(sig.trigger(), MX_USER_SIGNAL_0 | MX_USER_SIGNAL_1); | |
330 | assert_eq!(sig.observed(), MX_USER_SIGNAL_0 | MX_SIGNAL_LAST_HANDLE); | |
331 | assert_eq!(sig.count(), 1); | |
332 | } | |
333 | _ => panic!("wrong packet type"), | |
334 | } | |
335 | ||
336 | // Cancelling the wait should stop us getting packets... | |
337 | assert!(port.cancel(&event, key).is_ok()); | |
338 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
339 | // ... even if we clear and resignal | |
340 | assert!(event.signal(MX_USER_SIGNAL_0, MX_SIGNAL_NONE).is_ok()); // clear signal | |
341 | assert!(event.signal(MX_SIGNAL_NONE, MX_USER_SIGNAL_0).is_ok()); | |
342 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
343 | ||
344 | // Calling wait_async again should result in another packet. | |
345 | assert!(event.wait_async(&port, key, MX_USER_SIGNAL_0, WaitAsyncOpts::Repeating).is_ok()); | |
346 | let read_packet = port.wait(deadline_after(ten_ms)).unwrap(); | |
347 | assert_eq!(read_packet.key(), key); | |
348 | assert_eq!(read_packet.status(), 0); | |
349 | match read_packet.contents() { | |
350 | PacketContents::SignalRep(sig) => { | |
351 | assert_eq!(sig.trigger(), MX_USER_SIGNAL_0); | |
352 | assert_eq!(sig.observed(), MX_USER_SIGNAL_0 | MX_SIGNAL_LAST_HANDLE); | |
353 | assert_eq!(sig.count(), 1); | |
354 | } | |
355 | _ => panic!("wrong packet type"), | |
356 | } | |
357 | ||
358 | // Closing the handle should stop us getting packets. | |
359 | drop(event); | |
360 | assert_eq!(port.wait(deadline_after(ten_ms)), Err(Status::ErrTimedOut)); | |
361 | } | |
362 | } |