@@ -27,8 +27,7 @@ typedef struct cronent_ud {
27
27
28
28
static ETSTimer cron_timer ;
29
29
30
- static int * cronent_list = 0 ;
31
- static size_t cronent_count = 0 ;
30
+ static int cronent_table_ref ;
32
31
33
32
static uint64_t lcron_parsepart (lua_State * L , char * str , char * * end , uint8_t min , uint8_t max ) {
34
33
uint64_t res = 0 ;
@@ -86,12 +85,20 @@ static int lcron_parsedesc(lua_State *L, char *str, struct cronent_desc *desc) {
86
85
static int lcron_create (lua_State * L ) {
87
86
// Check arguments
88
87
char * strdesc = (char * )luaL_checkstring (L , 1 );
89
- void * newlist ;
90
88
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 free index
94
+ int ix = 1 ;
95
+ while (lua_rawgeti (L , -1 , ix ) != LUA_TNIL ) {
96
+ lua_pop (L , 1 );
97
+ ix ++ ;
98
+ }
99
+ lua_pop (L , 1 ); // pop the nil off the stack
100
+
101
+ // Allocate userdata onto the stack
95
102
cronent_ud_t * ud = lua_newuserdata (L , sizeof (cronent_ud_t ));
96
103
// Set metatable
97
104
luaL_getmetatable (L , "cron.entry" );
@@ -100,29 +107,30 @@ static int lcron_create(lua_State *L) {
100
107
lua_pushvalue (L , 2 );
101
108
ud -> cb_ref = luaL_ref (L , LUA_REGISTRYINDEX );
102
109
// 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 ;
110
+ lcron_parsedesc (L , strdesc , & ud -> desc );
111
+
112
+ // Store entry to table while preserving userdata
113
+ lua_pushvalue (L , -1 ); // clone userdata
114
+ lua_rawseti (L , -3 , ix ); // -userdata; userdata, cronent table, cb, desc
115
+
116
+ return 1 ; // just the userdata
113
117
}
114
118
119
+ // Finds index, leaves table at top of stack for convenience
115
120
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 );
121
+ lua_rawgeti (L , LUA_REGISTRYINDEX , cronent_table_ref );
122
+ size_t count = lua_objlen (L , -1 );
123
+
124
+ for (size_t i = 1 ; i <= count ; i ++ ) {
125
+ lua_rawgeti (L , -1 , i );
126
+ cronent_ud_t * eud = lua_touserdata (L , -1 );
121
127
lua_pop (L , 1 );
122
- if (eud == ud ) break ;
128
+ if (eud == ud ) {
129
+ return i ;
130
+ }
123
131
}
124
- if ( i == cronent_count ) return -1 ;
125
- return i ;
132
+
133
+ return count + 1 ;
126
134
}
127
135
128
136
static int lcron_schedule (lua_State * L ) {
@@ -131,17 +139,12 @@ static int lcron_schedule(lua_State *L) {
131
139
struct cronent_desc desc ;
132
140
lcron_parsedesc (L , strdesc , & desc );
133
141
ud -> desc = desc ;
142
+
134
143
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
- }
144
+
145
+ lua_pushvalue (L , 1 ); // copy ud to top of stack
146
+ lua_rawseti (L , -2 , i ); // install into table
147
+
145
148
return 0 ;
146
149
}
147
150
@@ -157,27 +160,25 @@ static int lcron_handler(lua_State *L) {
157
160
static int lcron_unschedule (lua_State * L ) {
158
161
cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
159
162
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 -- ;
163
+
164
+ lua_pushnil ( L );
165
+ lua_rawseti ( L , -2 , i );
166
+
164
167
return 0 ;
165
168
}
166
169
170
+ // scheduled entries are pinned, so we cannot arrive at the __gc metamethod
167
171
static int lcron_delete (lua_State * L ) {
168
172
cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
169
- lcron_unschedule (L );
170
173
luaL_unref (L , LUA_REGISTRYINDEX , ud -> cb_ref );
171
174
return 0 ;
172
175
}
173
176
174
177
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 ;
178
+ lua_newtable (L );
179
+ luaL_unref (L , LUA_REGISTRYINDEX , cronent_table_ref );
180
+ cronent_table_ref = luaL_ref (L , LUA_REGISTRYINDEX );
181
+
181
182
return 0 ;
182
183
}
183
184
@@ -189,19 +190,27 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
189
190
desc .dow = ( uint8_t )1 << dow ;
190
191
desc .hour = (uint32_t )1 << hour ;
191
192
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 ]);
193
+
194
+ lua_rawgeti (L , LUA_REGISTRYINDEX , cronent_table_ref );
195
+ size_t count = lua_objlen (L , -1 );
196
+
197
+ for (size_t i = 1 ; i <= count ; i ++ ) {
198
+ lua_rawgeti (L , -1 , i );
194
199
cronent_ud_t * ent = lua_touserdata (L , -1 );
195
200
lua_pop (L , 1 );
201
+
196
202
if ((ent -> desc .mon & desc .mon ) == 0 ) continue ;
197
203
if ((ent -> desc .dom & desc .dom ) == 0 ) continue ;
198
204
if ((ent -> desc .dow & desc .dow ) == 0 ) continue ;
199
205
if ((ent -> desc .hour & desc .hour ) == 0 ) continue ;
200
206
if ((ent -> desc .min & desc .min ) == 0 ) continue ;
207
+
201
208
lua_rawgeti (L , LUA_REGISTRYINDEX , ent -> cb_ref );
202
- lua_rawgeti (L , LUA_REGISTRYINDEX , cronent_list [ i ]);
209
+ lua_rawgeti (L , -2 , i ); // get ud again
203
210
luaL_pcallx (L , 1 , 0 );
204
211
}
212
+
213
+ lua_pop (L , 1 ); // pop table
205
214
}
206
215
207
216
static void cron_handle_tmr () {
@@ -250,6 +259,10 @@ int luaopen_cron( lua_State *L ) {
250
259
//My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer.
251
260
os_timer_arm (& cron_timer , 1000 , 0 );
252
261
luaL_rometatable (L , "cron.entry" , LROT_TABLEREF (cronent ));
262
+
263
+ cronent_table_ref = LUA_NOREF ;
264
+ lcron_reset (L );
265
+
253
266
return 0 ;
254
267
}
255
268
0 commit comments