diff --git a/tests/thread_race/Makefile b/tests/thread_race/Makefile new file mode 100644 index 000000000000..9e94f9c66856 --- /dev/null +++ b/tests/thread_race/Makefile @@ -0,0 +1,10 @@ +include ../Makefile.tests_common + +DISABLE_MODULE += auto_init + +TEST_ON_CI_WHITELIST += all + +include $(RIOTBASE)/Makefile.include + +test: + tests/01-run.py diff --git a/tests/thread_race/main.c b/tests/thread_race/main.c new file mode 100644 index 000000000000..72b9ccb63d5e --- /dev/null +++ b/tests/thread_race/main.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 Matthew Blue + * + * 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 test + * @{ + * + * @file + * @brief Test for race conditions in context switching + * + * May have false negatives. + * + * @author Matthew Blue + * + * @} + */ + +#include + +#include "irq.h" +#include "sched.h" +#include "thread.h" + +char iqr_check_stack[THREAD_STACKSIZE_DEFAULT]; + +static volatile uint8_t irq_occurred; + +static void *_thread_irq_check(void *arg) +{ + (void)arg; + + while (1) { + irq_occurred = 1; + + thread_sleep(); + } + + return NULL; +} + +/* Mostly copied from thread_wakeup() */ +static void _thread_wake_wo_yield(kernel_pid_t pid) +{ + unsigned old_state = irq_disable(); + + thread_t *other_thread = (thread_t *) thread_get(pid); + + sched_set_status(other_thread, STATUS_RUNNING); + + irq_restore(old_state); +} + +static void _spin(void) +{ + /* Volatile so it is not messed with by optimizations */ + volatile uint8_t i; + + for (i = 0; i < 255; i++) ; +} + +int main(void) +{ + puts("Context swap race condition test application"); + + kernel_pid_t pid; + + puts("Starting IRQ check thread"); + pid = thread_create(iqr_check_stack, sizeof(iqr_check_stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_SLEEPING, + _thread_irq_check, NULL, "irqchk"); + + printf("Checking for working context swap (to detect false positives)... "); + irq_occurred = 0; + _thread_wake_wo_yield(pid); + + thread_yield_higher(); + + /* Delay so we are not testing for race conditions also */ + _spin(); + + if (irq_occurred == 1) { + puts("[Success]"); + } + else { + puts("[Failed]"); + return -1; + } + + printf("Checking for reset of swaps (to detect false positives)... "); + irq_occurred = 0; + _thread_wake_wo_yield(pid); + + thread_yield_higher(); + + /* Delay so we are not testing for race conditions also */ + _spin(); + + if (irq_occurred == 1) { + puts("[Success]"); + } + else { + puts("[Failed]"); + return -1; + } + + /* Volatile so it is not messed with by optimizations */ + volatile uint8_t race_test; + + printf("Checking for context swap race condition... "); + irq_occurred = 0; + _thread_wake_wo_yield(pid); + + thread_yield_higher(); + + /* Race instruction */ + race_test = irq_occurred; + + if (race_test == 1) { + puts("[Success]"); + } + else { + puts("[Failed]"); + return -1; + } + + return 0; +} diff --git a/tests/thread_race/tests/01-run.py b/tests/thread_race/tests/01-run.py new file mode 100755 index 000000000000..de6faabb3c82 --- /dev/null +++ b/tests/thread_race/tests/01-run.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018 Matthew Blue +# +# 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 os +import sys + + +def testfunc(child): + child.expect_exact("Context swap race condition test application") + child.expect_exact("Starting IRQ check thread") + child.expect_exact( + "Checking for working context swap (to detect false positives)... [Success]") + child.expect_exact( + "Checking for reset of swaps (to detect false positives)... [Success]") + child.expect_exact("Checking for context swap race condition... [Success]") + + +if __name__ == "__main__": + sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner')) + from testrunner import run + sys.exit(run(testfunc))