Weird scheduling issue when using spinlocks in critical section with multiple cores
Posted: Sat Mar 06, 2021 11:54 pm
This may or may not be related to my watchdog timer issue in this thread(viewtopic.php?f=2&t=19752), but it seems different enough that I decided to post it here anyway.
I'm working with the following demo code (for illustrating how spinlocks work).
If I comment out the following lines:
the code works as expected. I see
"Task 0 critical section...
...done"
being printed to the console and the LED pin toggling every 100 ms.
However, when I leave the critical section lines uncommented (there is now a critical section in task 0), the LED begins to exhibit odd behavior. It stays on for about 5 ms, turns off, and remains off for about 295 ms.
My understanding is that portENTER_CRITICAL() stops the scheduler and disables interrupts in the current core. In the demo above, that should only stop interrupts (and scheduler) in core 0. Core 1 should be unaffected, which means I should see the LED toggling at the regular 100 ms rate (like it was before putting in the critical section lines), right? Is there something I missed regarding using both cores in the ESP32?
I'm working with the following demo code (for illustrating how spinlocks work).
Code: Select all
// Core definitions (assuming you have dual-core ESP32)
static const BaseType_t pro_cpu = 0;
static const BaseType_t app_cpu = 1;
// Settings
static const TickType_t task_hog = 200; // Time (ms) hogging the CPU
static const TickType_t task_delay = 100; // Time (ms) each task blocks itself
// Pins (change this if your Arduino board does not have LED_BUILTIN defined)
static const int led_pin = LED_BUILTIN;
// Globals
static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
//*****************************************************************************
// Functions
// Hogs the processor. Accurate to about 1 second (no promises).
static void hog_delay(uint32_t ms) {
for (uint32_t i = 0; i < ms; i++) {
for (uint32_t j = 0; j < 40000; j++) {
asm("nop");
}
}
}
//*****************************************************************************
// Tasks
// Task in Core 0
void doTask0(void *parameters) {
TickType_t timestamp;
char str[20];
// Do forever
while (1) {
// Do some long critical section (this is bad practice)
portENTER_CRITICAL(&spinlock);
Serial.println("Task 0 critical section...");
hog_delay(task_hog);
Serial.println("...done");
portEXIT_CRITICAL(&spinlock);
// Yield processor for a while
vTaskDelay(task_delay / portTICK_PERIOD_MS);
}
}
// Task in Core 1
void doTask1(void *parameters) {
TickType_t timestamp;
char str[20];
// Configure LED pin
pinMode(led_pin, OUTPUT);
// Do forever
while (1) {
// Toggle LED
digitalWrite(led_pin, !digitalRead(led_pin));
// Yield processor for a while
vTaskDelay(task_delay / portTICK_PERIOD_MS);
}
}
//*****************************************************************************
// Main (runs as its own task with priority 1 on core 1)
void setup() {
// Configure Serial
Serial.begin(115200);
// Wait a moment to start (so we don't miss Serial output)
vTaskDelay(1000 / portTICK_PERIOD_MS);
Serial.println();
Serial.println("---FreeRTOS Multicore Demo---");
// Start Task 0 (in core 0)
xTaskCreatePinnedToCore(doTask0,
"Task 0",
1024,
NULL,
1,
NULL,
pro_cpu);
// Start Task 1 (in core 1)
xTaskCreatePinnedToCore(doTask1,
"Task 1",
1024,
NULL,
2,
NULL,
app_cpu);
// Delete "setup and loop" task
vTaskDelete(NULL);
}
void loop() {
// Execution should never get here
}
Code: Select all
//portENTER_CRITICAL(&spinlock);
Code: Select all
//portEXIT_CRITICAL(&spinlock);
"Task 0 critical section...
...done"
being printed to the console and the LED pin toggling every 100 ms.
However, when I leave the critical section lines uncommented (there is now a critical section in task 0), the LED begins to exhibit odd behavior. It stays on for about 5 ms, turns off, and remains off for about 295 ms.
My understanding is that portENTER_CRITICAL() stops the scheduler and disables interrupts in the current core. In the demo above, that should only stop interrupts (and scheduler) in core 0. Core 1 should be unaffected, which means I should see the LED toggling at the regular 100 ms rate (like it was before putting in the critical section lines), right? Is there something I missed regarding using both cores in the ESP32?