MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix timer mutations

+74 -30
+1 -1
meson.build
··· 79 79 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 80 80 81 81 version_conf = configuration_data() 82 - version_conf.set('ANT_VERSION', '0.2.2.26') 82 + version_conf.set('ANT_VERSION', '0.2.2.27') 83 83 version_conf.set('ANT_GIT_HASH', git_hash) 84 84 version_conf.set('ANT_BUILD_DATE', build_date) 85 85
+37 -29
src/modules/timer.c
··· 235 235 return 0; 236 236 } 237 237 238 + static void remove_timer(timer_entry_t *target) { 239 + timer_entry_t **ptr = &timer_state.timers; 240 + 241 + scan: 242 + if (!*ptr) return; 243 + if (*ptr == target) { *ptr = target->next; free(target); return; } 244 + ptr = &(*ptr)->next; 245 + goto scan; 246 + } 247 + 238 248 void process_timers(struct js *js) { 239 - if (timer_state.timers == NULL) return; 240 - 241 249 uint64_t current_time = get_current_time_ms(); 242 - timer_entry_t **entry_ptr = &timer_state.timers; 250 + timer_entry_t **ptr = &timer_state.timers; 251 + timer_entry_t *entry; 252 + 253 + scan: 254 + if (!*ptr) return; 255 + entry = *ptr; 243 256 244 - while (*entry_ptr != NULL) { 245 - timer_entry_t *entry = *entry_ptr; 246 - 247 - if (!entry->active) { 248 - *entry_ptr = entry->next; 249 - free(entry); 250 - continue; 251 - } 252 - 253 - if (entry->active && current_time >= entry->target_time_ms) { 254 - jsval_t args[0]; 255 - js_call(js, entry->callback, args, 0); 256 - 257 - process_microtasks(js); 258 - 259 - if (entry->is_interval) { 260 - entry->target_time_ms = get_current_time_ms() + entry->interval_ms; 261 - entry_ptr = &entry->next; 262 - } else { 263 - *entry_ptr = entry->next; 264 - free(entry); 265 - } 266 - continue; 267 - } 268 - 269 - entry_ptr = &entry->next; 257 + if (!entry->active) { 258 + *ptr = entry->next; 259 + free(entry); 260 + goto scan; 261 + } 262 + 263 + if (current_time < entry->target_time_ms) { 264 + ptr = &entry->next; 265 + goto scan; 270 266 } 267 + 268 + jsval_t args[0]; 269 + js_call(js, entry->callback, args, 0); 270 + process_microtasks(js); 271 + 272 + if (entry->is_interval && entry->active) { 273 + entry->target_time_ms = get_current_time_ms() + entry->interval_ms; 274 + } else remove_timer(entry); 275 + 276 + current_time = get_current_time_ms(); 277 + ptr = &timer_state.timers; 278 + goto scan; 271 279 } 272 280 273 281 int has_pending_timers(void) {
+36
tests/test_timer_mutation.cjs
··· 1 + // Test timer list mutation during callback execution 2 + // This tests the case where a setTimeout callback adds new timers 3 + 4 + let results = []; 5 + let expected = ['timer1', 'timer2', 'timer3', 'done']; 6 + 7 + // Timer 1 fires and adds timer 2 at head of list 8 + setTimeout(() => { 9 + results.push('timer1'); 10 + 11 + // Add a new timer with 0ms delay - inserts at head of timer list 12 + setTimeout(() => { 13 + results.push('timer2'); 14 + 15 + // Add another timer from within timer2 16 + setTimeout(() => { 17 + results.push('timer3'); 18 + }, 0); 19 + }, 0); 20 + }, 10); 21 + 22 + // Final check after all timers should have fired 23 + setTimeout(() => { 24 + results.push('done'); 25 + 26 + const passed = JSON.stringify(results) === JSON.stringify(expected); 27 + console.log('Results:', JSON.stringify(results)); 28 + console.log('Expected:', JSON.stringify(expected)); 29 + console.log('Test:', passed ? 'PASSED' : 'FAILED'); 30 + 31 + if (!passed) { 32 + process.exit(1); 33 + } 34 + }, 100); 35 + 36 + console.log('Timer mutation test started...');