@@ -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,20 @@ 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 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
95102 cronent_ud_t * ud = lua_newuserdata (L , sizeof (cronent_ud_t ));
96103 // Set metatable
97104 luaL_getmetatable (L , "cron.entry" );
@@ -100,29 +107,30 @@ static int lcron_create(lua_State *L) {
100107 lua_pushvalue (L , 2 );
101108 ud -> cb_ref = luaL_ref (L , LUA_REGISTRYINDEX );
102109 // 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
113117}
114118
119+ // Finds index, leaves table at top of stack for convenience
115120static 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 );
121127 lua_pop (L , 1 );
122- if (eud == ud ) break ;
128+ if (eud == ud ) {
129+ return i ;
130+ }
123131 }
124- if ( i == cronent_count ) return -1 ;
125- return i ;
132+
133+ return count + 1 ;
126134}
127135
128136static int lcron_schedule (lua_State * L ) {
@@ -131,17 +139,12 @@ static int lcron_schedule(lua_State *L) {
131139 struct cronent_desc desc ;
132140 lcron_parsedesc (L , strdesc , & desc );
133141 ud -> desc = desc ;
142+
134143 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+
145148 return 0 ;
146149}
147150
@@ -157,27 +160,25 @@ static int lcron_handler(lua_State *L) {
157160static int lcron_unschedule (lua_State * L ) {
158161 cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
159162 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+
164167 return 0 ;
165168}
166169
170+ // scheduled entries are pinned, so we cannot arrive at the __gc metamethod
167171static int lcron_delete (lua_State * L ) {
168172 cronent_ud_t * ud = luaL_checkudata (L , 1 , "cron.entry" );
169- lcron_unschedule (L );
170173 luaL_unref (L , LUA_REGISTRYINDEX , ud -> cb_ref );
171174 return 0 ;
172175}
173176
174177static 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+
181182 return 0 ;
182183}
183184
@@ -189,19 +190,27 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
189190 desc .dow = ( uint8_t )1 << dow ;
190191 desc .hour = (uint32_t )1 << hour ;
191192 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 );
194199 cronent_ud_t * ent = lua_touserdata (L , -1 );
195200 lua_pop (L , 1 );
201+
196202 if ((ent -> desc .mon & desc .mon ) == 0 ) continue ;
197203 if ((ent -> desc .dom & desc .dom ) == 0 ) continue ;
198204 if ((ent -> desc .dow & desc .dow ) == 0 ) continue ;
199205 if ((ent -> desc .hour & desc .hour ) == 0 ) continue ;
200206 if ((ent -> desc .min & desc .min ) == 0 ) continue ;
207+
201208 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
203210 luaL_pcallx (L , 1 , 0 );
204211 }
212+
213+ lua_pop (L , 1 ); // pop table
205214}
206215
207216static void cron_handle_tmr () {
@@ -250,6 +259,10 @@ int luaopen_cron( lua_State *L ) {
250259 //My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer.
251260 os_timer_arm (& cron_timer , 1000 , 0 );
252261 luaL_rometatable (L , "cron.entry" , LROT_TABLEREF (cronent ));
262+
263+ cronent_table_ref = LUA_NOREF ;
264+ lcron_reset (L );
265+
253266 return 0 ;
254267}
255268
0 commit comments