timeout-lua.c 7.0 KB

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