diff --git a/tests/thread_msg_block_race/Makefile b/tests/thread_msg_block_race/Makefile new file mode 100644 index 000000000000..08e58b1d8287 --- /dev/null +++ b/tests/thread_msg_block_race/Makefile @@ -0,0 +1,14 @@ +DEVELHELP := 1 + +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-nano arduino-uno \ + nucleo-f031k6 + +DISABLE_MODULE += auto_init +FEATURES_REQUIRED += periph_timer +USEMODULE += random + +TEST_ON_CI_WHITELIST += all + +include $(RIOTBASE)/Makefile.include diff --git a/tests/thread_msg_block_race/main.c b/tests/thread_msg_block_race/main.c new file mode 100644 index 000000000000..39ff14de3d22 --- /dev/null +++ b/tests/thread_msg_block_race/main.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Thread race condition test application to reproduce + * https://github.com/RIOT-OS/RIOT/issues/10881 + * + * @author Martine Lenders + * + * @} + */ + +#include +#include + +#include "periph/timer.h" +#include "random.h" +#include "thread.h" +#include "msg.h" + +#define CANARY_TYPE (0x21fd) + +#define TIMER_FREQ (1000000LU) +#define TIMER_TIMEOUT_MIN (1U) +#define TIMER_TIMEOUT_MAX (100U) + +static char _stack[THREAD_STACKSIZE_DEFAULT]; + +static kernel_pid_t _pid_main = KERNEL_PID_UNDEF; + +/** + * @brief Schedule next timer event in TIMER_TIMEOUT_MIN to TIMER_TIMEOUT_MAX + * ticks. + */ +static void _sched_next(void) +{ + timer_set(TIMER_DEV(0), 0, random_uint32_range(TIMER_TIMEOUT_MIN, + TIMER_TIMEOUT_MAX)); +} + +/** + * @brief The timer interrupt + */ +static void _timer(void *arg, int channel) +{ + (void)arg; + (void)channel; + /* just continue rescheduling interrupt triggering at random time */ + _sched_next(); +} + +/** + * @brief The sending thread + */ +static void *_thread(void *arg) +{ + (void) arg; + + while (1) { + msg_t msg = { .type = 0U }; + + write(STDOUT_FILENO, ".", 1U); + /* send without blocking */ + msg_try_send(&msg, _pid_main); + thread_yield(); + } + + return NULL; +} + +int main(void) +{ + kernel_pid_t pid; + + timer_init(TIMER_DEV(0), TIMER_FREQ, _timer, NULL); + random_init(timer_read(TIMER_DEV(0))); + puts("Test is \"successful\" if it runs forever without halting\n" + "on any of the assertion in this file\n"); + _pid_main = sched_active_pid; + + puts("I will try to trigger an interrupt at random intervals. When an\n" + "interrupt is fired while ISR is disable in the thread_yield_higher()\n" + "function some platform-specific implementations used to not call\n" + "sched_run() which was the cause of the bug tested here"); + _sched_next(); + pid = thread_create(_stack, sizeof(_stack), THREAD_PRIORITY_MAIN + 1, + THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST, + _thread, NULL, "nr2"); + assert(pid != KERNEL_PID_UNDEF); + + while (1) { + msg_t msg = { .type = CANARY_TYPE }; + + /* receive blocked */ + msg_receive(&msg); + /* check msg_receive() returned without blocking (i.e. the sending + * thread did not get a chance to copy the message over) */ + if (msg.type == CANARY_TYPE) { + puts("Message was not written"); + return 1; + } + write(STDOUT_FILENO, "\b", 1U); + } + return 0; +} diff --git a/tests/thread_msg_block_race/tests/01-run.py b/tests/thread_msg_block_race/tests/01-run.py new file mode 100755 index 000000000000..e57d08dcd626 --- /dev/null +++ b/tests/thread_msg_block_race/tests/01-run.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run +from pexpect import TIMEOUT + + +def testfunc(child): + res = child.expect([TIMEOUT, "Message was not written"]) + # we actually want the timeout here. The application runs into an assertion + # pretty quickly when failing and runs forever on success + assert(res == 0) + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=10))