summaryrefslogtreecommitdiffstats
path: root/third_party/rust/fuchsia-zircon/src/port.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/fuchsia-zircon/src/port.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/fuchsia-zircon/src/port.rs')
-rw-r--r--third_party/rust/fuchsia-zircon/src/port.rs344
1 files changed, 344 insertions, 0 deletions
diff --git a/third_party/rust/fuchsia-zircon/src/port.rs b/third_party/rust/fuchsia-zircon/src/port.rs
new file mode 100644
index 0000000000..6a9e8a8f7f
--- /dev/null
+++ b/third_party/rust/fuchsia-zircon/src/port.rs
@@ -0,0 +1,344 @@
+// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Type-safe bindings for Zircon port objects.
+
+use std::mem;
+
+use {AsHandleRef, HandleBased, Handle, HandleRef, Signals, Status, Time};
+use {sys, ok};
+
+/// An object representing a Zircon
+/// [port](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/port.md).
+///
+/// As essentially a subtype of `Handle`, it can be freely interconverted.
+#[derive(Debug, Eq, PartialEq)]
+pub struct Port(Handle);
+impl_handle_based!(Port);
+
+/// A packet sent through a port. This is a type-safe wrapper for
+/// [zx_port_packet_t](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md).
+#[derive(PartialEq, Eq, Debug)]
+pub struct Packet(sys::zx_port_packet_t);
+
+/// The contents of a `Packet`.
+#[derive(Debug, Copy, Clone)]
+pub enum PacketContents {
+ /// A user-generated packet.
+ User(UserPacket),
+ /// A one-shot signal packet generated via `object_wait_async`.
+ SignalOne(SignalPacket),
+ /// A repeating signal packet generated via `object_wait_async`.
+ SignalRep(SignalPacket),
+
+ #[doc(hidden)]
+ __Nonexhaustive
+}
+
+/// Contents of a user packet (one sent by `port_queue`). This is a type-safe wrapper for
+/// [zx_packet_user_t](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md).
+#[derive(Debug, Copy, Clone)]
+pub struct UserPacket(sys::zx_packet_user_t);
+
+/// Contents of a signal packet (one generated by the kernel). This is a type-safe wrapper for
+/// [zx_packet_signal_t](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md).
+#[derive(Debug, Copy, Clone)]
+pub struct SignalPacket(sys::zx_packet_signal_t);
+
+impl Packet {
+ /// Creates a new packet with `UserPacket` data.
+ pub fn from_user_packet(key: u64, status: i32, user: UserPacket) -> Packet {
+ Packet(
+ sys::zx_port_packet_t {
+ key: key,
+ packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_USER,
+ status: status,
+ union: user.0,
+ }
+ )
+ }
+
+ /// The packet's key.
+ pub fn key(&self) -> u64 {
+ self.0.key
+ }
+
+ /// The packet's status.
+ // TODO: should this type be wrapped?
+ pub fn status(&self) -> i32 {
+ self.0.status
+ }
+
+ /// The contents of the packet.
+ pub fn contents(&self) -> PacketContents {
+ if self.0.packet_type == sys::zx_packet_type_t::ZX_PKT_TYPE_USER {
+ PacketContents::User(UserPacket(self.0.union))
+ } else if self.0.packet_type == sys::zx_packet_type_t::ZX_PKT_TYPE_SIGNAL_ONE {
+ PacketContents::SignalOne(SignalPacket(unsafe { mem::transmute_copy(&self.0.union) }))
+ } else if self.0.packet_type == sys::zx_packet_type_t::ZX_PKT_TYPE_SIGNAL_REP {
+ PacketContents::SignalRep(SignalPacket(unsafe { mem::transmute_copy(&self.0.union) }))
+ } else {
+ panic!("unexpected packet type");
+ }
+ }
+}
+
+impl UserPacket {
+ pub fn from_u8_array(val: [u8; 32]) -> UserPacket {
+ UserPacket(val)
+ }
+
+ pub fn as_u8_array(&self) -> &[u8; 32] {
+ &self.0
+ }
+
+ pub fn as_mut_u8_array(&mut self) -> &mut [u8; 32] {
+ &mut self.0
+ }
+}
+
+impl SignalPacket {
+ /// The signals used in the call to `object_wait_async`.
+ pub fn trigger(&self) -> Signals {
+ Signals::from_bits_truncate(self.0.trigger)
+ }
+
+ /// The observed signals.
+ pub fn observed(&self) -> Signals {
+ Signals::from_bits_truncate(self.0.observed)
+ }
+
+ /// A per object count of pending operations.
+ pub fn count(&self) -> u64 {
+ self.0.count
+ }
+}
+
+impl Port {
+ /// Create an IO port, allowing IO packets to be read and enqueued.
+ ///
+ /// Wraps the
+ /// [zx_port_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_create.md)
+ /// syscall.
+ pub fn create() -> Result<Port, Status> {
+ unsafe {
+ let mut handle = 0;
+ let opts = 0;
+ let status = sys::zx_port_create(opts, &mut handle);
+ ok(status)?;
+ Ok(Handle::from_raw(handle).into())
+ }
+ }
+
+ /// Attempt to queue a user packet to the IO port.
+ ///
+ /// Wraps the
+ /// [zx_port_queue](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_queue.md)
+ /// syscall.
+ pub fn queue(&self, packet: &Packet) -> Result<(), Status> {
+ let status = unsafe {
+ sys::zx_port_queue(self.raw_handle(),
+ &packet.0 as *const sys::zx_port_packet_t, 0)
+ };
+ ok(status)
+ }
+
+ /// Wait for a packet to arrive on a (V2) port.
+ ///
+ /// Wraps the
+ /// [zx_port_wait](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md)
+ /// syscall.
+ pub fn wait(&self, deadline: Time) -> Result<Packet, Status> {
+ let mut packet = Default::default();
+ let status = unsafe {
+ sys::zx_port_wait(self.raw_handle(), deadline.nanos(),
+ &mut packet as *mut sys::zx_port_packet_t, 0)
+ };
+ ok(status)?;
+ Ok(Packet(packet))
+ }
+
+ /// Cancel pending wait_async calls for an object with the given key.
+ ///
+ /// Wraps the
+ /// [zx_port_cancel](https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/port_cancel.md)
+ /// syscall.
+ pub fn cancel<H>(&self, source: &H, key: u64) -> Result<(), Status> where H: HandleBased {
+ let status = unsafe {
+ sys::zx_port_cancel(self.raw_handle(), source.raw_handle(), key)
+ };
+ ok(status)
+ }
+}
+
+/// Options for wait_async.
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum WaitAsyncOpts {
+ Once = sys::ZX_WAIT_ASYNC_ONCE,
+ Repeating = sys::ZX_WAIT_ASYNC_REPEATING,
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use {DurationNum, Event};
+
+ #[test]
+ fn port_basic() {
+ let ten_ms = 10.millis();
+
+ let port = Port::create().unwrap();
+
+ // Waiting now should time out.
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // Send a valid packet.
+ let packet = Packet::from_user_packet(
+ 42,
+ 123,
+ UserPacket::from_u8_array([13; 32]),
+ );
+ assert!(port.queue(&packet).is_ok());
+
+ // Waiting should succeed this time. We should get back the packet we sent.
+ let read_packet = port.wait(ten_ms.after_now()).unwrap();
+ assert_eq!(read_packet, packet);
+ }
+
+ #[test]
+ fn wait_async_once() {
+ let ten_ms = 10.millis();
+ let key = 42;
+
+ let port = Port::create().unwrap();
+ let event = Event::create().unwrap();
+
+ assert!(event.wait_async_handle(&port, key, Signals::USER_0 | Signals::USER_1,
+ WaitAsyncOpts::Once).is_ok());
+
+ // Waiting without setting any signal should time out.
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // If we set a signal, we should be able to wait for it.
+ assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok());
+ let read_packet = port.wait(ten_ms.after_now()).unwrap();
+ assert_eq!(read_packet.key(), key);
+ assert_eq!(read_packet.status(), 0);
+ match read_packet.contents() {
+ PacketContents::SignalOne(sig) => {
+ assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1);
+ assert_eq!(sig.observed(), Signals::USER_0);
+ assert_eq!(sig.count(), 1);
+ }
+ _ => panic!("wrong packet type"),
+ }
+
+ // Shouldn't get any more packets.
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // Calling wait_async again should result in another packet.
+ assert!(event.wait_async_handle(&port, key, Signals::USER_0, WaitAsyncOpts::Once).is_ok());
+ let read_packet = port.wait(ten_ms.after_now()).unwrap();
+ assert_eq!(read_packet.key(), key);
+ assert_eq!(read_packet.status(), 0);
+ match read_packet.contents() {
+ PacketContents::SignalOne(sig) => {
+ assert_eq!(sig.trigger(), Signals::USER_0);
+ assert_eq!(sig.observed(), Signals::USER_0);
+ assert_eq!(sig.count(), 1);
+ }
+ _ => panic!("wrong packet type"),
+ }
+
+ // Calling wait_async_handle then cancel, we should not get a packet as cancel will
+ // remove it from the queue.
+ assert!(event.wait_async_handle(&port, key, Signals::USER_0, WaitAsyncOpts::Once).is_ok());
+ assert!(port.cancel(&event, key).is_ok());
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // If the event is signalled after the cancel, we also shouldn't get a packet.
+ assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal
+ assert!(event.wait_async_handle(&port, key, Signals::USER_0, WaitAsyncOpts::Once).is_ok());
+ assert!(port.cancel(&event, key).is_ok());
+ assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok());
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+ }
+
+ #[test]
+ fn wait_async_repeating() {
+ let ten_ms = 10.millis();
+ let key = 42;
+
+ let port = Port::create().unwrap();
+ let event = Event::create().unwrap();
+
+ assert!(event.wait_async_handle(&port, key, Signals::USER_0 | Signals::USER_1,
+ WaitAsyncOpts::Repeating).is_ok());
+
+ // Waiting without setting any signal should time out.
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // If we set a signal, we should be able to wait for it.
+ assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok());
+ let read_packet = port.wait(ten_ms.after_now()).unwrap();
+ assert_eq!(read_packet.key(), key);
+ assert_eq!(read_packet.status(), 0);
+ match read_packet.contents() {
+ PacketContents::SignalRep(sig) => {
+ assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1);
+ assert_eq!(sig.observed(), Signals::USER_0);
+ assert_eq!(sig.count(), 1);
+ }
+ _ => panic!("wrong packet type"),
+ }
+
+ // Should not get any more packets, as ZX_WAIT_ASYNC_REPEATING is edge triggered rather than
+ // level triggered.
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // If we clear and resignal, we should get the same packet again,
+ // even though we didn't call event.wait_async again.
+ assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal
+ assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok());
+ let read_packet = port.wait(ten_ms.after_now()).unwrap();
+ assert_eq!(read_packet.key(), key);
+ assert_eq!(read_packet.status(), 0);
+ match read_packet.contents() {
+ PacketContents::SignalRep(sig) => {
+ assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1);
+ assert_eq!(sig.observed(), Signals::USER_0);
+ assert_eq!(sig.count(), 1);
+ }
+ _ => panic!("wrong packet type"),
+ }
+
+ // Cancelling the wait should stop us getting packets...
+ assert!(port.cancel(&event, key).is_ok());
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+ // ... even if we clear and resignal
+ assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal
+ assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok());
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+
+ // Calling wait_async again should result in another packet.
+ assert!(event.wait_async_handle(
+ &port, key, Signals::USER_0, WaitAsyncOpts::Repeating).is_ok());
+ let read_packet = port.wait(ten_ms.after_now()).unwrap();
+ assert_eq!(read_packet.key(), key);
+ assert_eq!(read_packet.status(), 0);
+ match read_packet.contents() {
+ PacketContents::SignalRep(sig) => {
+ assert_eq!(sig.trigger(), Signals::USER_0);
+ assert_eq!(sig.observed(), Signals::USER_0);
+ assert_eq!(sig.count(), 1);
+ }
+ _ => panic!("wrong packet type"),
+ }
+
+ // Closing the handle should stop us getting packets.
+ drop(event);
+ assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT));
+ }
+}