-
Notifications
You must be signed in to change notification settings - Fork 6
/
time.lua
187 lines (143 loc) · 4.72 KB
/
time.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
--[=[
Wall clock, monotonic clock, and sleep function (Windows, Linux and OSX).
Written by Cosmin Apreutesei. Public Domain.
now() -> ts current time with ~100us precision
clock() -> ts monotonic clock in seconds with ~1us precision
startclock clock() when this module was loaded
sleep(s) sleep with sub-second precision (~10-100ms)
now() -> ts
Reads the time as a UNIX timestamp (a Lua number).
It is the same as the time returned by `os.time()` on all platforms,
except it has sub-second precision. It is affected by drifting,
leap seconds and time adjustments by the user. It is not affected
by timezones. It can be used to synchronize time between different
boxes on a network regardless of platform.
clock() -> ts
Reads a monotonic performance counter, and is thus more accurate than
`now()`, it should never go back or drift, but it doesn't have
a fixed time base between program executions. It can be used
for measuring short time intervals for thread synchronization, etc.
sleep(s)
Suspends the current process `s` seconds. Different than wait() which
only suspends the current Lua thread.
]=]
local ffi = require'ffi'
local C = ffi.C
if ffi.os == 'Windows' then
ffi.cdef[[
void time_GetSystemTimeAsFileTime(uint64_t*) asm("GetSystemTimeAsFileTime");
int time_QueryPerformanceCounter(int64_t*) asm("QueryPerformanceCounter");
int time_QueryPerformanceFrequency(int64_t*) asm("QueryPerformanceFrequency");
void time_Sleep(uint32_t ms) asm("Sleep");
]]
local t = ffi.new'uint64_t[1]'
local DELTA_EPOCH_IN_100NS = 116444736000000000ULL
function now()
C.time_GetSystemTimeAsFileTime(t)
return tonumber(t[0] - DELTA_EPOCH_IN_100NS) * 1e-7
end
assert(C.time_QueryPerformanceFrequency(t) ~= 0)
local inv_qpf = 1 / tonumber(t[0]) --precision loss in e-10
local t0 = 0
function clock()
assert(C.time_QueryPerformanceCounter(t) ~= 0)
return tonumber(t[0]) * inv_qpf - t0
end
t0 = clock()
startclock = t0
function sleep(s)
C.time_Sleep(s * 1000)
end
elseif ffi.os == 'Linux' or ffi.os == 'OSX' then
ffi.cdef[[
typedef struct {
long s;
long ns;
} time_timespec;
int time_nanosleep(time_timespec*, time_timespec *) asm("nanosleep");
]]
local EINTR = 4
local t = ffi.new'time_timespec'
function sleep(s)
local int, frac = math.modf(s)
t.s = int
t.ns = frac * 1e9
local ret = C.time_nanosleep(t, t)
while ret == -1 and ffi.errno() == EINTR do --interrupted
ret = C.time_nanosleep(t, t)
end
assert(ret == 0)
end
if ffi.os == 'Linux' then
ffi.cdef[[
int time_clock_gettime(int clock_id, time_timespec *tp) asm("clock_gettime");
]]
local CLOCK_REALTIME = 0
local CLOCK_MONOTONIC = 1
local ok, rt_C = pcall(ffi.load, 'rt')
local clock_gettime = (ok and rt_C or C).time_clock_gettime
local function tos(t)
return tonumber(t.s) + tonumber(t.ns) / 1e9
end
function now()
assert(clock_gettime(CLOCK_REALTIME, t) == 0)
return tos(t)
end
local t0 = 0
function clock()
assert(clock_gettime(CLOCK_MONOTONIC, t) == 0)
return tos(t) - t0
end
t0 = clock()
startclock = t0
elseif ffi.os == 'OSX' then
ffi.cdef[[
typedef struct {
long s;
int32_t us;
} time_timeval;
typedef struct {
uint32_t numer;
uint32_t denom;
} time_mach_timebase_info_data_t;
int time_gettimeofday(time_timeval*, void*) asm("gettimeofday");
int time_mach_timebase_info(time_mach_timebase_info_data_t* info) asm("mach_timebase_info");
uint64_t time_mach_absolute_time(void) asm("mach_absolute_time");
]]
local t = ffi.new'time_timeval'
function now()
assert(C.time_gettimeofday(t, nil) == 0)
return tonumber(t.s) + tonumber(t.us) * 1e-6
end
--NOTE: this appears to be pointless on Intel Macs. The timebase fraction
--is always 1/1 and mach_absolute_time() does dynamic scaling internally.
local timebase = ffi.new'time_mach_timebase_info_data_t'
assert(C.time_mach_timebase_info(timebase) == 0)
local scale = tonumber(timebase.numer) / tonumber(timebase.denom) / 1e9
local t0
function clock()
return tonumber(C.time_mach_absolute_time()) * scale - t0
end
t0 = clock()
end --OSX
end --Linux or OSX
--demo -----------------------------------------------------------------------
if not ... then
io.stdout:setvbuf'no'
print('now' , now())
print('clock' , clock())
local function test_sleep(s, ss)
local t0 = clock()
local times = math.floor(s*1/ss)
s = times * ss
print(string.format('sleeping %gms in %gms increments (%d times)...', s * 1000, ss * 1000, times))
for i=1,times do
sleep(ss)
end
local t1 = clock()
print(string.format(' missed by: %0.2fms', (t1 - t0 - s) / times * 1000))
end
test_sleep(0.001, 0.001)
test_sleep(0.2, 0.02)
test_sleep(2, 0.2)
end