Skip to main content

c_scape/time/
mod.rs

1use errno::{set_errno, Errno};
2use libc::{c_int, c_uint};
3
4use crate::convert_res;
5
6fn rustix_timespec_to_libc_timespec(
7    rustix_time: rustix::time::Timespec,
8) -> Result<libc::timespec, core::num::TryFromIntError> {
9    // SAFETY: libc structs can be zero-initialized freely
10    let mut time: libc::timespec = unsafe { core::mem::zeroed() };
11    time.tv_sec = rustix_time.tv_sec.try_into()?;
12    time.tv_nsec = rustix_time.tv_nsec.try_into()?;
13    Ok(time)
14}
15
16#[no_mangle]
17unsafe extern "C" fn clock_gettime(id: c_int, tp: *mut libc::timespec) -> c_int {
18    libc!(libc::clock_gettime(id, tp));
19
20    let id = match id {
21        libc::CLOCK_MONOTONIC => {
22            rustix::time::DynamicClockId::Known(rustix::time::ClockId::Monotonic)
23        }
24        libc::CLOCK_REALTIME => {
25            rustix::time::DynamicClockId::Known(rustix::time::ClockId::Realtime)
26        }
27        libc::CLOCK_BOOTTIME => rustix::time::DynamicClockId::Boottime,
28        libc::CLOCK_MONOTONIC_COARSE => {
29            rustix::time::DynamicClockId::Known(rustix::time::ClockId::MonotonicCoarse)
30        }
31        libc::CLOCK_REALTIME_COARSE => {
32            rustix::time::DynamicClockId::Known(rustix::time::ClockId::RealtimeCoarse)
33        }
34        libc::CLOCK_MONOTONIC_RAW => {
35            rustix::time::DynamicClockId::Known(rustix::time::ClockId::MonotonicRaw)
36        }
37        libc::CLOCK_THREAD_CPUTIME_ID => {
38            rustix::time::DynamicClockId::Known(rustix::time::ClockId::ThreadCPUTime)
39        }
40        _ => panic!("unimplemented clock_gettime({})", id),
41    };
42
43    let rustix_time = match convert_res(rustix::time::clock_gettime_dynamic(id)) {
44        Some(rustix_time) => rustix_time,
45        None => return -1,
46    };
47
48    match rustix_timespec_to_libc_timespec(rustix_time) {
49        Ok(t) => {
50            *tp = t;
51            0
52        }
53        Err(_) => {
54            set_errno(Errno(libc::EOVERFLOW));
55            -1
56        }
57    }
58}
59
60#[no_mangle]
61unsafe extern "C" fn clock_getres(id: c_int, tp: *mut libc::timespec) -> c_int {
62    libc!(libc::clock_getres(id, tp));
63
64    let id = match id {
65        libc::CLOCK_MONOTONIC => rustix::time::ClockId::Monotonic,
66        libc::CLOCK_REALTIME => rustix::time::ClockId::Realtime,
67        _ => panic!("unimplemented clock_getres({})", id),
68    };
69
70    let rustix_time = rustix::time::clock_getres(id);
71
72    match rustix_timespec_to_libc_timespec(rustix_time) {
73        Ok(t) => {
74            *tp = t;
75            0
76        }
77        Err(_) => {
78            set_errno(Errno(libc::EOVERFLOW));
79            -1
80        }
81    }
82}
83
84#[no_mangle]
85unsafe extern "C" fn time(t: *mut libc::time_t) -> libc::time_t {
86    libc!(libc::time(t));
87
88    let mut ts: libc::timespec = { core::mem::zeroed() };
89    if clock_gettime(libc::CLOCK_REALTIME, &mut ts) == -1 {
90        return -1;
91    }
92
93    if !t.is_null() {
94        *t = ts.tv_sec;
95    }
96
97    ts.tv_sec
98}
99
100#[cfg(not(target_env = "musl"))]
101#[no_mangle]
102unsafe extern "C" fn gettimeofday(t: *mut libc::timeval, _tz: *mut libc::timezone) -> c_int {
103    libc!(libc::gettimeofday(t, _tz));
104    _gettimeofday(t)
105}
106
107#[cfg(target_env = "musl")]
108#[no_mangle]
109unsafe extern "C" fn gettimeofday(t: *mut libc::timeval, _tz: *mut libc::c_void) -> c_int {
110    libc!(libc::gettimeofday(t, _tz));
111    _gettimeofday(t)
112}
113
114unsafe fn _gettimeofday(t: *mut libc::timeval) -> c_int {
115    if t.is_null() {
116        return 0;
117    }
118
119    let mut ts: libc::timespec = { core::mem::zeroed() };
120    if clock_gettime(libc::CLOCK_REALTIME, &mut ts) == -1 {
121        return -1;
122    }
123
124    if !t.is_null() {
125        (*t).tv_sec = ts.tv_sec;
126        (*t).tv_usec = ts.tv_nsec / 1000;
127    }
128
129    0
130}
131
132#[no_mangle]
133unsafe extern "C" fn nanosleep(req: *const libc::timespec, rem: *mut libc::timespec) -> c_int {
134    libc!(libc::nanosleep(req, rem));
135
136    let req = rustix::time::Timespec {
137        tv_sec: (*req).tv_sec.into(),
138        tv_nsec: (*req).tv_nsec as _,
139    };
140    match rustix::thread::nanosleep(&req) {
141        rustix::thread::NanosleepRelativeResult::Ok => 0,
142        rustix::thread::NanosleepRelativeResult::Interrupted(remaining) => {
143            if !rem.is_null() {
144                *rem = libc::timespec {
145                    tv_sec: remaining.tv_sec.try_into().unwrap(),
146                    tv_nsec: remaining.tv_nsec as _,
147                };
148            }
149            set_errno(Errno(libc::EINTR));
150            -1
151        }
152        rustix::thread::NanosleepRelativeResult::Err(err) => {
153            set_errno(Errno(err.raw_os_error()));
154            -1
155        }
156    }
157}
158
159#[no_mangle]
160unsafe extern "C" fn clock_nanosleep(
161    clockid: libc::clockid_t,
162    flags: c_int,
163    req: *const libc::timespec,
164    rem: *mut libc::timespec,
165) -> c_int {
166    libc!(libc::clock_nanosleep(clockid, flags, req, rem));
167
168    let clockid = match clockid {
169        libc::CLOCK_MONOTONIC => rustix::thread::ClockId::Monotonic,
170        libc::CLOCK_REALTIME => rustix::thread::ClockId::Realtime,
171        libc::CLOCK_PROCESS_CPUTIME_ID => rustix::thread::ClockId::ProcessCPUTime,
172        libc::CLOCK_THREAD_CPUTIME_ID => rustix::thread::ClockId::ThreadCPUTime,
173        libc::CLOCK_REALTIME_COARSE => rustix::thread::ClockId::RealtimeCoarse,
174        libc::CLOCK_MONOTONIC_COARSE => rustix::thread::ClockId::MonotonicCoarse,
175        libc::CLOCK_MONOTONIC_RAW => rustix::thread::ClockId::MonotonicRaw,
176        _ => return libc::EINVAL,
177    };
178
179    let req = rustix::time::Timespec {
180        tv_sec: (*req).tv_sec.into(),
181        tv_nsec: (*req).tv_nsec as _,
182    };
183    if flags == libc::TIMER_ABSTIME {
184        match convert_res(rustix::thread::clock_nanosleep_absolute(clockid, &req)) {
185            Some(()) => 0,
186            None => -1,
187        }
188    } else if flags == 0 {
189        match rustix::thread::clock_nanosleep_relative(clockid, &req) {
190            rustix::thread::NanosleepRelativeResult::Ok => 0,
191            rustix::thread::NanosleepRelativeResult::Interrupted(remaining) => {
192                if !rem.is_null() {
193                    *rem = libc::timespec {
194                        tv_sec: remaining.tv_sec.try_into().unwrap(),
195                        tv_nsec: remaining.tv_nsec as _,
196                    };
197                }
198                libc::EINTR
199            }
200            rustix::thread::NanosleepRelativeResult::Err(err) => err.raw_os_error(),
201        }
202    } else {
203        libc::EINVAL
204    }
205}
206
207#[no_mangle]
208unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
209    libc!(libc::sleep(seconds));
210
211    let req = rustix::time::Timespec {
212        tv_sec: seconds.into(),
213        tv_nsec: 0,
214    };
215    match rustix::thread::nanosleep(&req) {
216        rustix::thread::NanosleepRelativeResult::Ok => 0,
217        rustix::thread::NanosleepRelativeResult::Interrupted(remaining) => remaining.tv_sec as _,
218        rustix::thread::NanosleepRelativeResult::Err(_err) => unreachable!(),
219    }
220}
221
222#[no_mangle]
223unsafe extern "C" fn usleep(usec: libc::useconds_t) -> c_int {
224    libc!(libc::usleep(usec));
225
226    let usec: i64 = usec.into();
227    let req = rustix::time::Timespec {
228        tv_sec: usec / 1000000,
229        tv_nsec: (usec % 1000000) * 1000,
230    };
231    match rustix::thread::nanosleep(&req) {
232        rustix::thread::NanosleepRelativeResult::Ok => 0,
233        rustix::thread::NanosleepRelativeResult::Interrupted(_remaining) => {
234            set_errno(Errno(libc::EINTR));
235            -1
236        }
237        rustix::thread::NanosleepRelativeResult::Err(err) => {
238            set_errno(Errno(err.raw_os_error()));
239            -1
240        }
241    }
242}
243
244#[no_mangle]
245unsafe extern "C" fn clock_settime(id: c_int, tp: *mut libc::timespec) -> c_int {
246    libc!(libc::clock_settime(id, tp));
247
248    let id = match id {
249        libc::CLOCK_MONOTONIC => rustix::time::ClockId::Monotonic,
250        libc::CLOCK_REALTIME => rustix::time::ClockId::Realtime,
251        _ => panic!("unimplemented clock({})", id),
252    };
253
254    let timespec = rustix::time::Timespec {
255        tv_sec: (*tp).tv_sec.into(),
256        tv_nsec: (*tp).tv_nsec as _,
257    };
258
259    match convert_res(rustix::time::clock_settime(id, timespec)) {
260        Some(()) => 0,
261        None => -1,
262    }
263}
264
265#[no_mangle]
266unsafe extern "C" fn difftime(time1: libc::time_t, time0: libc::time_t) -> f64 {
267    libc!(libc::difftime(time1, time0));
268
269    (time1 as i128 - time0 as i128) as f64
270}
271
272#[no_mangle]
273unsafe extern "C" fn clock() -> libc::clock_t {
274    //libc!(libc::clock());
275
276    let time = rustix::time::clock_gettime(rustix::time::ClockId::ProcessCPUTime);
277
278    time.tv_sec
279        .checked_mul(1_000_000)
280        .map(|usec| usec + time.tv_nsec / 1000)
281        .unwrap_or(-1)
282        .try_into()
283        .unwrap_or(-1)
284}