1 #include <assert.h>
2 #include <string.h>
3
4 #include <lua.h>
5 #include <lualib.h>
6 #include <lauxlib.h>
7
8 #if LUA_VERSION_NUM != 503
9 #error only Lua 5.3 supported
10 #endif
11
12 #define TIMEOUT_PUBLIC static
13 #include "timeout.h"
14 #include "timeout.c"
15
16 #define TIMEOUT_METANAME "struct timeout"
17 #define TIMEOUTS_METANAME "struct timeouts*"
18
19 static struct timeout *
to_checkudata(lua_State * L,int index)20 to_checkudata(lua_State *L, int index)
21 {
22 return luaL_checkudata(L, index, TIMEOUT_METANAME);
23 }
24
25 static struct timeouts *
tos_checkudata(lua_State * L,int index)26 tos_checkudata(lua_State *L, int index)
27 {
28 return *(struct timeouts **)luaL_checkudata(L, index, TIMEOUTS_METANAME);
29 }
30
31 static void
tos_bind(lua_State * L,int tos_index,int to_index)32 tos_bind(lua_State *L, int tos_index, int to_index)
33 {
34 lua_getuservalue(L, tos_index);
35 lua_pushlightuserdata(L, to_checkudata(L, to_index));
36 lua_pushvalue(L, to_index);
37 lua_rawset(L, -3);
38 lua_pop(L, 1);
39 }
40
41 static void
tos_unbind(lua_State * L,int tos_index,int to_index)42 tos_unbind(lua_State *L, int tos_index, int to_index)
43 {
44 lua_getuservalue(L, tos_index);
45 lua_pushlightuserdata(L, to_checkudata(L, to_index));
46 lua_pushnil(L);
47 lua_rawset(L, -3);
48 lua_pop(L, 1);
49 }
50
51 static int
to__index(lua_State * L)52 to__index(lua_State *L)
53 {
54 struct timeout *to = to_checkudata(L, 1);
55
56 if (lua_type(L, 2 == LUA_TSTRING)) {
57 const char *key = lua_tostring(L, 2);
58
59 if (!strcmp(key, "flags")) {
60 lua_pushinteger(L, to->flags);
61
62 return 1;
63 } else if (!strcmp(key, "expires")) {
64 lua_pushinteger(L, to->expires);
65
66 return 1;
67 }
68 }
69
70 if (LUA_TNIL != lua_getuservalue(L, 1)) {
71 lua_pushvalue(L, 2);
72 if (LUA_TNIL != lua_rawget(L, -2))
73 return 1;
74 }
75
76 lua_pushvalue(L, 2);
77 if (LUA_TNIL != lua_rawget(L, lua_upvalueindex(1)))
78 return 1;
79
80 return 0;
81 }
82
83 static int
to__newindex(lua_State * L)84 to__newindex(lua_State *L)
85 {
86 if (LUA_TNIL == lua_getuservalue(L, 1)) {
87 lua_newtable(L);
88 lua_pushvalue(L, -1);
89 lua_setuservalue(L, 1);
90 }
91
92 lua_pushvalue(L, 2);
93 lua_pushvalue(L, 3);
94 lua_rawset(L, -3);
95
96 return 0;
97 }
98
99 static int
to__gc(lua_State * L)100 to__gc(lua_State *L)
101 {
102 struct timeout *to = to_checkudata(L, 1);
103
104 /*
105 * NB: On script exit it's possible for a timeout to still be
106 * associated with a timeouts object, particularly when the timeouts
107 * object was created first.
108 */
109 timeout_del(to);
110
111 return 0;
112 }
113
114 static int
to_new(lua_State * L)115 to_new(lua_State *L)
116 {
117 int flags = luaL_optinteger(L, 1, 0);
118 struct timeout *to;
119
120 to = lua_newuserdata(L, sizeof *to);
121 timeout_init(to, flags);
122 luaL_setmetatable(L, TIMEOUT_METANAME);
123
124 return 1;
125 }
126
127 static const luaL_Reg to_methods[] = {
128 { NULL, NULL },
129 };
130
131 static const luaL_Reg to_metatable[] = {
132 { "__index", &to__index },
133 { "__newindex", &to__newindex },
134 { "__gc", &to__gc },
135 { NULL, NULL },
136 };
137
138 static const luaL_Reg to_globals[] = {
139 { "new", &to_new },
140 { NULL, NULL },
141 };
142
143 static void
to_newmetatable(lua_State * L)144 to_newmetatable(lua_State *L)
145 {
146 if (luaL_newmetatable(L, TIMEOUT_METANAME)) {
147 /*
148 * fill metamethod table, capturing the methods table as an
149 * upvalue for use by __index metamethod
150 */
151 luaL_newlib(L, to_methods);
152 luaL_setfuncs(L, to_metatable, 1);
153 }
154 }
155
156 int
luaopen_timeout(lua_State * L)157 luaopen_timeout(lua_State *L)
158 {
159 to_newmetatable(L);
160
161 luaL_newlib(L, to_globals);
162 lua_pushinteger(L, TIMEOUT_INT);
163 lua_setfield(L, -2, "INT");
164 lua_pushinteger(L, TIMEOUT_ABS);
165 lua_setfield(L, -2, "ABS");
166
167 return 1;
168 }
169
170 static int
tos_update(lua_State * L)171 tos_update(lua_State *L)
172 {
173 struct timeouts *T = tos_checkudata(L, 1);
174 lua_Number n = luaL_checknumber(L, 2);
175
176 timeouts_update(T, timeouts_f2i(T, n));
177
178 lua_pushvalue(L, 1);
179
180 return 1;
181 }
182
183 static int
tos_step(lua_State * L)184 tos_step(lua_State *L)
185 {
186 struct timeouts *T = tos_checkudata(L, 1);
187 lua_Number n = luaL_checknumber(L, 2);
188
189 timeouts_step(T, timeouts_f2i(T, n));
190
191 lua_pushvalue(L, 1);
192
193 return 1;
194 }
195
196 static int
tos_timeout(lua_State * L)197 tos_timeout(lua_State *L)
198 {
199 struct timeouts *T = tos_checkudata(L, 1);
200
201 lua_pushnumber(L, timeouts_i2f(T, timeouts_timeout(T)));
202
203 return 1;
204 }
205
206 static int
tos_add(lua_State * L)207 tos_add(lua_State *L)
208 {
209 struct timeouts *T = tos_checkudata(L, 1);
210 struct timeout *to = to_checkudata(L, 2);
211 lua_Number timeout = luaL_checknumber(L, 3);
212
213 tos_bind(L, 1, 2);
214 timeouts_addf(T, to, timeout);
215
216 return lua_pushvalue(L, 1), 1;
217 }
218
219 static int
tos_del(lua_State * L)220 tos_del(lua_State *L)
221 {
222 struct timeouts *T = tos_checkudata(L, 1);
223 struct timeout *to = to_checkudata(L, 2);
224
225 timeouts_del(T, to);
226 tos_unbind(L, 1, 2);
227
228 return lua_pushvalue(L, 1), 1;
229 }
230
231 static int
tos_get(lua_State * L)232 tos_get(lua_State *L)
233 {
234 struct timeouts *T = tos_checkudata(L, 1);
235 struct timeout *to;
236
237 if (!(to = timeouts_get(T)))
238 return 0;
239
240 lua_getuservalue(L, 1);
241 lua_rawgetp(L, -1, to);
242
243 if (!timeout_pending(to))
244 tos_unbind(L, 1, lua_absindex(L, -1));
245
246 return 1;
247 }
248
249 static int
tos_pending(lua_State * L)250 tos_pending(lua_State *L)
251 {
252 struct timeouts *T = tos_checkudata(L, 1);
253
254 lua_pushboolean(L, timeouts_pending(T));
255
256 return 1;
257 }
258
259 static int
tos_expired(lua_State * L)260 tos_expired(lua_State *L)
261 {
262 struct timeouts *T = tos_checkudata(L, 1);
263
264 lua_pushboolean(L, timeouts_expired(T));
265
266 return 1;
267 }
268
269 static int
tos_check(lua_State * L)270 tos_check(lua_State *L)
271 {
272 struct timeouts *T = tos_checkudata(L, 1);
273
274 lua_pushboolean(L, timeouts_check(T, NULL));
275
276 return 1;
277 }
278
279 static int
tos__next(lua_State * L)280 tos__next(lua_State *L)
281 {
282 struct timeouts *T = tos_checkudata(L, lua_upvalueindex(1));
283 struct timeouts_it *it = lua_touserdata(L, lua_upvalueindex(2));
284 struct timeout *to;
285
286 if (!(to = timeouts_next(T, it)))
287 return 0;
288
289 lua_getuservalue(L, lua_upvalueindex(1));
290 lua_rawgetp(L, -1, to);
291
292 return 1;
293 }
294
295 static int
tos_timeouts(lua_State * L)296 tos_timeouts(lua_State *L)
297 {
298 int flags = luaL_checkinteger(L, 2);
299 struct timeouts_it *it;
300
301 tos_checkudata(L, 1);
302 lua_pushvalue(L, 1);
303 it = lua_newuserdata(L, sizeof *it);
304 TIMEOUTS_IT_INIT(it, flags);
305 lua_pushcclosure(L, &tos__next, 2);
306
307 return 1;
308 }
309
310 static int
tos__gc(lua_State * L)311 tos__gc(lua_State *L)
312 {
313 struct timeouts **tos = luaL_checkudata(L, 1, TIMEOUTS_METANAME);
314 struct timeout *to;
315
316 TIMEOUTS_FOREACH(to, *tos, TIMEOUTS_ALL) {
317 timeouts_del(*tos, to);
318 }
319
320 timeouts_close(*tos);
321 *tos = NULL;
322
323 return 0;
324 }
325
326 static int
tos_new(lua_State * L)327 tos_new(lua_State *L)
328 {
329 timeout_t hz = luaL_optinteger(L, 1, 0);
330 struct timeouts **T;
331 int error;
332
333 T = lua_newuserdata(L, sizeof *T);
334 luaL_setmetatable(L, TIMEOUTS_METANAME);
335
336 lua_newtable(L);
337 lua_setuservalue(L, -2);
338
339 if (!(*T = timeouts_open(hz, &error)))
340 return luaL_error(L, "%s", strerror(error));
341
342 return 1;
343 }
344
345 static const luaL_Reg tos_methods[] = {
346 { "update", &tos_update },
347 { "step", &tos_step },
348 { "timeout", &tos_timeout },
349 { "add", &tos_add },
350 { "del", &tos_del },
351 { "get", &tos_get },
352 { "pending", &tos_pending },
353 { "expired", &tos_expired },
354 { "check", &tos_check },
355 { "timeouts", &tos_timeouts },
356 { NULL, NULL },
357 };
358
359 static const luaL_Reg tos_metatable[] = {
360 { "__gc", &tos__gc },
361 { NULL, NULL },
362 };
363
364 static const luaL_Reg tos_globals[] = {
365 { "new", &tos_new },
366 { NULL, NULL },
367 };
368
369 static void
tos_newmetatable(lua_State * L)370 tos_newmetatable(lua_State *L)
371 {
372 if (luaL_newmetatable(L, TIMEOUTS_METANAME)) {
373 luaL_setfuncs(L, tos_metatable, 0);
374 luaL_newlib(L, tos_methods);
375 lua_setfield(L, -2, "__index");
376 }
377 }
378
379 int
luaopen_timeouts(lua_State * L)380 luaopen_timeouts(lua_State *L)
381 {
382 to_newmetatable(L);
383 tos_newmetatable(L);
384
385 luaL_newlib(L, tos_globals);
386 lua_pushinteger(L, TIMEOUTS_PENDING);
387 lua_setfield(L, -2, "PENDING");
388 lua_pushinteger(L, TIMEOUTS_EXPIRED);
389 lua_setfield(L, -2, "EXPIRED");
390 lua_pushinteger(L, TIMEOUTS_ALL);
391 lua_setfield(L, -2, "ALL");
392 lua_pushinteger(L, TIMEOUTS_CLEAR);
393 lua_setfield(L, -2, "CLEAR");
394
395 return 1;
396 }
397