diff --git a/sfw/time.cpp b/sfw/time.cpp new file mode 100644 index 0000000..eb621a9 --- /dev/null +++ b/sfw/time.cpp @@ -0,0 +1,170 @@ +// ---------------------------------------------------------------------------- +// time + +#if 0 +uint64_t time_gpu() { + GLint64 t = 123456789; + glGetInteger64v(GL_TIMESTAMP, &t); + return (uint64_t)t; +} +#endif +uint64_t date() { + time_t epoch = time(0); + struct tm *ti = localtime(&epoch); + return atoi64(va("%04d%02d%02d%02d%02d%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec)); +} +char *date_string() { + time_t epoch = time(0); + struct tm *ti = localtime(&epoch); + return va("%04d-%02d-%02d %02d:%02d:%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec); +} +uint64_t date_epoch() { + time_t epoch = time(0); + return epoch; +} +#if 0 +double time_ss() { + return glfwGetTime(); +} +double time_ms() { + return glfwGetTime() * 1000.0; +} +uint64_t time_us() { + return (uint64_t)(glfwGetTime() * 1000000.0); // @fixme: use a high resolution timer instead, or time_gpu below +} +uint64_t sleep_us(uint64_t us) { // @fixme: use a high resolution sleeper instead + return sleep_ms( us / 1000.0 ); +} +double sleep_ms(double ms) { + double now = time_ms(); + if( ms <= 0 ) { +#if is(win32) + Sleep(0); // yield +#else + usleep(0); +#endif + } else { +#if is(win32) + Sleep(ms); +#else + usleep(ms * 1000); +#endif + } + return time_ms() - now; +} +double sleep_ss(double ss) { + return sleep_ms( ss * 1000 ) / 1000.0; +} +#endif + +// high-perf functions + +#define TIMER_E3 1000ULL +#define TIMER_E6 1000000ULL +#define TIMER_E9 1000000000ULL + +#ifdef CLOCK_MONOTONIC_RAW +#define TIME_MONOTONIC CLOCK_MONOTONIC_RAW +#elif defined CLOCK_MONOTONIC +#define TIME_MONOTONIC CLOCK_MONOTONIC +#else +// #define TIME_MONOTONIC CLOCK_REALTIME // untested +#endif + +static uint64_t nanotimer(uint64_t *out_freq) { + if( out_freq ) { +#if is(win32) + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + *out_freq = li.QuadPart; +//#elif is(ANDROID) +// *out_freq = CLOCKS_PER_SEC; +#elif defined TIME_MONOTONIC + *out_freq = TIMER_E9; +#else + *out_freq = TIMER_E6; +#endif + } +#if is(win32) + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return (uint64_t)li.QuadPart; +//#elif is(ANDROID) +// return (uint64_t)clock(); +#elif defined TIME_MONOTONIC + struct timespec ts; + clock_gettime(TIME_MONOTONIC, &ts); + return (TIMER_E9 * (uint64_t)ts.tv_sec) + ts.tv_nsec; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return (TIMER_E6 * (uint64_t)tv.tv_sec) + tv.tv_usec; +#endif +} + +uint64_t time_ns() { + static __thread uint64_t epoch = 0; + static __thread uint64_t freq = 0; + if( !freq ) { + epoch = nanotimer(&freq); + } + + uint64_t a = nanotimer(NULL) - epoch; + uint64_t b = TIMER_E9; + uint64_t c = freq; + + // Computes (a*b)/c without overflow, as long as both (a*b) and the overall result fit into 64-bits. + // [ref] https://github.com/rust-lang/rust/blob/3809bbf47c8557bd149b3e52ceb47434ca8378d5/src/libstd/sys_common/mod.rs#L124 + uint64_t q = a / c; + uint64_t r = a % c; + return q * b + r * b / c; +} +uint64_t time_us() { + return time_ns() / TIMER_E3; +} +uint64_t time_ms() { + return time_ns() / TIMER_E6; +} +double time_ss() { + return time_ns() / 1e9; // TIMER_E9; +} +double time_mm() { + return time_ss() / 60; +} +double time_hh() { + return time_mm() / 60; +} + +void sleep_ns( double ns ) { +#if is(win32) + if( ns >= 100 ) { + LARGE_INTEGER li; // Windows sleep in 100ns units + HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL); + li.QuadPart = (LONGLONG)(__int64)(-ns/100); // Negative for relative time + SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); +#else + if( ns > 0 ) { + struct timespec wait = {0}; + wait.tv_sec = ns / 1e9; + wait.tv_nsec = ns - wait.tv_sec * 1e9; + nanosleep(&wait, NULL); +#endif + } else { +#if is(win32) + Sleep(0); // yield, Sleep(0), SwitchToThread +#else + usleep(0); +#endif + } +} +void sleep_us( double us ) { + sleep_ns(us * 1e3); +} +void sleep_ms( double ms ) { + sleep_ns(ms * 1e6); +} +void sleep_ss( double ss ) { + sleep_ns(ss * 1e9); +} diff --git a/sfw/time.h b/sfw/time.h new file mode 100644 index 0000000..b341250 --- /dev/null +++ b/sfw/time.h @@ -0,0 +1,21 @@ +// ----------------------------------------------------------------------------- +// time framework utils +// - rlyeh, public domain. + +API uint64_t date(); // YYYYMMDDhhmmss +API uint64_t date_epoch(); // linux epoch +API char* date_string(); // "YYYY-MM-DD hh:mm:ss" +API double time_hh(); +API double time_mm(); +API double time_ss(); +API uint64_t time_ms(); +API uint64_t time_us(); +API uint64_t time_ns(); +API void sleep_ss(double ss); +API void sleep_ms(double ms); +API void sleep_us(double us); +API void sleep_ns(double us); + +API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg); +API void timer_destroy(unsigned timer_handle); +