// ---------------------------------------------------------------------------- // 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 uint64_t epoch = 0; static 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); } // ---------------------------------------------------------------------------- // timer struct timer_internal_t { unsigned ms; unsigned (*callback)(unsigned interval, void *arg); void *arg; thread_ptr_t thd; }; static int timer_func(void *arg) { struct timer_internal_t *p = (struct timer_internal_t*)arg; sleep_ms( p->ms ); for( ;; ) { unsigned then = time_ms(); p->ms = p->callback(p->ms, p->arg); if( !p->ms ) break; unsigned now = time_ms(); unsigned lapse = now - then; int diff = p->ms - lapse; sleep_ms( diff <= 0 ? 0 : diff ); } thread_exit(0); return 0; } static __thread array(struct timer_internal_t *) timers; unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg) { struct timer_internal_t *p = MALLOC( sizeof(struct timer_internal_t) ); p->ms = ms; p->callback = callback; p->arg = arg; p->thd = thread_init( timer_func, p, "", 0 ); array_push(timers, p); return array_count(timers); } void timer_destroy(unsigned i) { if( i-- ) { thread_join(timers[i]->thd); thread_term(timers[i]->thd); FREE(timers[i]); timers[i] = 0; } } // ---------------------------------------------------------------------------- // guid //typedef vec3i guid; guid guid_create() { static __thread unsigned counter = 0; static uint64_t appid = 0; do_once appid = hash_str(app_name()); union conv { struct { unsigned timestamp : 32; unsigned threadid : 16; // inverted order in LE unsigned appid : 16; // unsigned counter : 32; }; vec3i v3; } c; c.timestamp = date_epoch() - 0x65000000; c.appid = (unsigned)appid; c.threadid = (unsigned)(uintptr_t)thread_current_thread_id(); c.counter = ++counter; return c.v3; }