mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Merge pull request #1719 from ftphikari/precise_sleep
time: add accurate sleep procedure
This commit is contained in:
@@ -401,6 +401,7 @@ Raw_Cstring :: struct {
|
|||||||
Linux,
|
Linux,
|
||||||
Essence,
|
Essence,
|
||||||
FreeBSD,
|
FreeBSD,
|
||||||
|
OpenBSD,
|
||||||
WASI,
|
WASI,
|
||||||
JS,
|
JS,
|
||||||
Freestanding,
|
Freestanding,
|
||||||
|
|||||||
@@ -213,6 +213,44 @@ time_add :: proc(t: Time, d: Duration) -> Time {
|
|||||||
return Time{t._nsec + i64(d)}
|
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_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);
|
ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY);
|
||||||
|
|||||||
@@ -17,6 +17,20 @@ foreign libc {
|
|||||||
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 ---
|
@(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 {
|
TimeSpec :: struct {
|
||||||
tv_sec : i64, /* seconds */
|
tv_sec : i64, /* seconds */
|
||||||
tv_nsec : i64, /* nanoseconds */
|
tv_nsec : i64, /* nanoseconds */
|
||||||
|
|||||||
@@ -35,3 +35,7 @@ _tick_now :: proc "contextless" () -> Tick {
|
|||||||
_nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency))
|
_nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency))
|
||||||
return Tick{_nsec = _nsec}
|
return Tick{_nsec = _nsec}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_yield :: proc "contextless" () {
|
||||||
|
win32.SwitchToThread()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user