Skip to content

Commit 936b2d3

Browse files
committed
cron: use Lua table rather than native array
Pin a table to the registry and use that instead of manually allocating and reallocating and shuffling integers around.
1 parent 0555a4c commit 936b2d3

File tree

1 file changed

+65
-49
lines changed

1 file changed

+65
-49
lines changed

app/modules/cron.c

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ typedef struct cronent_ud {
2727

2828
static ETSTimer cron_timer;
2929

30-
static int *cronent_list = 0;
31-
static size_t cronent_count = 0;
30+
static int cronent_table_ref;
3231

3332
static uint64_t lcron_parsepart(lua_State *L, char *str, char **end, uint8_t min, uint8_t max) {
3433
uint64_t res = 0;
@@ -86,12 +85,21 @@ static int lcron_parsedesc(lua_State *L, char *str, struct cronent_desc *desc) {
8685
static int lcron_create(lua_State *L) {
8786
// Check arguments
8887
char *strdesc = (char*)luaL_checkstring(L, 1);
89-
void *newlist;
9088
luaL_checktype(L, 2, LUA_TFUNCTION);
91-
// Parse description
92-
struct cronent_desc desc;
93-
lcron_parsedesc(L, strdesc, &desc);
94-
// Allocate userdata
89+
90+
// Grab the table of all entries onto the stack
91+
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_table_ref);
92+
93+
// Find a place in the table for this entry by scanning down from the end
94+
int lsz = lua_objlen(L, -1);
95+
int ix = 1;
96+
for (int found = 0; !found & ix <= lsz; ix++) {
97+
lua_rawgeti(L, -1, ix);
98+
found = lua_isnil(L, -1);
99+
lua_pop(L, 1);
100+
}
101+
102+
// Allocate userdata onto the stack
95103
cronent_ud_t *ud = lua_newuserdata(L, sizeof(cronent_ud_t));
96104
// Set metatable
97105
luaL_getmetatable(L, "cron.entry");
@@ -100,29 +108,32 @@ static int lcron_create(lua_State *L) {
100108
lua_pushvalue(L, 2);
101109
ud->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
102110
// Set entry
103-
ud->desc = desc;
104-
// Store entry
105-
newlist = os_realloc(cronent_list, sizeof(int) * (cronent_count + 1));
106-
if (newlist == NULL) {
107-
return luaL_error(L, "out of memory");
108-
}
109-
lua_pushvalue(L, -1);
110-
cronent_list = newlist;
111-
cronent_list[cronent_count++] = luaL_ref(L, LUA_REGISTRYINDEX);
112-
return 1;
111+
lcron_parsedesc(L, strdesc, &ud->desc);
112+
113+
// Store entry to table while preserving userdata
114+
lua_pushvalue(L, -1); // clone userdata
115+
lua_rawseti(L, -3, ix); // -userdata; userdata, cronent table, cb, desc
116+
117+
cron_dbg("cron: create %p at index %d\n", ud, ix);
118+
119+
return 1; // just the userdata
113120
}
114121

122+
// Finds index, leaves table at top of stack for convenience
115123
static size_t lcron_findindex(lua_State *L, cronent_ud_t *ud) {
116-
cronent_ud_t *eud;
117-
size_t i;
118-
for (i = 0; i < cronent_count; i++) {
119-
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]);
120-
eud = lua_touserdata(L, -1);
124+
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_table_ref);
125+
size_t count = lua_objlen(L, -1);
126+
127+
for (size_t i = 1; i <= count; i++) {
128+
lua_rawgeti(L, -1, i);
129+
cronent_ud_t *eud = lua_touserdata(L, -1);
121130
lua_pop(L, 1);
122-
if (eud == ud) break;
131+
if (eud == ud) {
132+
return i;
133+
}
123134
}
124-
if (i == cronent_count) return -1;
125-
return i;
135+
136+
return count + 1;
126137
}
127138

128139
static int lcron_schedule(lua_State *L) {
@@ -131,17 +142,12 @@ static int lcron_schedule(lua_State *L) {
131142
struct cronent_desc desc;
132143
lcron_parsedesc(L, strdesc, &desc);
133144
ud->desc = desc;
145+
134146
size_t i = lcron_findindex(L, ud);
135-
if (i == -1) {
136-
void *newlist;
137-
newlist = os_realloc(cronent_list, sizeof(int) * (cronent_count + 1));
138-
if (newlist == NULL) {
139-
return luaL_error(L, "out of memory");
140-
}
141-
cronent_list = newlist;
142-
lua_pushvalue(L, 1);
143-
cronent_list[cronent_count++] = luaL_ref(L, LUA_REGISTRYINDEX);
144-
}
147+
148+
lua_pushvalue(L, 1); // copy ud to top of stack
149+
lua_rawseti(L, -2, i); // install into table
150+
145151
return 0;
146152
}
147153

@@ -157,27 +163,25 @@ static int lcron_handler(lua_State *L) {
157163
static int lcron_unschedule(lua_State *L) {
158164
cronent_ud_t *ud = luaL_checkudata(L, 1, "cron.entry");
159165
size_t i = lcron_findindex(L, ud);
160-
if (i == -1) return 0;
161-
luaL_unref(L, LUA_REGISTRYINDEX, cronent_list[i]);
162-
memmove(cronent_list + i, cronent_list + i + 1, sizeof(int) * (cronent_count - i - 1));
163-
cronent_count--;
166+
167+
lua_pushnil(L);
168+
lua_rawseti(L, -2, i);
169+
164170
return 0;
165171
}
166172

173+
// scheduled entries are pinned, so we cannot arrive at the __gc metamethod
167174
static int lcron_delete(lua_State *L) {
168175
cronent_ud_t *ud = luaL_checkudata(L, 1, "cron.entry");
169-
lcron_unschedule(L);
170176
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_ref);
171177
return 0;
172178
}
173179

174180
static int lcron_reset(lua_State *L) {
175-
for (size_t i = 0; i < cronent_count; i++) {
176-
luaL_unref(L, LUA_REGISTRYINDEX, cronent_list[i]);
177-
}
178-
cronent_count = 0;
179-
free(cronent_list);
180-
cronent_list = 0;
181+
lua_newtable(L);
182+
luaL_unref(L, LUA_REGISTRYINDEX, cronent_table_ref);
183+
cronent_table_ref = luaL_ref(L, LUA_REGISTRYINDEX);
184+
181185
return 0;
182186
}
183187

@@ -189,19 +193,27 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
189193
desc.dow = ( uint8_t)1 << dow;
190194
desc.hour = (uint32_t)1 << hour;
191195
desc.min = (uint64_t)1 << min;
192-
for (size_t i = 0; i < cronent_count; i++) {
193-
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]);
196+
197+
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_table_ref);
198+
size_t count = lua_objlen(L, -1);
199+
200+
for (size_t i = 1; i <= count; i++) {
201+
lua_rawgeti(L, -1, i);
194202
cronent_ud_t *ent = lua_touserdata(L, -1);
195203
lua_pop(L, 1);
204+
196205
if ((ent->desc.mon & desc.mon ) == 0) continue;
197206
if ((ent->desc.dom & desc.dom ) == 0) continue;
198207
if ((ent->desc.dow & desc.dow ) == 0) continue;
199208
if ((ent->desc.hour & desc.hour) == 0) continue;
200209
if ((ent->desc.min & desc.min ) == 0) continue;
210+
201211
lua_rawgeti(L, LUA_REGISTRYINDEX, ent->cb_ref);
202-
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]);
212+
lua_rawgeti(L, -2, i); // get ud again
203213
lua_call(L, 1, 0);
204214
}
215+
216+
lua_pop(L, 1); // pop table
205217
}
206218

207219
static void cron_handle_tmr() {
@@ -250,6 +262,10 @@ int luaopen_cron( lua_State *L ) {
250262
//My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer.
251263
os_timer_arm(&cron_timer, 1000, 0);
252264
luaL_rometatable(L, "cron.entry", LROT_TABLEREF(cronent));
265+
266+
cronent_table_ref = LUA_NOREF;
267+
lcron_reset(L);
268+
253269
return 0;
254270
}
255271

0 commit comments

Comments
 (0)