diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 4ab21d8cf..4269450de 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -401,6 +401,7 @@ Raw_Cstring :: struct { Linux, Essence, FreeBSD, + OpenBSD, WASI, JS, Freestanding, diff --git a/core/time/time.odin b/core/time/time.odin index fddb09d85..1f778a8de 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -213,6 +213,44 @@ time_add :: proc(t: Time, d: Duration) -> Time { return Time{t._nsec + i64(d)} } +// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/ +// +// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case. +// On Windows it depends but is comparable with regular sleep in the worst case. +// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to +// tell Windows to use a more accurate timer for your process. +accurate_sleep :: proc(d: Duration) { + to_sleep, estimate, mean, m2, count: Duration + + to_sleep = d + estimate = 5 * Millisecond + mean = 5 * Millisecond + count = 1 + + for to_sleep > estimate { + start := tick_now() + sleep(1 * Millisecond) + + observed := tick_since(start) + to_sleep -= observed + + count += 1 + + delta := observed - mean + mean += delta / count + m2 += delta * (observed - mean) + stddev := intrinsics.sqrt(f64(m2) / f64(count - 1)) + estimate = mean + Duration(stddev) + } + + start := tick_now() + for to_sleep > tick_since(start) { + // prevent the spinlock from taking the thread hostage, still accurate enough + _yield() + // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while + // TODO: needs actual testing done to check if that's the case + } +} ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY); diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index 37fc1fd3e..0cfa196a2 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -17,6 +17,20 @@ foreign libc { @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 --- } +foreign import "system:pthread" + +import "core:c" + +@(private="file") +@(default_calling_convention="c") +foreign pthread { + sched_yield :: proc() -> c.int --- +} + +_yield :: proc "contextless" () { + sched_yield() +} + TimeSpec :: struct { tv_sec : i64, /* seconds */ tv_nsec : i64, /* nanoseconds */ diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin index 0fb9eaa0f..397741126 100644 --- a/core/time/time_windows.odin +++ b/core/time/time_windows.odin @@ -35,3 +35,7 @@ _tick_now :: proc "contextless" () -> Tick { _nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency)) return Tick{_nsec = _nsec} } + +_yield :: proc "contextless" () { + win32.SwitchToThread() +}