kernel/abi/linux/riscv64/
time.rs

1//! Time-related system calls for Linux ABI on RISC-V 64
2//!
3//! This module implements Linux time system calls for the Scarlet kernel,
4//! providing compatibility with Linux userspace programs that need time information.
5
6use super::{
7    errno,
8    signal::{LinuxSignal, SignalState},
9};
10use crate::{
11    abi::linux::riscv64::LinuxRiscv64Abi,
12    arch::Trapframe,
13    sched::scheduler::get_scheduler,
14    task::mytask,
15    time::current_time,
16    timer::{TimerHandler, add_timer, cancel_timer, get_tick, ns_to_ticks, ticks_to_ns},
17};
18use alloc::sync::{Arc, Weak};
19use spin::Mutex;
20
21const NSEC_PER_SEC_I64: i64 = 1_000_000_000;
22const NSEC_PER_SEC_U64: u64 = 1_000_000_000;
23const TIMER_ABSTIME: i32 = 1;
24
25/// Linux POSIX timer shared state guarded by a spin mutex for interior mutability.
26pub struct PosixTimerShared {
27    state: Mutex<PosixTimerState>,
28}
29
30impl PosixTimerShared {
31    fn new(
32        id: u64,
33        clock_id: i32,
34        sigev_notify: i32,
35        sigev_signo: i32,
36        sigev_value: u64,
37        owner_task_id: usize,
38        signal_state: Arc<spin::Mutex<SignalState>>,
39    ) -> Self {
40        Self {
41            state: Mutex::new(PosixTimerState {
42                id,
43                clock_id,
44                sigev_notify,
45                sigev_signo,
46                sigev_value,
47                interval_ns: 0,
48                timer_entry_id: None,
49                next_deadline_tick: None,
50                active: false,
51                owner_task_id,
52                signal_state,
53                overrun_count: 0,
54            }),
55        }
56    }
57
58    fn lock(&self) -> spin::MutexGuard<'_, PosixTimerState> {
59        self.state.lock()
60    }
61}
62
63/// Mutable state stored for each POSIX timer.
64pub struct PosixTimerState {
65    pub id: u64,
66    pub clock_id: i32,
67    pub sigev_notify: i32,
68    pub sigev_signo: i32,
69    pub sigev_value: u64,
70    pub interval_ns: u64,
71    pub timer_entry_id: Option<u64>,
72    pub next_deadline_tick: Option<u64>,
73    pub active: bool,
74    pub owner_task_id: usize,
75    pub signal_state: Arc<spin::Mutex<SignalState>>,
76    pub overrun_count: u32,
77}
78
79/// Public representation of a POSIX timer stored in the Linux ABI state.
80#[derive(Clone)]
81pub struct PosixTimer {
82    pub id: u64,
83    shared: Arc<PosixTimerShared>,
84    handler: Arc<PosixTimerHandler>,
85}
86
87impl PosixTimer {
88    pub fn new(
89        id: u64,
90        clock_id: i32,
91        sigev_notify: i32,
92        sigev_signo: i32,
93        sigev_value: u64,
94        owner_task_id: usize,
95        signal_state: Arc<spin::Mutex<SignalState>>,
96    ) -> Self {
97        let shared = Arc::new(PosixTimerShared::new(
98            id,
99            clock_id,
100            sigev_notify,
101            sigev_signo,
102            sigev_value,
103            owner_task_id,
104            signal_state,
105        ));
106        let handler = Arc::new(PosixTimerHandler {
107            timer_id: id,
108            shared: Arc::downgrade(&shared),
109        });
110        Self {
111            id,
112            shared,
113            handler,
114        }
115    }
116
117    /// Schedule (or reschedule) this timer with the specified first expiration and interval.
118    ///
119    /// A zero `first_ns` disarms the timer.
120    pub fn schedule(&self, first_ns: u64, interval_ns: u64) {
121        let mut state = self.shared.lock();
122        if let Some(entry_id) = state.timer_entry_id.take() {
123            cancel_timer(entry_id);
124        }
125        state.interval_ns = interval_ns;
126        state.overrun_count = 0;
127
128        if first_ns == 0 {
129            state.active = false;
130            state.next_deadline_tick = None;
131            return;
132        }
133
134        let mut ticks = ns_to_ticks(first_ns);
135        if ticks == 0 {
136            ticks = 1;
137        }
138        let target_tick = get_tick().saturating_add(ticks);
139        let handler_dyn: Arc<dyn TimerHandler> = self.handler.clone();
140        let new_id = add_timer(target_tick, &handler_dyn, self.id as usize);
141        state.timer_entry_id = Some(new_id);
142        state.next_deadline_tick = Some(target_tick);
143        state.active = true;
144    }
145
146    /// Cancel any outstanding kernel timer and mark this POSIX timer inactive.
147    pub fn cancel(&self) {
148        let mut state = self.shared.lock();
149        if let Some(entry_id) = state.timer_entry_id.take() {
150            cancel_timer(entry_id);
151        }
152        state.active = false;
153        state.next_deadline_tick = None;
154    }
155
156    /// Snapshot the remaining time (in ns) and current interval (in ns).
157    pub fn snapshot(&self) -> (u64, u64) {
158        let state = self.shared.lock();
159        let interval = state.interval_ns;
160        let remaining = match state.next_deadline_tick {
161            Some(deadline) => {
162                let now = get_tick();
163                if deadline > now {
164                    ticks_to_ns(deadline - now)
165                } else {
166                    0
167                }
168            }
169            None => 0,
170        };
171        (remaining, interval)
172    }
173
174    pub fn state(&self) -> spin::MutexGuard<'_, PosixTimerState> {
175        self.shared.lock()
176    }
177}
178
179struct PosixTimerHandler {
180    timer_id: u64,
181    shared: Weak<PosixTimerShared>,
182}
183
184impl TimerHandler for PosixTimerHandler {
185    fn on_timer_expired(self: Arc<Self>, _context: usize) {
186        let Some(shared) = self.shared.upgrade() else {
187            return;
188        };
189
190        // Snapshot data while holding the state lock
191        let (notify, signo, interval_ns, owner_task_id, signal_state) = {
192            let mut state = shared.lock();
193            if !state.active {
194                return;
195            }
196            state.timer_entry_id = None;
197            state.next_deadline_tick = None;
198            (
199                state.sigev_notify,
200                state.sigev_signo,
201                state.interval_ns,
202                state.owner_task_id,
203                state.signal_state.clone(),
204            )
205        };
206
207        // Deliver notifications outside of the lock
208        if notify == SIGEV_SIGNAL {
209            if let Some(signal) = LinuxSignal::from_u32(signo as u32) {
210                let mut locked = signal_state.lock();
211                locked.add_pending(signal);
212                drop(locked);
213                let scheduler = get_scheduler();
214                let _ = scheduler.wake_task(owner_task_id);
215            }
216        }
217
218        if notify == SIGEV_NONE {
219            // No wake required for SIGEV_NONE.
220        }
221
222        // Re-arm periodic timers.
223        if interval_ns > 0 {
224            let mut state = shared.lock();
225            let mut ticks = ns_to_ticks(interval_ns);
226            if ticks == 0 {
227                ticks = 1;
228            }
229            let target_tick = get_tick().saturating_add(ticks);
230            let handler_dyn: Arc<dyn TimerHandler> = self.clone();
231            let entry_id = add_timer(target_tick, &handler_dyn, self.timer_id as usize);
232            state.timer_entry_id = Some(entry_id);
233            state.next_deadline_tick = Some(target_tick);
234            state.active = true;
235        } else {
236            let mut state = shared.lock();
237            state.active = false;
238        }
239    }
240}
241
242/// Linux sigevent notification values (subset).
243pub const SIGEV_SIGNAL: i32 = 0;
244pub const SIGEV_NONE: i32 = 1;
245pub const SIGEV_THREAD: i32 = 2;
246pub const SIGEV_THREAD_ID: i32 = 4;
247
248fn is_supported_clock(clock_id: i32) -> bool {
249    matches!(
250        clock_id,
251        CLOCK_REALTIME
252            | CLOCK_MONOTONIC
253            | CLOCK_PROCESS_CPUTIME_ID
254            | CLOCK_THREAD_CPUTIME_ID
255            | CLOCK_MONOTONIC_RAW
256            | CLOCK_REALTIME_COARSE
257            | CLOCK_MONOTONIC_COARSE
258            | CLOCK_BOOTTIME
259    )
260}
261
262fn timespec_to_ns(ts: &TimeSpec) -> Result<u64, usize> {
263    if ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= NSEC_PER_SEC_I64 {
264        return Err(errno::EINVAL);
265    }
266    let sec = ts.tv_sec as u128;
267    let nsec = ts.tv_nsec as u128;
268    let total = sec
269        .saturating_mul(NSEC_PER_SEC_U64 as u128)
270        .saturating_add(nsec);
271    Ok(total.min(u64::MAX as u128) as u64)
272}
273
274fn ns_to_timespec(ns: u64) -> TimeSpec {
275    let sec = (ns / NSEC_PER_SEC_U64).min(i64::MAX as u64);
276    let nsec = if sec >= i64::MAX as u64 {
277        (NSEC_PER_SEC_U64 - 1) as i64
278    } else {
279        (ns % NSEC_PER_SEC_U64) as i64
280    };
281    TimeSpec {
282        tv_sec: sec as i64,
283        tv_nsec: nsec,
284    }
285}
286
287/// Linux timespec structure (matches Linux userspace)
288#[repr(C)]
289#[derive(Debug, Clone, Copy)]
290pub struct TimeSpec {
291    pub tv_sec: i64,  // seconds
292    pub tv_nsec: i64, // nanoseconds
293}
294
295/// Linux itimerspec structure used by timer_settime/gettime.
296#[repr(C)]
297#[derive(Clone, Copy)]
298struct ItimerSpec {
299    it_interval: TimeSpec,
300    it_value: TimeSpec,
301}
302
303/// Linux clock IDs (subset of commonly used ones)
304pub const CLOCK_REALTIME: i32 = 0;
305pub const CLOCK_MONOTONIC: i32 = 1;
306pub const CLOCK_PROCESS_CPUTIME_ID: i32 = 2;
307pub const CLOCK_THREAD_CPUTIME_ID: i32 = 3;
308pub const CLOCK_MONOTONIC_RAW: i32 = 4;
309pub const CLOCK_REALTIME_COARSE: i32 = 5;
310pub const CLOCK_MONOTONIC_COARSE: i32 = 6;
311pub const CLOCK_BOOTTIME: i32 = 7;
312
313/// Linux `timer_create` implementation.
314///
315/// Returns 0 on success or negative errno on failure.
316pub fn sys_timer_create(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
317    let task = match mytask() {
318        Some(task) => task,
319        None => return errno::to_result(errno::EFAULT),
320    };
321
322    let clock_id = trapframe.get_arg(0) as i32;
323    let sevp_ptr = trapframe.get_arg(1);
324    let timerid_ptr = trapframe.get_arg(2);
325
326    trapframe.increment_pc_next(&task);
327
328    if !is_supported_clock(clock_id) {
329        return errno::to_result(errno::EINVAL);
330    }
331
332    if timerid_ptr == 0 {
333        return errno::to_result(errno::EFAULT);
334    }
335
336    let timerid_paddr = match task.vm_manager.translate_vaddr(timerid_ptr) {
337        Some(ptr) => ptr as *mut u64,
338        None => return errno::to_result(errno::EFAULT),
339    };
340
341    // Defaults if user does not supply struct sigevent
342    let mut sigev_notify = SIGEV_SIGNAL;
343    let mut sigev_signo = LinuxSignal::SIGALRM as i32;
344    let mut sigev_value = 0u64;
345
346    if sevp_ptr != 0 {
347        let sevp_paddr = match task.vm_manager.translate_vaddr(sevp_ptr) {
348            Some(addr) => addr,
349            None => return errno::to_result(errno::EFAULT),
350        };
351
352        unsafe {
353            let base = sevp_paddr as *const u8;
354            sigev_value = *(base as *const u64);
355            sigev_signo = *(base.add(8) as *const i32);
356            sigev_notify = *(base.add(12) as *const i32);
357        }
358
359        match sigev_notify {
360            SIGEV_SIGNAL => {
361                if sigev_signo <= 0 || LinuxSignal::from_u32(sigev_signo as u32).is_none() {
362                    return errno::to_result(errno::EINVAL);
363                }
364            }
365            SIGEV_NONE => {
366                // No additional validation needed
367            }
368            SIGEV_THREAD | SIGEV_THREAD_ID => {
369                // Thread-based notifications require pthread helpers we do not have yet.
370                return errno::to_result(errno::ENOSYS);
371            }
372            _ => {
373                return errno::to_result(errno::EINVAL);
374            }
375        }
376    }
377
378    let timer_id = abi.allocate_posix_timer_id();
379    let timer = PosixTimer::new(
380        timer_id,
381        clock_id,
382        sigev_notify,
383        sigev_signo,
384        sigev_value,
385        task.get_id(),
386        abi.signal_state.clone(),
387    );
388    abi.store_posix_timer(timer);
389
390    unsafe {
391        *timerid_paddr = timer_id as u64;
392    }
393
394    trapframe.set_return_value(0);
395    0
396}
397
398/// Linux `timer_settime` implementation.
399pub fn sys_timer_settime(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
400    let task = match mytask() {
401        Some(task) => task,
402        None => return errno::to_result(errno::EFAULT),
403    };
404
405    let timer_id = trapframe.get_arg(0) as u64;
406    let flags = trapframe.get_arg(1) as i32;
407    let new_value_ptr = trapframe.get_arg(2);
408    let old_value_ptr = trapframe.get_arg(3);
409
410    trapframe.increment_pc_next(&task);
411
412    if (flags & !TIMER_ABSTIME) != 0 {
413        return errno::to_result(errno::EINVAL);
414    }
415    if (flags & TIMER_ABSTIME) != 0 {
416        return errno::to_result(errno::ENOSYS);
417    }
418    if new_value_ptr == 0 {
419        return errno::to_result(errno::EINVAL);
420    }
421
422    let timer = match abi.get_posix_timer(timer_id) {
423        Some(timer) => timer,
424        None => return errno::to_result(errno::EINVAL),
425    };
426
427    let new_value_paddr = match task.vm_manager.translate_vaddr(new_value_ptr) {
428        Some(addr) => addr,
429        None => return errno::to_result(errno::EFAULT),
430    };
431
432    let new_spec = unsafe { *(new_value_paddr as *const ItimerSpec) };
433
434    let first_ns = match timespec_to_ns(&new_spec.it_value) {
435        Ok(ns) => ns,
436        Err(errno_val) => return errno::to_result(errno_val),
437    };
438    let interval_ns = match timespec_to_ns(&new_spec.it_interval) {
439        Ok(ns) => ns,
440        Err(errno_val) => return errno::to_result(errno_val),
441    };
442
443    if old_value_ptr != 0 {
444        let old_value_paddr = match task.vm_manager.translate_vaddr(old_value_ptr) {
445            Some(addr) => addr as *mut ItimerSpec,
446            None => return errno::to_result(errno::EFAULT),
447        };
448        let (remaining_ns, previous_interval_ns) = timer.snapshot();
449        let snapshot = ItimerSpec {
450            it_interval: ns_to_timespec(previous_interval_ns),
451            it_value: ns_to_timespec(remaining_ns),
452        };
453        unsafe {
454            *old_value_paddr = snapshot;
455        }
456    }
457
458    timer.schedule(first_ns, interval_ns);
459
460    trapframe.set_return_value(0);
461    0
462}
463
464/// Linux `timer_gettime` implementation.
465pub fn sys_timer_gettime(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
466    let task = match mytask() {
467        Some(task) => task,
468        None => return errno::to_result(errno::EFAULT),
469    };
470
471    let timer_id = trapframe.get_arg(0) as u64;
472    let setting_ptr = trapframe.get_arg(1);
473
474    trapframe.increment_pc_next(&task);
475
476    if setting_ptr == 0 {
477        return errno::to_result(errno::EFAULT);
478    }
479
480    let timer = match abi.get_posix_timer(timer_id) {
481        Some(timer) => timer,
482        None => return errno::to_result(errno::EINVAL),
483    };
484
485    let setting_paddr = match task.vm_manager.translate_vaddr(setting_ptr) {
486        Some(addr) => addr as *mut ItimerSpec,
487        None => return errno::to_result(errno::EFAULT),
488    };
489
490    let (remaining_ns, interval_ns) = timer.snapshot();
491    let current = ItimerSpec {
492        it_interval: ns_to_timespec(interval_ns),
493        it_value: ns_to_timespec(remaining_ns),
494    };
495
496    unsafe {
497        *setting_paddr = current;
498    }
499
500    trapframe.set_return_value(0);
501    0
502}
503
504/// Linux `timer_getoverrun` implementation (simple stub returning 0).
505pub fn sys_timer_getoverrun(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
506    let task = match mytask() {
507        Some(task) => task,
508        None => return errno::to_result(errno::EFAULT),
509    };
510
511    let timer_id = trapframe.get_arg(0) as u64;
512
513    trapframe.increment_pc_next(&task);
514
515    let timer = match abi.get_posix_timer(timer_id) {
516        Some(timer) => timer,
517        None => return errno::to_result(errno::EINVAL),
518    };
519
520    let overrun = {
521        let state = timer.state();
522        state.overrun_count as usize
523    };
524
525    trapframe.set_return_value(overrun);
526    overrun
527}
528
529/// Linux `timer_delete` implementation.
530pub fn sys_timer_delete(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
531    let task = match mytask() {
532        Some(task) => task,
533        None => return errno::to_result(errno::EFAULT),
534    };
535
536    let timer_id = trapframe.get_arg(0) as u64;
537
538    trapframe.increment_pc_next(&task);
539
540    let timer = match abi.remove_posix_timer(timer_id) {
541        Some(timer) => timer,
542        None => return errno::to_result(errno::EINVAL),
543    };
544
545    timer.cancel();
546
547    trapframe.set_return_value(0);
548    0
549}
550
551/// sys_clock_gettime - Get time from specified clock
552///
553/// Arguments:
554/// - a0 (x10): clock_id - which clock to read from
555/// - a1 (x11): timespec - pointer to timespec structure to fill
556///
557/// Returns:
558/// - 0 on success
559/// - -EINVAL (-22) for invalid clock_id
560/// - -EFAULT (-14) for invalid timespec pointer
561pub fn sys_clock_gettime(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
562    let task = mytask().expect("No current task found");
563    let clock_id = trapframe.get_arg(0) as i32; // a0
564
565    trapframe.increment_pc_next(&task);
566
567    let timespec_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
568        Some(ptr) => ptr as *mut TimeSpec,   // a1
569        None => return (-14_isize) as usize, // -EFAULT
570    };
571
572    // Get the current time based on the clock type
573    let timespec = match clock_id {
574        CLOCK_REALTIME | CLOCK_REALTIME_COARSE => {
575            // For now, we use the same monotonic time for realtime
576            // In a full implementation, this would be adjusted to Unix epoch
577            let time_us = current_time();
578            TimeSpec {
579                tv_sec: (time_us / 1_000_000) as i64,
580                tv_nsec: ((time_us % 1_000_000) * 1000) as i64,
581            }
582        }
583        CLOCK_MONOTONIC | CLOCK_MONOTONIC_RAW | CLOCK_MONOTONIC_COARSE => {
584            // Monotonic time since boot
585            let time_us = current_time();
586            TimeSpec {
587                tv_sec: (time_us / 1_000_000) as i64,
588                tv_nsec: ((time_us % 1_000_000) * 1000) as i64,
589            }
590        }
591        CLOCK_BOOTTIME => {
592            // Boot time (same as monotonic for now)
593            let time_us = current_time();
594            TimeSpec {
595                tv_sec: (time_us / 1_000_000) as i64,
596                tv_nsec: ((time_us % 1_000_000) * 1000) as i64,
597            }
598        }
599        CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID => {
600            // CPU time for process/thread (simplified implementation)
601            // In a full implementation, this would track actual CPU time
602            let time_us = current_time();
603            TimeSpec {
604                tv_sec: (time_us / 1_000_000) as i64,
605                tv_nsec: ((time_us % 1_000_000) * 1000) as i64,
606            }
607        }
608        _ => {
609            return (-22_isize) as usize; // -EINVAL
610        }
611    };
612
613    // Write the timespec to user space
614    unsafe {
615        *timespec_ptr = timespec;
616    }
617
618    0 // Success
619}
620
621/// sys_nanosleep - Sleep for the specified time (Linux ABI)
622///
623/// Arguments:
624/// - a0 (x10): rqtp - pointer to requested sleep time (struct __kernel_timespec __user *)
625/// - a1 (x11): rmtp - pointer to remaining time (struct __kernel_timespec __user *)
626///
627/// Returns:
628/// - 0 on success
629/// - -EFAULT (-14) for invalid pointer
630/// - -EINTR (-4) if interrupted by signal (not implemented, always 0)
631pub fn sys_nanosleep(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
632    // Get current task
633    let task = match mytask() {
634        Some(task) => task,
635        None => return (-14_isize) as usize, // -EFAULT
636    };
637    trapframe.increment_pc_next(&task);
638
639    // Get user pointer to requested timespec
640    let rqtp_ptr = trapframe.get_arg(0);
641    let _rmtp_ptr = trapframe.get_arg(1);
642    let rqtp = match task.vm_manager.translate_vaddr(rqtp_ptr) {
643        Some(ptr) => unsafe { &*(ptr as *const TimeSpec) },
644        None => return (-14_isize) as usize, // -EFAULT
645    };
646    // Convert timespec to nanoseconds
647    let ns = rqtp
648        .tv_sec
649        .saturating_mul(1_000_000_000)
650        .saturating_add(rqtp.tv_nsec);
651    if ns <= 0 {
652        return 0;
653    }
654    // Convert nanoseconds to kernel ticks
655    let ticks = ns_to_ticks(ns as u64);
656    trapframe.set_return_value(0); // Set return value to 0 (success)
657    // Sleep the current task for the specified ticks
658    task.sleep(trapframe, ticks);
659    // If sleep is successful, this will not be reached. If interrupted, return -EINTR (not implemented)
660    0
661}
662
663/// Linux sys_clock_getres implementation (stub)
664///
665/// Get clock resolution. This is a stub implementation that
666/// returns a reasonable resolution for the specified clock.
667///
668/// Arguments:
669/// - abi: LinuxRiscv64Abi context
670/// - trapframe: Trapframe containing syscall arguments
671///   - arg0: clk_id (clock ID)
672///   - arg1: res (pointer to timespec structure for resolution)
673///
674/// Returns:
675/// - 0 on success
676/// - usize::MAX on error
677pub fn sys_clock_getres(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
678    let task = match mytask() {
679        Some(t) => t,
680        None => return usize::MAX,
681    };
682
683    let _clk_id = trapframe.get_arg(0) as i32;
684    let res_ptr = trapframe.get_arg(1);
685
686    // Increment PC to avoid infinite loop
687    trapframe.increment_pc_next(task);
688
689    // If res pointer is provided, write resolution
690    if res_ptr != 0 {
691        if let Some(res_paddr) = task.vm_manager.translate_vaddr(res_ptr) {
692            unsafe {
693                // Write timespec structure with nanosecond resolution
694                // struct timespec { long tv_sec; long tv_nsec; }
695                let timespec = res_paddr as *mut [u64; 2];
696                *timespec = [
697                    0,         // tv_sec = 0
698                    1_000_000, // tv_nsec = 1 millisecond (reasonable resolution)
699                ];
700            }
701        }
702    }
703
704    0 // Always succeed
705}
706
707#[cfg(test)]
708mod tests {
709    use super::*;
710
711    #[test_case]
712    fn test_timespec_size() {
713        // Ensure TimeSpec matches Linux ABI
714        assert_eq!(core::mem::size_of::<TimeSpec>(), 16);
715        assert_eq!(core::mem::align_of::<TimeSpec>(), 8);
716    }
717
718    #[test_case]
719    fn test_clock_constants() {
720        // Verify clock constants match Linux values
721        assert_eq!(CLOCK_REALTIME, 0);
722        assert_eq!(CLOCK_MONOTONIC, 1);
723        assert_eq!(CLOCK_PROCESS_CPUTIME_ID, 2);
724        assert_eq!(CLOCK_THREAD_CPUTIME_ID, 3);
725    }
726}