Browse Source

Clean up `core:time` to be consistent across all platforms

gingerBill 3 years ago
parent
commit
f002857edc

+ 2 - 0
core/os/os2/errors.odin

@@ -14,6 +14,7 @@ General_Error :: enum u32 {
 	Timeout,
 
 	Invalid_File,
+	Invalid_Dir,
 	Invalid_Path,
 
 	Unsupported,
@@ -51,6 +52,7 @@ error_string :: proc(ferr: Error) -> string {
 		case .Closed:            return "file already closed"
 		case .Timeout:           return "i/o timeout"
 		case .Invalid_File:      return "invalid file"
+		case .Invalid_Dir:       return "invalid directory"
 		case .Invalid_Path:      return "invalid path"
 		case .Unsupported:       return "unsupported"
 		}

+ 1 - 1
core/runtime/procs.odin

@@ -4,7 +4,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
 	foreign import lib "system:NtDll.lib"
 	
 	@(private="file")
-	@(default_calling_convention="std")
+	@(default_calling_convention="stdcall")
 	foreign lib {
 		RtlMoveMemory :: proc(dst, src: rawptr, length: int) ---
 		RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---

+ 2 - 3
core/sys/unix/pthread_unix.odin

@@ -4,7 +4,6 @@ package unix
 foreign import "system:pthread"
 
 import "core:c"
-import "core:time"
 
 //
 // On success, these functions return 0.
@@ -72,7 +71,7 @@ foreign pthread {
 
 	// assumes the mutex is pre-locked
 	pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int ---
-	pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---
+	pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int ---
 
 	pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int ---
 	pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int ---
@@ -95,7 +94,7 @@ foreign pthread {
 
 	pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
 
-	pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---
+	pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int ---
 
 	pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
 

+ 83 - 0
core/sys/unix/time_unix.odin

@@ -0,0 +1,83 @@
+//+build linux, darwin, freebsd, openbsd
+package unix
+
+when ODIN_OS == .Darwin {
+	foreign import libc "System.framework"
+} else  {
+	foreign import libc "system:c"
+}
+
+@(default_calling_convention="c")
+foreign libc {
+	clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 ---
+	sleep         :: proc(seconds: u32) -> i32 ---
+	nanosleep     :: proc(requested, remaining: ^timespec) -> i32 ---
+}
+
+foreign import "system:pthread"
+
+import "core:c"
+
+@(private="file")
+@(default_calling_convention="c")
+foreign pthread {
+	sched_yield :: proc() -> c.int ---
+}
+
+timespec :: struct {
+	tv_sec:  i64, // seconds
+	tv_nsec: i64, // nanoseconds
+}
+
+when ODIN_OS == .OpenBSD {
+	CLOCK_REALTIME           :: 0
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_MONOTONIC          :: 3
+	CLOCK_THREAD_CPUTIME_ID  :: 4
+	CLOCK_UPTIME             :: 5
+	CLOCK_BOOTTIME           :: 6
+
+	// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
+	CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
+} else {
+	CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
+	CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_THREAD_CPUTIME_ID  :: 3
+	CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+	CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
+	CLOCK_MONOTONIC_COARSE   :: 6
+	CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
+	CLOCK_REALTIME_ALARM     :: 8
+	CLOCK_BOOTTIME_ALARM     :: 9
+}
+
+// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
+// I do not know if Darwin programmers are used to the existance of these constants or not, so
+// I'm leaving aliases to them for now.
+CLOCK_SYSTEM   :: CLOCK_REALTIME
+CLOCK_CALENDAR :: CLOCK_MONOTONIC
+
+boot_time_in_nanoseconds :: proc "c" () -> i64 {
+	ts_now, ts_boottime: timespec
+	clock_gettime(CLOCK_REALTIME, &ts_now)
+	clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
+
+	ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
+	return i64(ns)
+}
+
+seconds_since_boot :: proc "c" () -> f64 {
+	ts_boottime: timespec
+	clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
+	return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
+}
+
+
+inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) {
+	s, ns := nanoseconds / 1e9, nanoseconds % 1e9
+	requested := timespec{tv_sec=s, tv_nsec=ns}
+	res = nanosleep(&requested, &remaining)
+	return
+}
+

+ 29 - 13
core/time/time.odin

@@ -14,6 +14,8 @@ Hour        :: 60 * Minute
 MIN_DURATION :: Duration(-1 << 63)
 MAX_DURATION :: Duration(1<<63 - 1)
 
+IS_SUPPORTED :: _IS_SUPPORTED
+
 Time :: struct {
 	_nsec: i64, // zero is 1970-01-01 00:00:00
 }
@@ -49,6 +51,14 @@ Stopwatch :: struct {
 	_accumulation: Duration,
 }
 
+now :: proc "contextless" () -> Time {
+	return _now()
+}
+
+sleep :: proc "contextless" (d: Duration) {
+	_sleep(d)
+}
+
 stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
 	if !running {
 		_start_time = tick_now()
@@ -82,36 +92,36 @@ since :: proc(start: Time) -> Duration {
 	return diff(start, now())
 }
 
-duration_nanoseconds :: proc(d: Duration) -> i64 {
+duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 {
 	return i64(d)
 }
-duration_microseconds :: proc(d: Duration) -> f64 {
+duration_microseconds :: proc "contextless" (d: Duration) -> f64 {
 	return duration_seconds(d) * 1e6
 }
-duration_milliseconds :: proc(d: Duration) -> f64 {
+duration_milliseconds :: proc "contextless" (d: Duration) -> f64 {
 	return duration_seconds(d) * 1e3
 }
-duration_seconds :: proc(d: Duration) -> f64 {
+duration_seconds :: proc "contextless" (d: Duration) -> f64 {
 	sec := d / Second
 	nsec := d % Second
 	return f64(sec) + f64(nsec)/1e9
 }
-duration_minutes :: proc(d: Duration) -> f64 {
+duration_minutes :: proc "contextless" (d: Duration) -> f64 {
 	min := d / Minute
 	nsec := d % Minute
 	return f64(min) + f64(nsec)/(60*1e9)
 }
-duration_hours :: proc(d: Duration) -> f64 {
+duration_hours :: proc "contextless" (d: Duration) -> f64 {
 	hour := d / Hour
 	nsec := d % Hour
 	return f64(hour) + f64(nsec)/(60*60*1e9)
 }
 
-_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
-	return u64(x)+u64(x) < u64(y)
-}
-
 duration_round :: proc(d, m: Duration) -> Duration {
+	_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
+		return u64(x)+u64(x) < u64(y)
+	}
+
 	if m <= 0 {
 		return d
 	}
@@ -201,10 +211,12 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
 	return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
 }
 
+to_unix_seconds :: time_to_unix
 time_to_unix :: proc(t: Time) -> i64 {
 	return t._nsec/1e9
 }
 
+to_unix_nanoseconds :: time_to_unix_nano
 time_to_unix_nano :: proc(t: Time) -> i64 {
 	return t._nsec
 }
@@ -265,20 +277,24 @@ INTERNAL_TO_WALL :: -WALL_TO_INTERNAL
 UNIX_TO_ABSOLUTE :: UNIX_TO_INTERNAL + INTERNAL_TO_ABSOLUTE
 ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
 
-_is_leap_year :: proc(year: int) -> bool {
-	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
-}
 
+@(private)
 _date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
 	year, month, day, yday = _abs_date(_time_abs(t), full)
 	return
 }
 
+@(private)
 _time_abs :: proc(t: Time) -> u64 {
 	return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
 }
 
+@(private)
 _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
+	_is_leap_year :: proc(year: int) -> bool {
+		return year%4 == 0 && (year%100 != 0 || year%400 == 0)
+	}
+
 	d := abs / SECONDS_PER_DAY
 
 	// 400 year cycles

+ 7 - 6
core/time/time_essence.odin

@@ -1,18 +1,19 @@
+//+private
 package time
 
 import "core:sys/es"
 
-IS_SUPPORTED :: true;
+_IS_SUPPORTED :: true
 
-now :: proc "contextless" () -> Time {
+_now :: proc "contextless" () -> Time {
 	// TODO Replace once there's a proper time API.
-	return Time{_nsec = i64(es.TimeStampMs() * 1e6)};
+	return Time{_nsec = i64(es.TimeStampMs() * 1e6)}
 }
 
-sleep :: proc "contextless" (d: Duration) {
-	es.Sleep(u64(d/Millisecond));
+_sleep :: proc "contextless" (d: Duration) {
+	es.Sleep(u64(d/Millisecond))
 }
 
 _tick_now :: proc "contextless" () -> Tick {
-	return Tick{_nsec = i64(es.TimeStampMs() * 1e6)};
+	return Tick{_nsec = i64(es.TimeStampMs() * 1e6)}
 }

+ 6 - 3
core/time/time_freestanding.odin

@@ -1,16 +1,19 @@
+//+private
 //+build freestanding
 package time
 
-IS_SUPPORTED :: false
+_IS_SUPPORTED :: false
 
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
 	return {}
 }
 
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
 }
 
 _tick_now :: proc "contextless" () -> Tick {
 	return {}
 }
 
+_yield :: proc "contextless" () {
+}

+ 4 - 3
core/time/time_js.odin

@@ -1,13 +1,14 @@
+//+private
 //+build js
 package time
 
-IS_SUPPORTED :: false
+_IS_SUPPORTED :: false
 
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
 	return {}
 }
 
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
 }
 
 _tick_now :: proc "contextless" () -> Tick {

+ 16 - 100
core/time/time_unix.odin

@@ -1,118 +1,34 @@
+//+private
 //+build linux, darwin, freebsd, openbsd
 package time
 
-IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
+import "core:sys/unix"
 
-when ODIN_OS == .Darwin {
-	foreign import libc "System.framework"
-} else  {
-	foreign import libc "system:c"
-}
-
-
-@(default_calling_convention="c")
-foreign libc {
-	@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) -> i32 ---
-	@(link_name="sleep")         _unix_sleep         :: proc(seconds: u32) -> 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 {
-	tv_sec  : i64,  /* seconds */
-	tv_nsec : i64,  /* nanoseconds */
-}
-
-when ODIN_OS == .OpenBSD {
-	CLOCK_REALTIME           :: 0
-	CLOCK_PROCESS_CPUTIME_ID :: 2
-	CLOCK_MONOTONIC          :: 3
-	CLOCK_THREAD_CPUTIME_ID  :: 4
-	CLOCK_UPTIME             :: 5
-	CLOCK_BOOTTIME           :: 6
-
-	// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
-	CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
-} else {
-	CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
-	CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
-	CLOCK_PROCESS_CPUTIME_ID :: 2
-	CLOCK_THREAD_CPUTIME_ID  :: 3
-	CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
-	CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
-	CLOCK_MONOTONIC_COARSE   :: 6
-	CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
-	CLOCK_REALTIME_ALARM     :: 8
-	CLOCK_BOOTTIME_ALARM     :: 9
-}
-
-// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
-// I do not know if Darwin programmers are used to the existance of these constants or not, so
-// I'm leaving aliases to them for now.
-CLOCK_SYSTEM   :: CLOCK_REALTIME
-CLOCK_CALENDAR :: CLOCK_MONOTONIC
+_IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
 
-
-clock_gettime :: proc "contextless" (clock_id: u64) -> TimeSpec {
-	ts : TimeSpec // NOTE(tetra): Do we need to initialize this?
-	_unix_clock_gettime(clock_id, &ts)
-	return ts
-}
-
-now :: proc() -> Time {
-	time_spec_now := clock_gettime(CLOCK_REALTIME)
+_now :: proc "contextless" () -> Time {
+	time_spec_now: unix.timespec
+	unix.clock_gettime(unix.CLOCK_REALTIME, &time_spec_now)
 	ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec
 	return Time{_nsec=ns}
 }
 
-boot_time :: proc() -> Time {
-	ts_now := clock_gettime(CLOCK_REALTIME)
-	ts_boottime := clock_gettime(CLOCK_BOOTTIME)
-
-	ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
-	return Time{_nsec=ns}
-}
-
-seconds_since_boot :: proc() -> f64 {
-	ts_boottime := clock_gettime(CLOCK_BOOTTIME)
-	return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
-}
-
-
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
 	ds := duration_seconds(d)
 	seconds := u32(ds)
 	nanoseconds := i64((ds - f64(seconds)) * 1e9)
 
-	if seconds > 0     { _unix_sleep(seconds)   }
-	if nanoseconds > 0 { nanosleep(nanoseconds) }
+	if seconds > 0     { unix.sleep(seconds)   }
+	if nanoseconds > 0 { unix.inline_nanosleep(nanoseconds) }
 }
 
-nanosleep :: proc(nanoseconds: i64) -> int {
-	// NOTE(tetra): Should we remove this assert? We are measuring nanoseconds after all...
-	assert(nanoseconds <= 999999999)
-
-	requested := TimeSpec{tv_nsec = nanoseconds}
-	remaining: TimeSpec // NOTE(tetra): Do we need to initialize this?
-	return int(_unix_nanosleep(&requested, &remaining))
+_tick_now :: proc "contextless" () -> Tick {
+	t: unix.timespec
+	unix.clock_gettime(unix.CLOCK_MONOTONIC_RAW, &t)
+	return Tick{_nsec = t.tv_sec*1e9 + t.tv_nsec}
 }
 
-
-_tick_now :: proc "contextless" () -> Tick {
-	t := clock_gettime(CLOCK_MONOTONIC_RAW)
-	_nsec := t.tv_sec*1e9 + t.tv_nsec
-	return Tick{_nsec = _nsec}
+_yield :: proc "contextless" () {
+	unix.sched_yield()
 }
+

+ 4 - 3
core/time/time_wasi.odin

@@ -1,15 +1,16 @@
+//+private
 //+build wasi
 package time
 
 import wasi "core:sys/wasm/wasi"
 
-IS_SUPPORTED :: false
+_IS_SUPPORTED :: false
 
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
 	return {}
 }
 
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
 }
 
 _tick_now :: proc "contextless" () -> Tick {

+ 4 - 5
core/time/time_windows.odin

@@ -1,22 +1,21 @@
+//+private
 package time
 
 import win32 "core:sys/windows"
 
-IS_SUPPORTED :: true
+_IS_SUPPORTED :: true
 
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
 	file_time: win32.FILETIME
 	win32.GetSystemTimeAsFileTime(&file_time)
 	ns := win32.FILETIME_as_unix_nanoseconds(file_time)
 	return Time{_nsec=ns}
 }
 
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
 	win32.Sleep(win32.DWORD(d/Millisecond))
 }
 
-
-
 _tick_now :: proc "contextless" () -> Tick {
 	mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
 		q := val / den