Tor  0.4.7.0-alpha-dev
timeout-lua.c
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 *
20 to_checkudata(lua_State *L, int index)
21 {
22  return luaL_checkudata(L, index, TIMEOUT_METANAME);
23 }
24 
25 static struct timeouts *
26 tos_checkudata(lua_State *L, int index)
27 {
28  return *(struct timeouts **)luaL_checkudata(L, index, TIMEOUTS_METANAME);
29 }
30 
31 static void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
#define T(s, t, a, o)
Definition: parsecommon.h:245