@@ -27,8 +27,7 @@ typedef struct cronent_ud {
2727
2828static ETSTimer cron_timer ;
2929
30- static int * cronent_list = 0 ;
31- static size_t cronent_count = 0 ;
30+ static int cronent_table_ref ;
3231
3332static 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) {
8685static 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,30 @@ 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+ return 1 ; // just the userdata
113118}
114119
120+ // Finds index, leaves table at top of stack for convenience
115121static 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 );
122+ lua_rawgeti (L , LUA_REGISTRYINDEX , cronent_table_ref );
123+ size_t count = lua_objlen (L , -1 );
124+
125+ for (size_t i = 1 ; i <= count ; i ++ ) {
126+ lua_rawgeti (L , -1 , i );
127+ cronent_ud_t * eud = lua_touserdata (L , -1 );
121128 lua_pop (L , 1 );
122- if (eud == ud ) break ;
129+ if (eud == ud ) {
130+ return i ;
131+ }
123132 }
124- if ( i == cronent_count ) return -1 ;
125- return i ;
133+
134+ return count + 1 ;
126135}
127136
128137static int lcron_schedule (lua_State * L ) {
@@ -131,17 +140,12 @@ static int lcron_schedule(lua_State *L) {
131140 struct cronent_desc desc ;
132141 lcron_parsedesc (L , strdesc , & desc );
133142 ud -> desc = desc ;
143+
134144 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- }
145+
146+ lua_pushvalue (L , 1 ); // copy ud to top of stack
147+ lua_rawseti (L , -2 , i ); // install into table
148+
145149 return 0 ;
146150}
147151
@@ -157,27 +161,25 @@ static int lcron_handler(lua_State *L) {
157161static int lcron_unschedule (lua_State * L ) {
158162 cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
159163 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 -- ;
164+
165+ lua_pushnil ( L );
166+ lua_rawseti ( L , -2 , i );
167+
164168 return 0 ;
165169}
166170
171+ // scheduled entries are pinned, so we cannot arrive at the __gc metamethod
167172static int lcron_delete (lua_State * L ) {
168173 cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
169- lcron_unschedule (L );
170174 luaL_unref (L , LUA_REGISTRYINDEX , ud -> cb_ref );
171175 return 0 ;
172176}
173177
174178static 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 ;
179+ lua_newtable (L );
180+ luaL_unref (L , LUA_REGISTRYINDEX , cronent_table_ref );
181+ cronent_table_ref = luaL_ref (L , LUA_REGISTRYINDEX );
182+
181183 return 0 ;
182184}
183185
@@ -189,19 +191,27 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
189191 desc .dow = ( uint8_t )1 << dow ;
190192 desc .hour = (uint32_t )1 << hour ;
191193 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 ]);
194+
195+ lua_rawgeti (L , LUA_REGISTRYINDEX , cronent_table_ref );
196+ size_t count = lua_objlen (L , -1 );
197+
198+ for (size_t i = 1 ; i <= count ; i ++ ) {
199+ lua_rawgeti (L , -1 , i );
194200 cronent_ud_t * ent = lua_touserdata (L , -1 );
195201 lua_pop (L , 1 );
202+
196203 if ((ent -> desc .mon & desc .mon ) == 0 ) continue ;
197204 if ((ent -> desc .dom & desc .dom ) == 0 ) continue ;
198205 if ((ent -> desc .dow & desc .dow ) == 0 ) continue ;
199206 if ((ent -> desc .hour & desc .hour ) == 0 ) continue ;
200207 if ((ent -> desc .min & desc .min ) == 0 ) continue ;
208+
201209 lua_rawgeti (L , LUA_REGISTRYINDEX , ent -> cb_ref );
202- lua_rawgeti (L , LUA_REGISTRYINDEX , cronent_list [ i ]);
210+ lua_rawgeti (L , -2 , i ); // get ud again
203211 lua_call (L , 1 , 0 );
204212 }
213+
214+ lua_pop (L , 1 ); // pop table
205215}
206216
207217static void cron_handle_tmr () {
@@ -250,6 +260,10 @@ int luaopen_cron( lua_State *L ) {
250260 //My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer.
251261 os_timer_arm (& cron_timer , 1000 , 0 );
252262 luaL_rometatable (L , "cron.entry" , LROT_TABLEREF (cronent ));
263+
264+ cronent_table_ref = LUA_NOREF ;
265+ lcron_reset (L );
266+
253267 return 0 ;
254268}
255269
0 commit comments