diff options
Diffstat (limited to 'third_party/rust/fuchsia-zircon/src/lib.rs')
-rw-r--r-- | third_party/rust/fuchsia-zircon/src/lib.rs | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/third_party/rust/fuchsia-zircon/src/lib.rs b/third_party/rust/fuchsia-zircon/src/lib.rs new file mode 100644 index 0000000000..26444402cc --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/lib.rs @@ -0,0 +1,365 @@ +// Copyright 2016 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 kernel +//! [syscalls](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls.md). + +#![deny(warnings)] + +#[macro_use] +extern crate bitflags; + +pub extern crate fuchsia_zircon_sys as sys; + +#[deprecated(note="use fuchsia_zircon::sys::ZX_CPRNG_DRAW_MAX_LEN instead")] +#[doc(hidden)] +pub use sys::ZX_CPRNG_DRAW_MAX_LEN; + +// Implements the HandleBased traits for a Handle newtype struct +macro_rules! impl_handle_based { + ($type_name:path) => { + impl AsHandleRef for $type_name { + fn as_handle_ref(&self) -> HandleRef { + self.0.as_handle_ref() + } + } + + impl From<Handle> for $type_name { + fn from(handle: Handle) -> Self { + $type_name(handle) + } + } + + impl From<$type_name> for Handle { + fn from(x: $type_name) -> Handle { + x.0 + } + } + + impl HandleBased for $type_name {} + } +} + +// Creates associated constants of TypeName of the form +// `pub const NAME: TypeName = TypeName(value);` +macro_rules! assoc_consts { + ($typename:ident, [$($name:ident = $num:expr;)*]) => { + #[allow(non_upper_case_globals)] + impl $typename { + $( + pub const $name: $typename = $typename($num); + )* + } + } +} + +mod channel; +mod cprng; +mod event; +mod eventpair; +mod fifo; +mod handle; +mod job; +mod port; +mod process; +mod rights; +mod socket; +mod signals; +mod status; +mod time; +mod thread; +mod vmar; +mod vmo; + +pub use channel::*; +pub use cprng::*; +pub use event::*; +pub use eventpair::*; +pub use fifo::*; +pub use handle::*; +pub use job::*; +pub use port::*; +pub use process::*; +pub use rights::*; +pub use socket::*; +pub use signals::*; +pub use status::*; +pub use thread::*; +pub use time::*; +pub use vmar::*; +pub use vmo::*; + +/// Prelude containing common utility traits. +/// Designed for use like `use fuchsia_zircon::prelude::*;` +pub mod prelude { + pub use { + AsHandleRef, + Cookied, + DurationNum, + HandleBased, + Peered, + }; +} + +/// Convenience re-export of `Status::ok`. +pub fn ok(raw: sys::zx_status_t) -> Result<(), Status> { + Status::ok(raw) +} + +/// A "wait item" containing a handle reference and information about what signals +/// to wait on, and, on return from `object_wait_many`, which are pending. +#[repr(C)] +#[derive(Debug)] +pub struct WaitItem<'a> { + /// The handle to wait on. + pub handle: HandleRef<'a>, + /// A set of signals to wait for. + pub waitfor: Signals, + /// The set of signals pending, on return of `object_wait_many`. + pub pending: Signals, +} + +/// An identifier to select a particular clock. See +/// [zx_time_get](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/time_get.md) +/// for more information about the possible values. +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ClockId { + /// The number of nanoseconds since the system was powered on. Corresponds to + /// `ZX_CLOCK_MONOTONIC`. + Monotonic = 0, + /// The number of wall clock nanoseconds since the Unix epoch (midnight on January 1 1970) in + /// UTC. Corresponds to ZX_CLOCK_UTC. + UTC = 1, + /// The number of nanoseconds the current thread has been running for. Corresponds to + /// ZX_CLOCK_THREAD. + Thread = 2, +} + +/// Wait on multiple handles. +/// The success return value is a bool indicating whether one or more of the +/// provided handle references was closed during the wait. +/// +/// Wraps the +/// [zx_object_wait_many](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_wait_many.md) +/// syscall. +pub fn object_wait_many(items: &mut [WaitItem], deadline: Time) -> Result<bool, Status> +{ + let len = try!(usize_into_u32(items.len()).map_err(|_| Status::OUT_OF_RANGE)); + let items_ptr = items.as_mut_ptr() as *mut sys::zx_wait_item_t; + let status = unsafe { sys::zx_object_wait_many( items_ptr, len, deadline.nanos()) }; + if status == sys::ZX_ERR_CANCELED { + return Ok(true) + } + ok(status).map(|()| false) +} + +#[cfg(test)] +mod tests { + use super::*; + #[allow(unused_imports)] + use super::prelude::*; + + #[test] + fn monotonic_time_increases() { + let time1 = Time::get(ClockId::Monotonic); + 1_000.nanos().sleep(); + let time2 = Time::get(ClockId::Monotonic); + assert!(time2 > time1); + } + + #[test] + fn utc_time_increases() { + let time1 = Time::get(ClockId::UTC); + 1_000.nanos().sleep(); + let time2 = Time::get(ClockId::UTC); + assert!(time2 > time1); + } + + #[test] + fn thread_time_increases() { + let time1 = Time::get(ClockId::Thread); + 1_000.nanos().sleep(); + let time2 = Time::get(ClockId::Thread); + assert!(time2 > time1); + } + + #[test] + fn ticks_increases() { + let ticks1 = ticks_get(); + 1_000.nanos().sleep(); + let ticks2 = ticks_get(); + assert!(ticks2 > ticks1); + } + + #[test] + fn tick_length() { + let sleep_time = 1.milli(); + let ticks1 = ticks_get(); + sleep_time.sleep(); + let ticks2 = ticks_get(); + + // The number of ticks should have increased by at least 1 ms worth + let sleep_ticks = sleep_time.millis() * ticks_per_second() / 1000; + assert!(ticks2 >= (ticks1 + sleep_ticks)); + } + + #[test] + fn into_raw() { + let vmo = Vmo::create(1).unwrap(); + let h = vmo.into_raw(); + let vmo2 = Vmo::from(unsafe { Handle::from_raw(h) }); + assert!(vmo2.write(b"1", 0).is_ok()); + } + + #[test] + fn sleep() { + let sleep_ns = 1.millis(); + let time1 = Time::get(ClockId::Monotonic); + sleep_ns.sleep(); + let time2 = Time::get(ClockId::Monotonic); + assert!(time2 > time1 + sleep_ns); + } + + /// Test duplication by means of a VMO + #[test] + fn duplicate() { + let hello_length: usize = 5; + + // Create a VMO and write some data to it. + let vmo = Vmo::create(hello_length as u64).unwrap(); + assert!(vmo.write(b"hello", 0).is_ok()); + + // Replace, reducing rights to read. + let readonly_vmo = vmo.duplicate_handle(Rights::READ).unwrap(); + // Make sure we can read but not write. + let mut read_vec = vec![0; hello_length]; + assert_eq!(readonly_vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"hello"); + assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED)); + + // Write new data to the original handle, and read it from the new handle + assert!(vmo.write(b"bye", 0).is_ok()); + assert_eq!(readonly_vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"byelo"); + } + + // Test replace by means of a VMO + #[test] + fn replace() { + let hello_length: usize = 5; + + // Create a VMO and write some data to it. + let vmo = Vmo::create(hello_length as u64).unwrap(); + assert!(vmo.write(b"hello", 0).is_ok()); + + // Replace, reducing rights to read. + let readonly_vmo = vmo.replace_handle(Rights::READ).unwrap(); + // Make sure we can read but not write. + let mut read_vec = vec![0; hello_length]; + assert_eq!(readonly_vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"hello"); + assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED)); + } + + #[test] + fn wait_and_signal() { + let event = Event::create().unwrap(); + let ten_ms = 10.millis(); + + // Waiting on it without setting any signal should time out. + assert_eq!(event.wait_handle( + Signals::USER_0, 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()); + assert_eq!(event.wait_handle(Signals::USER_0, ten_ms.after_now()).unwrap(), + Signals::USER_0); + + // Should still work, signals aren't automatically cleared. + assert_eq!(event.wait_handle(Signals::USER_0, ten_ms.after_now()).unwrap(), + Signals::USER_0); + + // Now clear it, and waiting should time out again. + assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); + assert_eq!(event.wait_handle( + Signals::USER_0, ten_ms.after_now()), Err(Status::TIMED_OUT)); + } + + #[test] + fn wait_many_and_signal() { + let ten_ms = 10.millis(); + let e1 = Event::create().unwrap(); + let e2 = Event::create().unwrap(); + + // Waiting on them now should time out. + let mut items = vec![ + WaitItem { handle: e1.as_handle_ref(), waitfor: Signals::USER_0, pending: Signals::NONE }, + WaitItem { handle: e2.as_handle_ref(), waitfor: Signals::USER_1, pending: Signals::NONE }, + ]; + assert_eq!(object_wait_many(&mut items, ten_ms.after_now()), Err(Status::TIMED_OUT)); + assert_eq!(items[0].pending, Signals::NONE); + assert_eq!(items[1].pending, Signals::NONE); + + // Signal one object and it should return success. + assert!(e1.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + assert!(object_wait_many(&mut items, ten_ms.after_now()).is_ok()); + assert_eq!(items[0].pending, Signals::USER_0); + assert_eq!(items[1].pending, Signals::NONE); + + // Signal the other and it should return both. + assert!(e2.signal_handle(Signals::NONE, Signals::USER_1).is_ok()); + assert!(object_wait_many(&mut items, ten_ms.after_now()).is_ok()); + assert_eq!(items[0].pending, Signals::USER_0); + assert_eq!(items[1].pending, Signals::USER_1); + + // Clear signals on both; now it should time out again. + assert!(e1.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); + assert!(e2.signal_handle(Signals::USER_1, Signals::NONE).is_ok()); + assert_eq!(object_wait_many(&mut items, ten_ms.after_now()), Err(Status::TIMED_OUT)); + assert_eq!(items[0].pending, Signals::NONE); + assert_eq!(items[1].pending, Signals::NONE); + } + + #[test] + fn cookies() { + let event = Event::create().unwrap(); + let scope = Event::create().unwrap(); + + // Getting a cookie when none has been set should fail. + assert_eq!(event.get_cookie(&scope.as_handle_ref()), Err(Status::ACCESS_DENIED)); + + // Set a cookie. + assert_eq!(event.set_cookie(&scope.as_handle_ref(), 42), Ok(())); + + // Should get it back.... + assert_eq!(event.get_cookie(&scope.as_handle_ref()), Ok(42)); + + // but not with the wrong scope! + assert_eq!(event.get_cookie(&event.as_handle_ref()), Err(Status::ACCESS_DENIED)); + + // Can change it, with the same scope... + assert_eq!(event.set_cookie(&scope.as_handle_ref(), 123), Ok(())); + + // but not with a different scope. + assert_eq!(event.set_cookie(&event.as_handle_ref(), 123), Err(Status::ACCESS_DENIED)); + } +} + +pub fn usize_into_u32(n: usize) -> Result<u32, ()> { + if n > ::std::u32::MAX as usize || n < ::std::u32::MIN as usize { + return Err(()) + } + Ok(n as u32) +} + +pub fn size_to_u32_sat(n: usize) -> u32 { + if n > ::std::u32::MAX as usize { + return ::std::u32::MAX; + } + if n < ::std::u32::MIN as usize { + return ::std::u32::MIN; + } + n as u32 +} |