Skip to content

Commit

Permalink
Update use of libc::timespec to prepare for future libc version
Browse files Browse the repository at this point in the history
In a future release of the `libc` crate, `libc::timespec` will contain
private padding fields on `*-linux-musl` targets and so the struct will
no longer be able to be created using the literal initialization syntax.

Update places where `libc::timespec` is created to first zero initialize
the value and then update the `tv_sec` and `tv_nsec` fields manually.
Many of these places are in `const fn`s so a helper function
`zero_init_timespec()` is introduced to help with this as
`std::mem::zeroed()` is not a `const` function.

Some matches on `libc::timespec` are also updated to include a trailing
`..` pattern which works when `libc::timespec` has additional, private
fields as well as when it does not (like for
`x86_64-unknown-linux-gnu`).
  • Loading branch information
wesleywiser committed Nov 28, 2022
1 parent a75a349 commit 8b4dad4
Showing 1 changed file with 33 additions and 35 deletions.
68 changes: 33 additions & 35 deletions src/sys/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ use std::convert::From;
use std::time::Duration;
use std::{cmp, fmt, ops};

const fn zero_init_timespec() -> timespec {
// `std::mem::zeroed()` is not yet a const fn (https://github.com/rust-lang/rust/issues/62061)
// so we will instead initialize an array of the appropriate size to zero and then
// transmute it to a timespec value.
unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
}

#[cfg(any(
all(feature = "time", any(target_os = "android", target_os = "linux")),
all(
Expand All @@ -20,7 +27,7 @@ use std::{cmp, fmt, ops};
)
))]
pub(crate) mod timer {
use crate::sys::time::TimeSpec;
use crate::sys::time::{zero_init_timespec, TimeSpec};
use bitflags::bitflags;

#[derive(Debug, Clone, Copy)]
Expand All @@ -29,14 +36,8 @@ pub(crate) mod timer {
impl TimerSpec {
pub const fn none() -> Self {
Self(libc::itimerspec {
it_interval: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_value: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_interval: zero_init_timespec(),
it_value: zero_init_timespec(),
})
}
}
Expand All @@ -57,10 +58,7 @@ pub(crate) mod timer {
fn from(expiration: Expiration) -> TimerSpec {
match expiration {
Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
it_interval: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_interval: zero_init_timespec(),
it_value: *t.as_ref(),
}),
Expiration::IntervalDelayed(start, interval) => {
Expand Down Expand Up @@ -118,6 +116,7 @@ pub(crate) mod timer {
libc::timespec {
tv_sec: 0,
tv_nsec: 0,
..
},
it_value: ts,
}) => Expiration::OneShot(ts.into()),
Expand Down Expand Up @@ -257,18 +256,17 @@ impl PartialOrd for TimeSpec {

impl TimeValLike for TimeSpec {
#[inline]
#[cfg_attr(target_env = "musl", allow(deprecated))]
// https://github.com/rust-lang/libc/issues/1848
fn seconds(seconds: i64) -> TimeSpec {
assert!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
"TimeSpec out of bounds; seconds={}",
seconds
);
#[cfg_attr(target_env = "musl", allow(deprecated))]
// https://github.com/rust-lang/libc/issues/1848
TimeSpec(timespec {
tv_sec: seconds as time_t,
tv_nsec: 0,
})
let mut ts = zero_init_timespec();
ts.tv_sec = seconds as time_t;
TimeSpec(ts)
}

#[inline]
Expand All @@ -292,18 +290,18 @@ impl TimeValLike for TimeSpec {

/// Makes a new `TimeSpec` with given number of nanoseconds.
#[inline]
#[cfg_attr(target_env = "musl", allow(deprecated))]
// https://github.com/rust-lang/libc/issues/1848
fn nanoseconds(nanoseconds: i64) -> TimeSpec {
let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
assert!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
"TimeSpec out of bounds"
);
#[cfg_attr(target_env = "musl", allow(deprecated))]
// https://github.com/rust-lang/libc/issues/1848
TimeSpec(timespec {
tv_sec: secs as time_t,
tv_nsec: nanos as timespec_tv_nsec_t,
})
let mut ts = zero_init_timespec();
ts.tv_sec = secs as time_t;
ts.tv_nsec = nanos as timespec_tv_nsec_t;
TimeSpec(ts)
}

// The cast is not unnecessary on all platforms.
Expand Down Expand Up @@ -337,10 +335,10 @@ impl TimeSpec {
/// Construct a new `TimeSpec` from its components
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
Self(timespec {
tv_sec: seconds,
tv_nsec: nanoseconds,
})
let mut ts = zero_init_timespec();
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
Self(ts)
}

fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
Expand All @@ -360,13 +358,13 @@ impl TimeSpec {
self.0.tv_nsec
}

#[cfg_attr(target_env = "musl", allow(deprecated))]
// https://github.com/rust-lang/libc/issues/1848
pub const fn from_duration(duration: Duration) -> Self {
#[cfg_attr(target_env = "musl", allow(deprecated))]
// https://github.com/rust-lang/libc/issues/1848
TimeSpec(timespec {
tv_sec: duration.as_secs() as time_t,
tv_nsec: duration.subsec_nanos() as timespec_tv_nsec_t,
})
let mut ts = zero_init_timespec();
ts.tv_sec = duration.as_secs() as time_t;
ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
TimeSpec(ts)
}

pub const fn from_timespec(timespec: timespec) -> Self {
Expand Down

0 comments on commit 8b4dad4

Please sign in to comment.