diff options
| author | Fenrir <[email protected]> | 2017-02-02 20:53:27 -0700 |
|---|---|---|
| committer | Fenrir <[email protected]> | 2017-02-18 18:04:15 -0700 |
| commit | 2ed837f8e17cfbaf9bf823eeb92fe1eac22c6159 (patch) | |
| tree | de5e1d52afdbc71b1dec80053824d9b60cde3592 /ctr-std/src/sys | |
| parent | Merge pull request #19 from FenrirWolf/thread_local (diff) | |
| download | ctru-rs-2ed837f8e17cfbaf9bf823eeb92fe1eac22c6159.tar.xz ctru-rs-2ed837f8e17cfbaf9bf823eeb92fe1eac22c6159.zip | |
ctr-std: Add Time module
Diffstat (limited to 'ctr-std/src/sys')
| -rw-r--r-- | ctr-std/src/sys/unix/mod.rs | 78 | ||||
| -rw-r--r-- | ctr-std/src/sys/unix/time.rs | 239 |
2 files changed, 315 insertions, 2 deletions
diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs index 13639eb..27c428d 100644 --- a/ctr-std/src/sys/unix/mod.rs +++ b/ctr-std/src/sys/unix/mod.rs @@ -10,6 +10,9 @@ #![allow(missing_docs, bad_style)] +use io::{self, ErrorKind}; +use libc; + pub mod ext; pub mod fast_thread_local; pub mod memchr; @@ -17,9 +20,45 @@ pub mod os; pub mod os_str; pub mod path; pub mod thread_local; +pub mod time; -use io::ErrorKind; -use libc; +#[cfg(not(test))] +pub fn init() { + use alloc::oom; + + // By default, some platforms will send a *signal* when an EPIPE error + // would otherwise be delivered. This runtime doesn't install a SIGPIPE + // handler, causing it to kill the program, which isn't exactly what we + // want! + // + // Hence, we set SIGPIPE to ignore when the program starts up in order + // to prevent this problem. + unsafe { + reset_sigpipe(); + } + + oom::set_oom_handler(oom_handler); + + // A nicer handler for out-of-memory situations than the default one. This + // one prints a message to stderr before aborting. It is critical that this + // code does not allocate any memory since we are in an OOM situation. Any + // errors are ignored while printing since there's nothing we can do about + // them and we are about to exit anyways. + fn oom_handler() -> ! { + use intrinsics; + let msg = "fatal runtime error: out of memory\n"; + unsafe { + libc::write(libc::STDERR_FILENO, + msg.as_ptr() as *const libc::c_void, + msg.len()); + intrinsics::abort(); + } + } + + // I don't think we have signal handling on the 3DS, so let's leave this + // blank for now + unsafe fn reset_sigpipe() {} +} pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno as libc::c_int { @@ -47,6 +86,41 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { + if t.is_minus_one() { + Err(io::Error::last_os_error()) + } else { + Ok(t) + } +} + +pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> + where T: IsMinusOne, + F: FnMut() -> T +{ + loop { + match cvt(f()) { + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + other => return other, + } + } +} + pub unsafe fn abort_internal() -> ! { ::libc::abort() } diff --git a/ctr-std/src/sys/unix/time.rs b/ctr-std/src/sys/unix/time.rs new file mode 100644 index 0000000..3eea581 --- /dev/null +++ b/ctr-std/src/sys/unix/time.rs @@ -0,0 +1,239 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cmp::Ordering; +use libc; +use time::Duration; + +pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: libc::timespec, +} + +impl Timespec { + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { + if self >= other { + Ok(if self.t.tv_nsec >= other.t.tv_nsec { + Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, + (self.t.tv_nsec - other.t.tv_nsec) as u32) + } else { + Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - + other.t.tv_nsec as u32) + }) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn add_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64); + let mut secs = secs.expect("overflow when adding duration to time"); + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1).expect("overflow when adding \ + duration to time"); + } + Timespec { + t: libc::timespec { + tv_sec: secs as libc::time_t, + tv_nsec: nsec as libc::c_long, + }, + } + } + + fn sub_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64); + let mut secs = secs.expect("overflow when subtracting duration \ + from time"); + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1).expect("overflow when subtracting \ + duration from time"); + } + Timespec { + t: libc::timespec { + tv_sec: secs as libc::time_t, + tv_nsec: nsec as libc::c_long, + }, + } + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + +mod inner { + use fmt; + use libc; + use sys::cvt; + use time::Duration; + + use super::Timespec; + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct Instant { + t: Timespec, + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct SystemTime { + t: Timespec, + } + + pub const UNIX_EPOCH: SystemTime = SystemTime { + t: Timespec { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, + }, + }; + + impl Instant { + // devkitARM does not expose monotonic time functions or types, + // so we fall back to constructing Instant with gettimeofday(2) + pub fn now() -> Instant { + use ptr; + + let mut s = libc::timeval { + tv_sec: 0, + tv_usec: 0, + }; + cvt(unsafe { + libc::gettimeofday(&mut s, ptr::null_mut()) + }).unwrap(); + return Instant::from(s) + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + self.t.sub_timespec(&other.t).unwrap_or_else(|_| { + panic!("other was less than the current instant") + }) + } + + pub fn add_duration(&self, other: &Duration) -> Instant { + Instant { t: self.t.add_duration(other) } + } + + pub fn sub_duration(&self, other: &Duration) -> Instant { + Instant { t: self.t.sub_duration(other) } + } + } + + impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + impl From<libc::timeval> for Instant { + fn from(t: libc::timeval) -> Instant { + Instant::from(libc::timespec { + tv_sec: t.tv_sec, + tv_nsec: (t.tv_usec * 1000) as libc::c_long, + }) + } + } + + impl From<libc::timespec> for Instant { + fn from(t: libc::timespec) -> Instant { + Instant { t: Timespec { t: t } } + } + } + + impl SystemTime { + pub fn now() -> SystemTime { + use ptr; + + let mut s = libc::timeval { + tv_sec: 0, + tv_usec: 0, + }; + cvt(unsafe { + libc::gettimeofday(&mut s, ptr::null_mut()) + }).unwrap(); + return SystemTime::from(s) + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.t.sub_timespec(&other.t) + } + + pub fn add_duration(&self, other: &Duration) -> SystemTime { + SystemTime { t: self.t.add_duration(other) } + } + + pub fn sub_duration(&self, other: &Duration) -> SystemTime { + SystemTime { t: self.t.sub_duration(other) } + } + } + + impl From<libc::timeval> for SystemTime { + fn from(t: libc::timeval) -> SystemTime { + SystemTime::from(libc::timespec { + tv_sec: t.tv_sec, + tv_nsec: (t.tv_usec * 1000) as libc::c_long, + }) + } + } + + impl From<libc::timespec> for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } + } + + impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } +} |