Skip to content

Commit 682b185

Browse files
committed
Merge pull request atomvm#587 from pguyot/w20/fix-crash-in-native-handlers-being-signaled
Fix a bug where native ports would be executed with no message When a process is signaled, it is executed to process the signal, but the mailbox may be empty after the signal is processed. This would yield a crash on native handlers that expect `mailbox_first` to always return the message to consume. Also work around native ports that only process one message by rescheduling them if there is another message. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 22c8521 + f9b71db commit 682b185

File tree

1 file changed

+31
-9
lines changed

1 file changed

+31
-9
lines changed

src/libAtomVM/scheduler.c

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#endif
4444

4545
static void scheduler_timeout_callback(struct TimerListItem *it);
46+
static void scheduler_make_ready(Context *ctx);
4647

4748
static int update_timer_list(GlobalContext *global)
4849
{
@@ -233,18 +234,39 @@ Context *scheduler_run(GlobalContext *global)
233234
// process signal messages and also empty outer list to inner list.
234235
scheduler_process_native_signal_messages(result);
235236
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+
}
243246
}
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);
244259
}
245-
context_update_flags(result, ~Running, NoFlags);
246260
} 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);
248270
}
249271
}
250272
result = NULL; // Schedule next process (native or not)

0 commit comments

Comments
 (0)