summaryrefslogtreecommitdiffstats
path: root/third_party/rust/fuchsia-zircon/src/lib.rs
blob: 26444402ccf04938cb8bbfd30c889038d1031d97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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
}