Merge branch 'fix-changelogs' into 'main'
[tor.git] / src / ext / timeouts / lua / timeout-lua.c
blob4d4e54cba6b24580bc4ef7fb1d09942759e2c81c
1 #include <assert.h>
2 #include <string.h>
4 #include <lua.h>
5 #include <lualib.h>
6 #include <lauxlib.h>
8 #if LUA_VERSION_NUM != 503
9 #error only Lua 5.3 supported
10 #endif
12 #define TIMEOUT_PUBLIC static
13 #include "timeout.h"
14 #include "timeout.c"
16 #define TIMEOUT_METANAME "struct timeout"
17 #define TIMEOUTS_METANAME "struct timeouts*"
19 static struct timeout *
20 to_checkudata(lua_State *L, int index)
22 return luaL_checkudata(L, index, TIMEOUT_METANAME);
25 static struct timeouts *
26 tos_checkudata(lua_State *L, int index)
28 return *(struct timeouts **)luaL_checkudata(L, index, TIMEOUTS_METANAME);
31 static void
32 tos_bind(lua_State *L, int tos_index, int to_index)
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);
41 static void
42 tos_unbind(lua_State *L, int tos_index, int to_index)
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);
51 static int
52 to__index(lua_State *L)
54 struct timeout *to = to_checkudata(L, 1);
56 if (lua_type(L, 2 == LUA_TSTRING)) {
57 const char *key = lua_tostring(L, 2);
59 if (!strcmp(key, "flags")) {
60 lua_pushinteger(L, to->flags);
62 return 1;
63 } else if (!strcmp(key, "expires")) {
64 lua_pushinteger(L, to->expires);
66 return 1;
70 if (LUA_TNIL != lua_getuservalue(L, 1)) {
71 lua_pushvalue(L, 2);
72 if (LUA_TNIL != lua_rawget(L, -2))
73 return 1;
76 lua_pushvalue(L, 2);
77 if (LUA_TNIL != lua_rawget(L, lua_upvalueindex(1)))
78 return 1;
80 return 0;
83 static int
84 to__newindex(lua_State *L)
86 if (LUA_TNIL == lua_getuservalue(L, 1)) {
87 lua_newtable(L);
88 lua_pushvalue(L, -1);
89 lua_setuservalue(L, 1);
92 lua_pushvalue(L, 2);
93 lua_pushvalue(L, 3);
94 lua_rawset(L, -3);
96 return 0;
99 static int
100 to__gc(lua_State *L)
102 struct timeout *to = to_checkudata(L, 1);
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.
109 timeout_del(to);
111 return 0;
114 static int
115 to_new(lua_State *L)
117 int flags = luaL_optinteger(L, 1, 0);
118 struct timeout *to;
120 to = lua_newuserdata(L, sizeof *to);
121 timeout_init(to, flags);
122 luaL_setmetatable(L, TIMEOUT_METANAME);
124 return 1;
127 static const luaL_Reg to_methods[] = {
128 { NULL, NULL },
131 static const luaL_Reg to_metatable[] = {
132 { "__index", &to__index },
133 { "__newindex", &to__newindex },
134 { "__gc", &to__gc },
135 { NULL, NULL },
138 static const luaL_Reg to_globals[] = {
139 { "new", &to_new },
140 { NULL, NULL },
143 static void
144 to_newmetatable(lua_State *L)
146 if (luaL_newmetatable(L, TIMEOUT_METANAME)) {
148 * fill metamethod table, capturing the methods table as an
149 * upvalue for use by __index metamethod
151 luaL_newlib(L, to_methods);
152 luaL_setfuncs(L, to_metatable, 1);
157 luaopen_timeout(lua_State *L)
159 to_newmetatable(L);
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");
167 return 1;
170 static int
171 tos_update(lua_State *L)
173 struct timeouts *T = tos_checkudata(L, 1);
174 lua_Number n = luaL_checknumber(L, 2);
176 timeouts_update(T, timeouts_f2i(T, n));
178 lua_pushvalue(L, 1);
180 return 1;
183 static int
184 tos_step(lua_State *L)
186 struct timeouts *T = tos_checkudata(L, 1);
187 lua_Number n = luaL_checknumber(L, 2);
189 timeouts_step(T, timeouts_f2i(T, n));
191 lua_pushvalue(L, 1);
193 return 1;
196 static int
197 tos_timeout(lua_State *L)
199 struct timeouts *T = tos_checkudata(L, 1);
201 lua_pushnumber(L, timeouts_i2f(T, timeouts_timeout(T)));
203 return 1;
206 static int
207 tos_add(lua_State *L)
209 struct timeouts *T = tos_checkudata(L, 1);
210 struct timeout *to = to_checkudata(L, 2);
211 lua_Number timeout = luaL_checknumber(L, 3);
213 tos_bind(L, 1, 2);
214 timeouts_addf(T, to, timeout);
216 return lua_pushvalue(L, 1), 1;
219 static int
220 tos_del(lua_State *L)
222 struct timeouts *T = tos_checkudata(L, 1);
223 struct timeout *to = to_checkudata(L, 2);
225 timeouts_del(T, to);
226 tos_unbind(L, 1, 2);
228 return lua_pushvalue(L, 1), 1;
231 static int
232 tos_get(lua_State *L)
234 struct timeouts *T = tos_checkudata(L, 1);
235 struct timeout *to;
237 if (!(to = timeouts_get(T)))
238 return 0;
240 lua_getuservalue(L, 1);
241 lua_rawgetp(L, -1, to);
243 if (!timeout_pending(to))
244 tos_unbind(L, 1, lua_absindex(L, -1));
246 return 1;
249 static int
250 tos_pending(lua_State *L)
252 struct timeouts *T = tos_checkudata(L, 1);
254 lua_pushboolean(L, timeouts_pending(T));
256 return 1;
259 static int
260 tos_expired(lua_State *L)
262 struct timeouts *T = tos_checkudata(L, 1);
264 lua_pushboolean(L, timeouts_expired(T));
266 return 1;
269 static int
270 tos_check(lua_State *L)
272 struct timeouts *T = tos_checkudata(L, 1);
274 lua_pushboolean(L, timeouts_check(T, NULL));
276 return 1;
279 static int
280 tos__next(lua_State *L)
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;
286 if (!(to = timeouts_next(T, it)))
287 return 0;
289 lua_getuservalue(L, lua_upvalueindex(1));
290 lua_rawgetp(L, -1, to);
292 return 1;
295 static int
296 tos_timeouts(lua_State *L)
298 int flags = luaL_checkinteger(L, 2);
299 struct timeouts_it *it;
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);
307 return 1;
310 static int
311 tos__gc(lua_State *L)
313 struct timeouts **tos = luaL_checkudata(L, 1, TIMEOUTS_METANAME);
314 struct timeout *to;
316 TIMEOUTS_FOREACH(to, *tos, TIMEOUTS_ALL) {
317 timeouts_del(*tos, to);
320 timeouts_close(*tos);
321 *tos = NULL;
323 return 0;
326 static int
327 tos_new(lua_State *L)
329 timeout_t hz = luaL_optinteger(L, 1, 0);
330 struct timeouts **T;
331 int error;
333 T = lua_newuserdata(L, sizeof *T);
334 luaL_setmetatable(L, TIMEOUTS_METANAME);
336 lua_newtable(L);
337 lua_setuservalue(L, -2);
339 if (!(*T = timeouts_open(hz, &error)))
340 return luaL_error(L, "%s", strerror(error));
342 return 1;
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 },
359 static const luaL_Reg tos_metatable[] = {
360 { "__gc", &tos__gc },
361 { NULL, NULL },
364 static const luaL_Reg tos_globals[] = {
365 { "new", &tos_new },
366 { NULL, NULL },
369 static void
370 tos_newmetatable(lua_State *L)
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");
380 luaopen_timeouts(lua_State *L)
382 to_newmetatable(L);
383 tos_newmetatable(L);
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");
395 return 1;