timeout-lua.c (7163B)
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 }