time_unix.odin 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. //+build linux, darwin, freebsd
  2. package time
  3. IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
  4. when ODIN_OS == "darwin" {
  5. foreign import libc "System.framework"
  6. } else {
  7. foreign import libc "system:c"
  8. }
  9. @(default_calling_convention="c")
  10. foreign libc {
  11. @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) -> i32 ---
  12. @(link_name="sleep") _unix_sleep :: proc(seconds: u32) -> i32 ---
  13. @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 ---
  14. }
  15. TimeSpec :: struct {
  16. tv_sec : i64, /* seconds */
  17. tv_nsec : i64, /* nanoseconds */
  18. }
  19. CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
  20. CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
  21. CLOCK_PROCESS_CPUTIME_ID :: 2
  22. CLOCK_THREAD_CPUTIME_ID :: 3
  23. CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
  24. CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
  25. CLOCK_MONOTONIC_COARSE :: 6
  26. CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
  27. CLOCK_REALTIME_ALARM :: 8
  28. CLOCK_BOOTTIME_ALARM :: 9
  29. // TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
  30. // I do not know if Darwin programmers are used to the existance of these constants or not, so
  31. // I'm leaving aliases to them for now.
  32. CLOCK_SYSTEM :: CLOCK_REALTIME
  33. CLOCK_CALENDAR :: CLOCK_MONOTONIC
  34. clock_gettime :: proc "contextless" (clock_id: u64) -> TimeSpec {
  35. ts : TimeSpec // NOTE(tetra): Do we need to initialize this?
  36. _unix_clock_gettime(clock_id, &ts)
  37. return ts
  38. }
  39. now :: proc() -> Time {
  40. time_spec_now := clock_gettime(CLOCK_REALTIME)
  41. ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec
  42. return Time{_nsec=ns}
  43. }
  44. boot_time :: proc() -> Time {
  45. ts_now := clock_gettime(CLOCK_REALTIME)
  46. ts_boottime := clock_gettime(CLOCK_BOOTTIME)
  47. ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
  48. return Time{_nsec=ns}
  49. }
  50. seconds_since_boot :: proc() -> f64 {
  51. ts_boottime := clock_gettime(CLOCK_BOOTTIME)
  52. return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
  53. }
  54. sleep :: proc(d: Duration) {
  55. ds := duration_seconds(d)
  56. seconds := u32(ds)
  57. nanoseconds := i64((ds - f64(seconds)) * 1e9)
  58. if seconds > 0 { _unix_sleep(seconds) }
  59. if nanoseconds > 0 { nanosleep(nanoseconds) }
  60. }
  61. nanosleep :: proc(nanoseconds: i64) -> int {
  62. // NOTE(tetra): Should we remove this assert? We are measuring nanoseconds after all...
  63. assert(nanoseconds <= 999999999)
  64. requested := TimeSpec{tv_nsec = nanoseconds}
  65. remaining: TimeSpec // NOTE(tetra): Do we need to initialize this?
  66. return int(_unix_nanosleep(&requested, &remaining))
  67. }
  68. _tick_now :: proc "contextless" () -> Tick {
  69. t := clock_gettime(CLOCK_MONOTONIC_RAW)
  70. _nsec := t.tv_sec*1e9 + t.tv_nsec
  71. return Tick{_nsec = _nsec}
  72. }