@@ -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,21 @@ 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 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
95
103
cronent_ud_t * ud = lua_newuserdata (L , sizeof (cronent_ud_t ));
96
104
// Set metatable
97
105
luaL_getmetatable (L , "cron.entry" );
@@ -100,29 +108,32 @@ static int lcron_create(lua_State *L) {
100
108
lua_pushvalue (L , 2 );
101
109
ud -> cb_ref = luaL_ref (L , LUA_REGISTRYINDEX );
102
110
// 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
113
120
}
114
121
122
+ // Finds index, leaves table at top of stack for convenience
115
123
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 );
121
130
lua_pop (L , 1 );
122
- if (eud == ud ) break ;
131
+ if (eud == ud ) {
132
+ return i ;
133
+ }
123
134
}
124
- if ( i == cronent_count ) return -1 ;
125
- return i ;
135
+
136
+ return count + 1 ;
126
137
}
127
138
128
139
static int lcron_schedule (lua_State * L ) {
@@ -131,17 +142,12 @@ static int lcron_schedule(lua_State *L) {
131
142
struct cronent_desc desc ;
132
143
lcron_parsedesc (L , strdesc , & desc );
133
144
ud -> desc = desc ;
145
+
134
146
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
+
145
151
return 0 ;
146
152
}
147
153
@@ -157,27 +163,25 @@ static int lcron_handler(lua_State *L) {
157
163
static int lcron_unschedule (lua_State * L ) {
158
164
cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
159
165
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
+
164
170
return 0 ;
165
171
}
166
172
173
+ // scheduled entries are pinned, so we cannot arrive at the __gc metamethod
167
174
static int lcron_delete (lua_State * L ) {
168
175
cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
169
- lcron_unschedule (L );
170
176
luaL_unref (L , LUA_REGISTRYINDEX , ud -> cb_ref );
171
177
return 0 ;
172
178
}
173
179
174
180
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
+
181
185
return 0 ;
182
186
}
183
187
@@ -189,19 +193,27 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
189
193
desc .dow = ( uint8_t )1 << dow ;
190
194
desc .hour = (uint32_t )1 << hour ;
191
195
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 );
194
202
cronent_ud_t * ent = lua_touserdata (L , -1 );
195
203
lua_pop (L , 1 );
204
+
196
205
if ((ent -> desc .mon & desc .mon ) == 0 ) continue ;
197
206
if ((ent -> desc .dom & desc .dom ) == 0 ) continue ;
198
207
if ((ent -> desc .dow & desc .dow ) == 0 ) continue ;
199
208
if ((ent -> desc .hour & desc .hour ) == 0 ) continue ;
200
209
if ((ent -> desc .min & desc .min ) == 0 ) continue ;
210
+
201
211
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
203
213
lua_call (L , 1 , 0 );
204
214
}
215
+
216
+ lua_pop (L , 1 ); // pop table
205
217
}
206
218
207
219
static void cron_handle_tmr () {
@@ -250,6 +262,10 @@ int luaopen_cron( lua_State *L ) {
250
262
//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
263
os_timer_arm (& cron_timer , 1000 , 0 );
252
264
luaL_rometatable (L , "cron.entry" , LROT_TABLEREF (cronent ));
265
+
266
+ cronent_table_ref = LUA_NOREF ;
267
+ lcron_reset (L );
268
+
253
269
return 0 ;
254
270
}
255
271
0 commit comments