Use u128 for time, store CPU time of processes
This commit is contained in:
@@ -19,7 +19,7 @@ static mut PL031_RTC: Pl031rtc = Pl031rtc {
|
||||
|
||||
pub unsafe fn init() {
|
||||
PL031_RTC.init();
|
||||
time::START.lock().0 = PL031_RTC.time();
|
||||
*time::START.lock() = (PL031_RTC.time() as u128) * time::NANOS_PER_SEC;
|
||||
}
|
||||
|
||||
struct Pl031rtc {
|
||||
|
||||
@@ -51,10 +51,7 @@ pub unsafe fn irq_handler_com1(irq: u32) {
|
||||
pub unsafe fn irq_handler_gentimer(irq: u32) {
|
||||
GENTIMER.clear_irq();
|
||||
{
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + GENTIMER.clk_freq as u64;
|
||||
offset.1 = sum % 1_000_000_000;
|
||||
offset.0 += sum / 1_000_000_000;
|
||||
*time::OFFSET.lock() += GENTIMER.clk_freq as u128;
|
||||
}
|
||||
|
||||
timeout::trigger();
|
||||
|
||||
@@ -15,3 +15,10 @@ pub unsafe fn init() {
|
||||
CHAN0.write((CHAN0_DIVISOR & 0xFF) as u8);
|
||||
CHAN0.write((CHAN0_DIVISOR >> 8) as u8);
|
||||
}
|
||||
|
||||
pub unsafe fn read() -> u16 {
|
||||
COMMAND.write(SELECT_CHAN0 | 0);
|
||||
let low = CHAN0.read();
|
||||
let high = CHAN0.read();
|
||||
((high as u16) << 8) | (low as u16)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::time;
|
||||
|
||||
pub fn init() {
|
||||
let mut rtc = Rtc::new();
|
||||
time::START.lock().0 = rtc.time();
|
||||
*time::START.lock() = (rtc.time() as u128) * time::NANOS_PER_SEC;
|
||||
}
|
||||
|
||||
fn cvt_bcd(value: usize) -> usize {
|
||||
|
||||
@@ -11,6 +11,8 @@ use crate::scheme::debug::{debug_input, debug_notify};
|
||||
use crate::scheme::serio::serio_input;
|
||||
use crate::{context, time};
|
||||
|
||||
pub const PIT_RATE: u128 = 2_250_286;
|
||||
|
||||
//resets to 0 in context::switch()
|
||||
#[thread_local]
|
||||
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
|
||||
@@ -137,13 +139,8 @@ unsafe fn ioapic_unmask(irq: usize) {
|
||||
interrupt_stack!(pit_stack, |_stack| {
|
||||
// Saves CPU time by not sending IRQ event irq_trigger(0);
|
||||
|
||||
const PIT_RATE: u64 = 2_250_286;
|
||||
|
||||
{
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + PIT_RATE;
|
||||
offset.1 = sum % 1_000_000_000;
|
||||
offset.0 += sum / 1_000_000_000;
|
||||
*time::OFFSET.lock() += PIT_RATE;
|
||||
}
|
||||
|
||||
eoi(0);
|
||||
@@ -270,13 +267,8 @@ interrupt!(lapic_error, || {
|
||||
});
|
||||
|
||||
interrupt!(calib_pit, || {
|
||||
const PIT_RATE: u64 = 2_250_286;
|
||||
|
||||
{
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + PIT_RATE;
|
||||
offset.1 = sum % 1_000_000_000;
|
||||
offset.0 += sum / 1_000_000_000;
|
||||
*time::OFFSET.lock() += PIT_RATE;
|
||||
}
|
||||
|
||||
eoi(0);
|
||||
|
||||
@@ -160,7 +160,7 @@ pub unsafe fn init(
|
||||
let flush_all = map_percpu(cpu_id, KernelMapper::lock_manually(cpu_id).get_mut().expect("expected KernelMapper not to be locked re-entrant in paging::init"));
|
||||
flush_all.flush();
|
||||
|
||||
return init_tcb(cpu_id);
|
||||
init_tcb(cpu_id)
|
||||
}
|
||||
|
||||
pub unsafe fn init_ap(
|
||||
|
||||
@@ -96,9 +96,9 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
// Initialize logger
|
||||
log::init_logger(|r| {
|
||||
use core::fmt::Write;
|
||||
let _ = write!(
|
||||
let _ = writeln!(
|
||||
super::debug::Writer::new(),
|
||||
"{}:{} -- {}\n",
|
||||
"{}:{} -- {}",
|
||||
r.target(),
|
||||
r.level(),
|
||||
r.args()
|
||||
|
||||
@@ -40,7 +40,7 @@ fn userspace_acpi_shutdown() {
|
||||
return;
|
||||
}
|
||||
log::info!("Waiting one second for ACPI driver to run the shutdown sequence.");
|
||||
let (initial_s, initial_ns) = time::monotonic();
|
||||
let initial = time::monotonic();
|
||||
|
||||
// Since this driver is a userspace process, and we do not use any magic like directly
|
||||
// context switching, we have to wait for the userspace driver to complete, with a timeout.
|
||||
@@ -51,13 +51,9 @@ fn userspace_acpi_shutdown() {
|
||||
// event flag like EVENT_DIRECT, which has already been suggested for IRQs.
|
||||
// TODO: Waitpid with timeout? Because, what if the ACPI driver would crash?
|
||||
let _ = unsafe { context::switch() };
|
||||
let (current_s, current_ns) = time::monotonic();
|
||||
|
||||
let diff_s = current_s - initial_s;
|
||||
let diff_part_ns = current_ns - initial_ns;
|
||||
let diff_ns = diff_s * 1_000_000_000 + diff_part_ns;
|
||||
|
||||
if diff_ns > 1_000_000_000 {
|
||||
let current = time::monotonic();
|
||||
if current - initial > time::NANOS_PER_SEC {
|
||||
log::info!("Timeout reached, thus falling back to other shutdown methods.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,3 +15,10 @@ pub unsafe fn init() {
|
||||
CHAN0.write((CHAN0_DIVISOR & 0xFF) as u8);
|
||||
CHAN0.write((CHAN0_DIVISOR >> 8) as u8);
|
||||
}
|
||||
|
||||
pub unsafe fn read() -> u16 {
|
||||
COMMAND.write(SELECT_CHAN0 | 0);
|
||||
let low = CHAN0.read();
|
||||
let high = CHAN0.read();
|
||||
((high as u16) << 8) | (low as u16)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::time;
|
||||
|
||||
pub fn init() {
|
||||
let mut rtc = Rtc::new();
|
||||
time::START.lock().0 = rtc.time();
|
||||
*time::START.lock() = (rtc.time() as u128) * time::NANOS_PER_SEC;
|
||||
}
|
||||
|
||||
fn cvt_bcd(value: usize) -> usize {
|
||||
|
||||
@@ -11,6 +11,8 @@ use crate::scheme::debug::{debug_input, debug_notify};
|
||||
use crate::scheme::serio::serio_input;
|
||||
use crate::{context, time};
|
||||
|
||||
pub const PIT_RATE: u128 = 2_250_286;
|
||||
|
||||
//resets to 0 in context::switch()
|
||||
#[thread_local]
|
||||
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
|
||||
@@ -137,13 +139,8 @@ unsafe fn ioapic_unmask(irq: usize) {
|
||||
interrupt_stack!(pit_stack, |_stack| {
|
||||
// Saves CPU time by not sending IRQ event irq_trigger(0);
|
||||
|
||||
const PIT_RATE: u64 = 2_250_286;
|
||||
|
||||
{
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + PIT_RATE;
|
||||
offset.1 = sum % 1_000_000_000;
|
||||
offset.0 += sum / 1_000_000_000;
|
||||
*time::OFFSET.lock() += PIT_RATE;
|
||||
}
|
||||
|
||||
eoi(0);
|
||||
@@ -270,13 +267,8 @@ interrupt!(lapic_error, || {
|
||||
});
|
||||
|
||||
interrupt!(calib_pit, || {
|
||||
const PIT_RATE: u64 = 2_250_286;
|
||||
|
||||
{
|
||||
let mut offset = time::OFFSET.lock();
|
||||
let sum = offset.1 + PIT_RATE;
|
||||
offset.1 = sum % 1_000_000_000;
|
||||
offset.0 += sum / 1_000_000_000;
|
||||
*time::OFFSET.lock() += PIT_RATE;
|
||||
}
|
||||
|
||||
eoi(0);
|
||||
|
||||
@@ -413,6 +413,8 @@ pub unsafe fn init(
|
||||
log::warn!("{:X}:{:X} overlaps with acpi {:X}:{:X}", base, size, acpi_base, acpi_size);
|
||||
new_base = cmp::max(new_base, acpi_end);
|
||||
}
|
||||
|
||||
// Ensure initfs areas are not used
|
||||
if base < initfs_end && base + size > initfs_base {
|
||||
log::warn!("{:X}:{:X} overlaps with initfs {:X}:{:X}", base, size, initfs_base, initfs_size);
|
||||
new_base = cmp::max(new_base, initfs_end);
|
||||
@@ -427,7 +429,7 @@ pub unsafe fn init(
|
||||
}
|
||||
|
||||
if size == 0 {
|
||||
// Area is zero sized
|
||||
// Area is zero sized, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ fn userspace_acpi_shutdown() {
|
||||
return;
|
||||
}
|
||||
log::info!("Waiting one second for ACPI driver to run the shutdown sequence.");
|
||||
let (initial_s, initial_ns) = time::monotonic();
|
||||
let initial = time::monotonic();
|
||||
|
||||
// Since this driver is a userspace process, and we do not use any magic like directly
|
||||
// context switching, we have to wait for the userspace driver to complete, with a timeout.
|
||||
@@ -51,13 +51,9 @@ fn userspace_acpi_shutdown() {
|
||||
// event flag like EVENT_DIRECT, which has already been suggested for IRQs.
|
||||
// TODO: Waitpid with timeout? Because, what if the ACPI driver would crash?
|
||||
let _ = unsafe { context::switch() };
|
||||
let (current_s, current_ns) = time::monotonic();
|
||||
|
||||
let diff_s = current_s - initial_s;
|
||||
let diff_part_ns = current_ns - initial_ns;
|
||||
let diff_ns = diff_s * 1_000_000_000 + diff_part_ns;
|
||||
|
||||
if diff_ns > 1_000_000_000 {
|
||||
let current = time::monotonic();
|
||||
if current - initial > time::NANOS_PER_SEC {
|
||||
log::info!("Timeout reached, thus falling back to other shutdown methods.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ pub struct ContextSnapshot {
|
||||
pub status_reason: &'static str,
|
||||
pub running: bool,
|
||||
pub cpu_id: Option<usize>,
|
||||
pub ticks: u64,
|
||||
pub cpu_time: u128,
|
||||
pub syscall: Option<(usize, usize, usize, usize, usize, usize)>,
|
||||
// Clone fields
|
||||
//TODO: is there a faster way than allocation?
|
||||
@@ -160,7 +160,7 @@ impl ContextSnapshot {
|
||||
status_reason: context.status_reason,
|
||||
running: context.running,
|
||||
cpu_id: context.cpu_id,
|
||||
ticks: context.ticks,
|
||||
cpu_time: context.cpu_time,
|
||||
syscall: context.syscall,
|
||||
name,
|
||||
files,
|
||||
@@ -200,8 +200,10 @@ pub struct Context {
|
||||
pub running: bool,
|
||||
/// CPU ID, if locked
|
||||
pub cpu_id: Option<usize>,
|
||||
/// Number of timer ticks executed
|
||||
pub ticks: u64,
|
||||
/// Time this context was switched to
|
||||
pub switch_time: u128,
|
||||
/// Amount of CPU time used
|
||||
pub cpu_time: u128,
|
||||
/// Current system call
|
||||
pub syscall: Option<(usize, usize, usize, usize, usize, usize)>,
|
||||
/// Head buffer to use when system call buffers are not page aligned
|
||||
@@ -215,7 +217,7 @@ pub struct Context {
|
||||
/// Context should handle pending signals
|
||||
pub pending: VecDeque<u8>,
|
||||
/// Context should wake up at specified time
|
||||
pub wake: Option<(u64, u64)>,
|
||||
pub wake: Option<u128>,
|
||||
/// The architecture specific context
|
||||
pub arch: arch::Context,
|
||||
/// Kernel FX - used to store SIMD and FPU registers on context switch
|
||||
@@ -348,7 +350,8 @@ impl Context {
|
||||
status_reason: "",
|
||||
running: false,
|
||||
cpu_id: None,
|
||||
ticks: 0,
|
||||
switch_time: 0,
|
||||
cpu_time: 0,
|
||||
syscall: None,
|
||||
syscall_head,
|
||||
syscall_tail,
|
||||
|
||||
@@ -57,7 +57,7 @@ unsafe fn update(context: &mut Context, cpu_id: usize) {
|
||||
let wake = context.wake.expect("context::switch: wake not set");
|
||||
|
||||
let current = time::monotonic();
|
||||
if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) {
|
||||
if current >= wake {
|
||||
context.wake = None;
|
||||
context.unblock();
|
||||
}
|
||||
@@ -96,7 +96,7 @@ unsafe fn runnable(context: &Context, cpu_id: usize) -> bool {
|
||||
pub unsafe fn switch() -> bool {
|
||||
// TODO: Better memory orderings?
|
||||
//set PIT Interrupt counter to 0, giving each process same amount of PIT ticks
|
||||
let ticks = PIT_TICKS.swap(0, Ordering::SeqCst);
|
||||
let _ticks = PIT_TICKS.swap(0, Ordering::SeqCst);
|
||||
|
||||
// Set the global lock to avoid the unsafe operations below from causing issues
|
||||
while arch::CONTEXT_SWITCH_LOCK.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Relaxed).is_err() {
|
||||
@@ -104,6 +104,7 @@ pub unsafe fn switch() -> bool {
|
||||
}
|
||||
|
||||
let cpu_id = crate::cpu_id();
|
||||
let switch_time = crate::time::monotonic();
|
||||
|
||||
let from_context_lock;
|
||||
let mut from_context_guard;
|
||||
@@ -116,7 +117,6 @@ pub unsafe fn switch() -> bool {
|
||||
.current()
|
||||
.expect("context::switch: not inside of context"));
|
||||
from_context_guard = from_context_lock.write();
|
||||
from_context_guard.ticks += ticks as u64 + 1; // Always round ticks up
|
||||
}
|
||||
|
||||
for (pid, context_lock) in contexts.iter() {
|
||||
@@ -162,8 +162,14 @@ pub unsafe fn switch() -> bool {
|
||||
if let Some((to_context_lock, to_ptr)) = to_context_lock {
|
||||
let to_context: &mut Context = &mut *to_ptr;
|
||||
|
||||
// Set old context as not running and update CPU time
|
||||
from_context_guard.running = false;
|
||||
from_context_guard.cpu_time += switch_time - from_context_guard.switch_time;
|
||||
|
||||
// Set new context as running and set switch time
|
||||
to_context.running = true;
|
||||
to_context.switch_time = switch_time;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
if let Some(ref stack) = to_context.kstack {
|
||||
|
||||
@@ -12,7 +12,7 @@ struct Timeout {
|
||||
pub scheme_id: SchemeId,
|
||||
pub event_id: usize,
|
||||
pub clock: usize,
|
||||
pub time: (u64, u64),
|
||||
pub time: u128,
|
||||
}
|
||||
|
||||
type Registry = VecDeque<Timeout>;
|
||||
@@ -35,7 +35,7 @@ pub fn register(scheme_id: SchemeId, event_id: usize, clock: usize, time: TimeSp
|
||||
scheme_id,
|
||||
event_id,
|
||||
clock,
|
||||
time: (time.tv_sec as u64, time.tv_nsec as u64)
|
||||
time: (time.tv_sec as u128 * time::NANOS_PER_SEC) + (time.tv_nsec as u128)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ pub fn trigger() {
|
||||
let trigger = match registry[i].clock {
|
||||
CLOCK_MONOTONIC => {
|
||||
let time = registry[i].time;
|
||||
mono.0 > time.0 || (mono.0 == time.0 && mono.1 >= time.1)
|
||||
mono >= time
|
||||
},
|
||||
CLOCK_REALTIME => {
|
||||
let time = registry[i].time;
|
||||
real.0 > time.0 || (real.0 == time.0 && real.1 >= time.1)
|
||||
real >= time
|
||||
},
|
||||
clock => {
|
||||
println!("timeout::trigger: unknown clock {}", clock);
|
||||
|
||||
@@ -63,18 +63,13 @@ pub fn resource() -> Result<Vec<u8>> {
|
||||
format!("?")
|
||||
};
|
||||
|
||||
let ticks = context.ticks;
|
||||
let ticks_string = if ticks >= 1000 * 1000 * 1000 * 1000 {
|
||||
format!("{} T", ticks / 1000 / 1000 / 1000 / 1000)
|
||||
} else if ticks >= 1000 * 1000 * 1000 {
|
||||
format!("{} G", ticks / 1000 / 1000 / 1000)
|
||||
} else if ticks >= 1000 * 1000 {
|
||||
format!("{} M", ticks / 1000 / 1000)
|
||||
} else if ticks >= 1000 {
|
||||
format!("{} K", ticks / 1000)
|
||||
} else {
|
||||
format!("{}", ticks)
|
||||
};
|
||||
let cpu_time = context.cpu_time / crate::time::NANOS_PER_SEC;
|
||||
let cpu_time_string = format!(
|
||||
"{:02}:{:02}:{:02}",
|
||||
cpu_time / 3600,
|
||||
(cpu_time / 60) % 60,
|
||||
cpu_time % 60
|
||||
);
|
||||
|
||||
let mut memory = context.kfx.len();
|
||||
if let Some(ref kstack) = context.kstack {
|
||||
@@ -98,7 +93,7 @@ pub fn resource() -> Result<Vec<u8>> {
|
||||
format!("{} B", memory)
|
||||
};
|
||||
|
||||
string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<8}{}\n",
|
||||
string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<9}{:<8}{}\n",
|
||||
context.id.into(),
|
||||
context.pgid.into(),
|
||||
context.ppid.into(),
|
||||
@@ -110,7 +105,7 @@ pub fn resource() -> Result<Vec<u8>> {
|
||||
context.ens.into(),
|
||||
stat_string,
|
||||
cpu_string,
|
||||
ticks_string,
|
||||
cpu_time_string,
|
||||
memory_string,
|
||||
*context.name.read()));
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ impl Scheme for TimeScheme {
|
||||
CLOCK_MONOTONIC => time::monotonic(),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
time_buf[i].tv_sec = arch_time.0 as i64;
|
||||
time_buf[i].tv_nsec = arch_time.1 as i32;
|
||||
time_buf[i].tv_sec = (arch_time / time::NANOS_PER_SEC) as i64;
|
||||
time_buf[i].tv_nsec = (arch_time % time::NANOS_PER_SEC) as i32;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,8 +107,7 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R
|
||||
|
||||
if let Some(timeout) = timeout_opt {
|
||||
let start = time::monotonic();
|
||||
let sum = start.1 + timeout.tv_nsec as u64;
|
||||
let end = (start.0 + timeout.tv_sec as u64 + sum / 1_000_000_000, sum % 1_000_000_000);
|
||||
let end = start + (timeout.tv_sec as u128 * time::NANOS_PER_SEC) + (timeout.tv_nsec as u128);
|
||||
context.wake = Some(end);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result<usize> {
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
time.tv_sec = arch_time.0 as i64;
|
||||
time.tv_nsec = arch_time.1 as i32;
|
||||
time.tv_sec = (arch_time / time::NANOS_PER_SEC) as i64;
|
||||
time.tv_nsec = (arch_time % time::NANOS_PER_SEC) as i32;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result<usize> {
|
||||
pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result<usize> {
|
||||
//start is a tuple of (seconds, nanoseconds)
|
||||
let start = time::monotonic();
|
||||
let sum = start.1 + req.tv_nsec as u64;
|
||||
let mut end = (start.0 + req.tv_sec as u64 + sum / 1_000_000_000, sum % 1_000_000_000);
|
||||
let end = start + (req.tv_sec as u128 * time::NANOS_PER_SEC) + (req.tv_nsec as u128);
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
@@ -49,14 +48,10 @@ pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result<usize
|
||||
if let Some(rem) = rem_opt {
|
||||
let current = time::monotonic();
|
||||
|
||||
if current.0 < end.0 || (current.0 == end.0 && current.1 < end.1) {
|
||||
if end.1 < current.1 {
|
||||
end.0 -= 1;
|
||||
end.1 += 1_000_000_000;
|
||||
}
|
||||
|
||||
rem.tv_sec = (end.0 - current.0) as i64;
|
||||
rem.tv_nsec = (end.1 - current.1) as i32;
|
||||
if current < end {
|
||||
let diff = end - current;
|
||||
rem.tv_sec = (diff / time::NANOS_PER_SEC) as i64;
|
||||
rem.tv_nsec = (diff % time::NANOS_PER_SEC) as i32;
|
||||
} else {
|
||||
rem.tv_sec = 0;
|
||||
rem.tv_nsec = 0;
|
||||
|
||||
19
src/time.rs
19
src/time.rs
@@ -1,17 +1,16 @@
|
||||
use spin::Mutex;
|
||||
|
||||
/// Kernel start time, measured in (seconds, nanoseconds) since Unix epoch
|
||||
pub static START: Mutex<(u64, u64)> = Mutex::new((0, 0));
|
||||
/// Kernel up time, measured in (seconds, nanoseconds) since `START_TIME`
|
||||
pub static OFFSET: Mutex<(u64, u64)> = Mutex::new((0, 0));
|
||||
pub const NANOS_PER_SEC: u128 = 1_000_000_000;
|
||||
|
||||
pub fn monotonic() -> (u64, u64) {
|
||||
/// Kernel start time, measured in (seconds, nanoseconds) since Unix epoch
|
||||
pub static START: Mutex<u128> = Mutex::new(0);
|
||||
/// Kernel up time, measured in (seconds, nanoseconds) since `START_TIME`
|
||||
pub static OFFSET: Mutex<u128> = Mutex::new(0);
|
||||
|
||||
pub fn monotonic() -> u128 {
|
||||
*OFFSET.lock()
|
||||
}
|
||||
|
||||
pub fn realtime() -> (u64, u64) {
|
||||
let offset = monotonic();
|
||||
let start = *START.lock();
|
||||
let sum = start.1 + offset.1;
|
||||
(start.0 + offset.0 + sum / 1_000_000_000, sum % 1_000_000_000)
|
||||
pub fn realtime() -> u128 {
|
||||
*START.lock() + monotonic()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user