|
43 | 43 | #endif |
44 | 44 |
|
45 | 45 | static void scheduler_timeout_callback(struct TimerListItem *it); |
| 46 | +static void scheduler_make_ready(Context *ctx); |
46 | 47 |
|
47 | 48 | static int update_timer_list(GlobalContext *global) |
48 | 49 | { |
@@ -233,18 +234,39 @@ Context *scheduler_run(GlobalContext *global) |
233 | 234 | // process signal messages and also empty outer list to inner list. |
234 | 235 | scheduler_process_native_signal_messages(result); |
235 | 236 | if (!(result->flags & Killed)) { |
236 | | - if (result->native_handler(result) == NativeContinue) { |
237 | | - // If native handler has memory fragments, garbage collect |
238 | | - // them |
239 | | - if (result->heap.root->next) { |
240 | | - if (UNLIKELY(memory_ensure_free_opt(result, 0, MEMORY_FORCE_SHRINK) != MEMORY_GC_OK)) { |
241 | | - fprintf(stderr, "Out of memory error in native handler\n"); |
242 | | - AVM_ABORT(); |
| 237 | + if (mailbox_has_next(&result->mailbox)) { |
| 238 | + if (result->native_handler(result) == NativeContinue) { |
| 239 | + // If native handler has memory fragments, garbage collect |
| 240 | + // them |
| 241 | + if (result->heap.root->next) { |
| 242 | + if (UNLIKELY(memory_ensure_free_opt(result, 0, MEMORY_FORCE_SHRINK) != MEMORY_GC_OK)) { |
| 243 | + fprintf(stderr, "Out of memory error in native handler\n"); |
| 244 | + AVM_ABORT(); |
| 245 | + } |
243 | 246 | } |
| 247 | + context_update_flags(result, ~Running, NoFlags); |
| 248 | + // The context was marked ready for the first message |
| 249 | + // However, another message may have arrived before it |
| 250 | + // was marked running in scheduler_run0, so there can |
| 251 | + // be several messages in the mailbox. Yet, the handler |
| 252 | + // may have processed only one message. So we rechedule |
| 253 | + // to make sure the handler process all messages. |
| 254 | + if (mailbox_has_next(&result->mailbox)) { |
| 255 | + scheduler_make_ready(result); |
| 256 | + } |
| 257 | + } else { |
| 258 | + scheduler_terminate(result); |
244 | 259 | } |
245 | | - context_update_flags(result, ~Running, NoFlags); |
246 | 260 | } else { |
247 | | - scheduler_terminate(result); |
| 261 | + // Don't call the native handler if the mailbox is empty |
| 262 | + // so native handlers can safely expect the first call to |
| 263 | + // `mailbox_first` will return a message. |
| 264 | + // |
| 265 | + // Indeed, a native process can be signaled and be made |
| 266 | + // ready, and in this case the mailbox may only contain |
| 267 | + // signal messages that are processed and removed by |
| 268 | + // `scheduler_process_native_signal_messages`. |
| 269 | + context_update_flags(result, ~Running, NoFlags); |
248 | 270 | } |
249 | 271 | } |
250 | 272 | result = NULL; // Schedule next process (native or not) |
|
0 commit comments