/* See license.txt in the root of this project. */ /*tex In |lmtlualib| we introduced more precise timer support but there we stored the data in \LUA's 64 bit integers. Here we use userdata. Again we delay division; see the other code for similar trickery. I will test this and when it's not more convenient (or used) we will not use it. */ # include # include # include /* In practice there is no real differenc unless we go into the millions. But it is anice example anyway so we keep it. 1 = fastest 2 = fast 0 = regular */ # define timer_mt_method 1 typedef struct timer { uint64_t start; uint64_t total; uint32_t timing; uint32_t padding; uint64_t offset; } timer; # if defined(_WIN32) || defined(_WIN64) # include inline static uint64_t get_ticks(void) { LARGE_INTEGER counter; QueryPerformanceCounter(&counter); return (uint64_t) counter.QuadPart; } inline static double ticks_to_seconds(uint64_t ticks) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return ((double) ticks) / (double) freq.QuadPart; } inline static double ticks_to_milli_seconds(uint64_t ticks) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return ((double) ticks * 1000.0) / (double) freq.QuadPart; } inline static double ticks_to_micro_seconds(uint64_t ticks) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return ((double) ticks * 1000000.0) / (double) freq.QuadPart; } # elif defined(__APPLE__) # include inline static uint64_t get_ticks(void) { return mach_absolute_time(); } inline static double ticks_to_seconds(uint64_t ticks) { static mach_timebase_info_data_t timebase; if (timebase.denom == 0) { mach_timebase_info(&timebase); } return ((double) ticks * timebase.numer / timebase.denom) / 1000000000.0; } inline static double ticks_to_milli_seconds(uint64_t ticks) { static mach_timebase_info_data_t timebase; if (timebase.denom == 0) { mach_timebase_info(&timebase); } return ((double) ticks * timebase.numer / timebase.denom) / 1000000.0; } inline static double ticks_to_micro_seconds(uint64_t ticks) { static mach_timebase_info_data_t timebase; if (timebase.denom == 0) { mach_timebase_info(&timebase); } return ((double) ticks * timebase.numer / timebase.denom) / 1000.0; } # else /* POSIX */ # include inline static uint64_t get_ticks(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ((uint64_t) ts.tv_sec * 1000000000ULL) + (uint64_t) ts.tv_nsec; } inline static double ticks_to_seconds(uint64_t ticks) { return (double) ticks / 1000000000.0; } inline static double ticks_to_milli_seconds(uint64_t ticks) { return (double) ticks / 1000000.0; } inline static double ticks_to_micro_seconds(uint64_t ticks) { return (double) ticks / 1000.0; } # endif # if timer_mt_method == 1 static const void *G_TIMER_METATABLE_PTR = NULL; static inline timer * timerlib_aux_valid(lua_State *L, int i) { timer * t = lua_touserdata(L, i); if (t && lua_getmetatable(L, i)) { /* compare the stack top's raw C pointer directly against the cached pointer */ if (lua_topointer(L, -1) != G_TIMER_METATABLE_PTR) { t = NULL; } lua_pop(L, 1); /* Only 1 pop required! */ } return t; } static int timerlib_new(lua_State *L) { timer *t = lua_newuserdatauv(L, sizeof(timer), 0); if (t) { luaL_getmetatable(L, TIMER_METATABLE_INSTANCE); lua_setmetatable(L, -2); memset(t, 0, sizeof(timer)); } else { tex_formatted_error("process lib", "out of memory"); lua_pushnil(L); } return 1; } # elif timer_mt_method == 2 static inline timer * timerlib_aux_valid(lua_State *L, int i) { timer * t = lua_touserdata(L, i); if (t && lua_getmetatable(L, i)) { /* upvalue 1 holds the metatable directly */ lua_pushvalue(L, lua_upvalueindex(1)); if (! lua_rawequal(L, -1, -2)) { t = NULL; } lua_pop(L, 2); } return t; } static int timerlib_new(lua_State *L) { timer *t = lua_newuserdatauv(L, sizeof(timer), 0); if (t) { lua_pushvalue(L, lua_upvalueindex(1)); lua_setmetatable(L, -2); memset(t, 0, sizeof(timer)); } else { tex_formatted_error("process lib", "out of memory"); lua_pushnil(L); /* never seen as we abort */ } return 1; } # else static inline timer * timerlib_aux_valid(lua_State *L, int i) { timer * t = lua_touserdata(L, i); if (t && lua_getmetatable(L, i)) { lua_get_metatablelua(timer_instance); if (! lua_rawequal(L, -1, -2)) { t = NULL; } lua_pop(L, 2); } return t; } static int timerlib_new(lua_State *L) { timer *t = lua_newuserdatauv(L, sizeof(timer), 0); if (t) { lua_get_metatablelua(timer_instance); lua_setmetatable(L, -2); memset(t, 0, sizeof(timer)); } else { tex_formatted_error("process lib", "out of memory"); lua_pushnil(L); /* never seen as we abort */ } return 1; } # endif static int timerlib_start(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t && ! t->timing) { t->start = get_ticks(); t->timing += 1; if (lua_toboolean(L, 2)) { t->total = 0; } } return 0; } static int timerlib_stop(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { if (t->timing == 1) { uint64_t stop = get_ticks(); t->total += (stop - t->start); t->timing = 0; t->start = 0; } else if (t->timing) { t->timing -= 1; } } return 0; } static int timerlib_reset(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { memset(t, 0, sizeof(timer)); } return 0; } static int timerlib_setoffset(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t && t->start) { t->offset = lmt_optunsigned(L, 2, 0) * (get_ticks() - t->start); } return 0; } static int timerlib_istiming(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { lua_pushboolean(L, t->timing); } else { lua_pushnil(L); } return 1; } static int timerlib_current(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { uint64_t total = t->total; if (t->timing) { total += (get_ticks() - t->start); } lua_pushnumber(L, ticks_to_seconds(total - t->offset)); } else { lua_pushnil(L); } return 1; } static int timerlib_elapsed(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { lua_pushnumber(L, ticks_to_seconds(t->total - t->offset)); } else { lua_pushnil(L); } return 1; } static int timerlib_elapsed_ms(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { lua_pushnumber(L, ticks_to_milli_seconds(t->total - t->offset)); } else { lua_pushnil(L); } return 1; } static int timerlib_elapsed_us(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { lua_pushnumber(L, ticks_to_micro_seconds(t->total - t->offset)); } else { lua_pushnil(L); } return 1; } static int timerlib_tostring(lua_State *L) { timer *t = timerlib_aux_valid(L, 1); if (t) { lua_pushfstring(L, "", t, t->timing); return 1; } else { return 0; } } /*tex The normal sleep function rounds and/or snap so on windows we only have some 15 ms accuracy. Each operating system has its interferences unless one goes for the more high resolution timers. */ # if defined(_WIN32) || defined(_WIN64) # include # ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION # define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002 /* older mingw */ # endif inline static void timerlib_aux_sleep(uint32_t ms) { HANDLE hTimer = CreateWaitableTimerExW( NULL, NULL, TIMER_ALL_ACCESS | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS ); if (hTimer) { /* a negative number is a relative time in 100-ns intervals */ LARGE_INTEGER liDueTime; liDueTime.QuadPart = -(int64_t) ms * 10000LL; if (SetWaitableTimerEx(hTimer, &liDueTime, 0, NULL, NULL, NULL, 0)) { WaitForSingleObject(hTimer, INFINITE); } else { Sleep(ms); } CloseHandle(hTimer); } else { /* older windows, not that we use them */ Sleep(ms); } } # elif defined(__linux__) # include # include inline static void timerlib_aux_sleep(uint32_t ms) { # ifdef PR_SET_TIMERSLACK prctl(PR_SET_TIMERSLACK, 1UL, 0, 0, 0); /* no auto-rounding */ # endif struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000UL; clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); /* avoid NTP interference */ } # elif defined(__APPLE__) # include inline static void timerlib_aux_sleep(uint32_t ms) { struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000UL; nanosleep(&ts, NULL); } # else /* POSIX */ # include inline static void timerlib_aux_sleep(uint32_t ms) { usleep(ms * 1000); } # endif static int timerlib_sleepms(lua_State *L) { timerlib_aux_sleep((uint32_t) lua_tointeger(L, 1)); return 0; } static const struct luaL_Reg timerlib_function_list[] = { /* management */ { "new", timerlib_new }, { "start", timerlib_start }, { "stop", timerlib_stop }, { "reset", timerlib_reset }, { "setoffset", timerlib_setoffset }, { "istiming", timerlib_istiming }, { "current", timerlib_current }, { "elapsed", timerlib_elapsed }, { "elapsedms", timerlib_elapsed_ms }, { "elapsedus", timerlib_elapsed_us }, { "elapsedμs", timerlib_elapsed_us }, /* easter egg */ { "tostring", timerlib_tostring }, /* */ { "sleepms", timerlib_sleepms }, /* */ { NULL, NULL }, }; # if timer_mt_method == 1 int luaopen_timer(lua_State *L) { luaL_newmetatable(L, TIMER_METATABLE_INSTANCE); G_TIMER_METATABLE_PTR = lua_topointer(L, -1); lua_newtable(L); luaL_setfuncs(L, timerlib_function_list, 0); lua_pushliteral(L, "__index"); lua_pushvalue(L, -2); lua_settable(L, -3); lua_pushliteral(L, "__tostring"); lua_pushliteral(L, "tostring"); lua_gettable(L, -3); lua_settable(L, -3); lua_pushliteral(L, "__name"); lua_pushliteral(L, "timer"); lua_settable(L, -3); return 1; } # elif timer_mt_method == 2 int luaopen_timer(lua_State *L) { luaL_newmetatable(L, TIMER_METATABLE_INSTANCE); lua_newtable(L); lua_pushvalue(L, -2); luaL_setfuncs(L, timerlib_function_list, 1); /* upvalue 1 */ lua_pushliteral(L, "__index"); lua_pushvalue(L, -2); lua_settable(L, -4); lua_pushliteral(L, "__tostring"); lua_pushliteral(L, "tostring"); lua_gettable(L, -3); lua_settable(L, -4); lua_pushliteral(L, "__name"); lua_pushliteral(L, "timer"); lua_settable(L, -4); lua_remove(L, -2); /* remove metatable */ /* no need to store it */ return 1; } # else int luaopen_timer(lua_State *L) { luaL_newmetatable(L, TIMER_METATABLE_INSTANCE); lua_newtable(L); luaL_setfuncs(L, timerlib_function_list, 0); lua_pushliteral(L, "__index"); lua_pushvalue(L, -2); lua_settable(L, -3); lua_pushliteral(L, "__tostring"); lua_pushliteral(L, "tostring"); lua_gettable(L, -3); lua_settable(L, -3); lua_pushliteral(L, "__name"); lua_pushliteral(L, "timer"); lua_settable(L, -3); return 1; } # endif