Hi sonofcy, below is some code that quite quicky demonstrates the problem. It reproduces the hazard every handful of seconds.
#include <Arduino.h>
const char* test1 = "hello from task 1!";
const char* test2 = "GREETINGS FROM TASK II";
// --- Global Variables ---
// This global variable will be subject to a race condition.
String global_message = "";
// A spinlock to protect the global_message variable within a critical section.
portMUX_TYPE string_spinlock = portMUX_INITIALIZER_UNLOCKED;
// --- Task Functions ---
// Task 1: Modifies the global_message string.
// String manipulation is not atomic and can be interrupted, leading to corruption.
void task_modify_string_1(void* pvParameters) {
for (;;) {
// Overwrite the global string with a new message.
global_message = test1;
Serial.print(".");
vTaskDelay(pdMS_TO_TICKS(150)); // Yield control for 150ms
}
}
// Task 2: Also modifies the global_message string.
// This task will race with task_modify_string_1, potentially causing
// non-thread-safe corruption if a context switch occurs mid-operation.
void task_modify_string_2(void* pvParameters) {
for (;;) {
// Overwrite the global string with a new message.
global_message = test2;
Serial.print(".");
vTaskDelay(pdMS_TO_TICKS(150)); // Yield control for 150ms
}
}
// Task 3: Monitors the global_message and prints if it's in an unexpected state.
// This task uses a critical section to ensure the check and print are atomic.
void task_monitor_string(void* pvParameters) {
for (;;) {
// Enter a critical section to prevent other tasks from interrupting.
taskENTER_CRITICAL(&string_spinlock);
// Check for a corrupted state.
if (global_message != test1 && global_message != test2) {
Serial.print("\nCORRUPTED MESSAGE DETECTED: ");
Serial.println(global_message);
}
// Exit the critical section.
taskEXIT_CRITICAL(&string_spinlock);
// Pause for a moment to let other tasks run.
vTaskDelay(pdMS_TO_TICKS(50));
}
}
// --- Arduino Setup Function ---
void setup() {
Serial.begin(19200);
// Give some time for the serial monitor to initialize.
delay(10000);
Serial.println("Starting FreeRTOS tasks to demonstrate race conditions.");
Serial.println("Observe the unpredictable output on the Serial Monitor.");
Serial.println("------------------------------------------------------");
delay(10000);
// Create the three tasks with a priority of 1.
// The scheduler will handle running them concurrently.
xTaskCreate(
task_modify_string_1,
"StringTask1",
2048,
NULL,
1,
NULL
);
xTaskCreate(
task_modify_string_2,
"StringTask2",
2048,
NULL,
1,
NULL
);
xTaskCreate(
task_monitor_string,
"MonitorStringTask",
2048,
NULL,
1,
NULL
);
}
// --- Arduino Loop Function ---
void loop() {
// The main loop is empty as all functionality is handled by the FreeRTOS tasks.
}
Looked at naively , each task assigns its value to the variable (either it does or it doesn't). A third task beings a critical (uninterruptible) piece of code to examine tha variable check it matches only one of the two values being assigned. It should only output if it finds that is not the case.
But indeed it does so, because the nature of threads is that can can interrupt each other at any time, and the process that makes up a simple matter of assigning a string consists of many instructions.... so you get corruption, a value that should not exist if the high level instruction viewed naively - serially.
It can be 'quite' timing sensitive, so change the instructions in there and the problem can go away as the timing changes, you can even easily block many instructions from ever completing, but I got this quite quickly, and its quite streamlined.
CORRUPTED MESSAGE DETECTED: GREETIfrom task 1!
........................................................................................
CORRUPTED MESSAGE DETECTED: hello from task ASK II
..
CORRUPTED MESSAGE DETECTED: helETINGS FROM TASK II
........................................................................................
CORRUPTED MESSAGE DETECTED: hello fromFROM TASK II
..
CORRUPTED MESSAGE DETECTED: helETINGS FROM TASK II
........................................................................................
CORRUPTED MESSAGE DETECTED: hello from tas TASK II
..........................................................................................
CORRUPTED MESSAGE DETECTED: hello from tOM TASK II
..
CORRUPTED MESSAGE DETECTED: heEETINGS FROM TASK II