Open gfvalvo opened 1 year ago
@gfvalvo - Good point... It seems that the time slice must expire before the context switch occurs.
Not sure about the reason. I can see that #define configUSE_PREEMPTION 1
is used in Arduino FreeRTOSConfig.h
...
@me-no-dev - Do you have any clue about it?
Hey, I think High priority task does not switch to low priority task until vTaskDelay(). You can try below code snippest. also you have used Events so 2nd task blocked until event set. So, after setting bit from high priority task without delay/vTaskDelay it will not switch task.
#include "Arduino.h"
void producerFunction(void *pvParameters);
void consumerFunction(void *pvParameters);
EventGroupHandle_t controlGroupHandle;
void setup() {
Serial.begin(115200);
vTaskDelay(2000);
log_i("Starting");
controlGroupHandle = xEventGroupCreate();
xTaskCreatePinnedToCore(consumerFunction, "Consumer Task 1", 2000, NULL, 2, NULL, CONFIG_ARDUINO_RUNNING_CORE);
vTaskDelay(500);
xTaskCreatePinnedToCore(producerFunction, "Producer Task", 2000, NULL, 2, NULL, CONFIG_ARDUINO_RUNNING_CORE);
}
void producerFunction(void *pvParameters) {
log_i("Producer Task Started\n");
for (;;) {
vTaskDelay(2000);
log_i("Producer Function: Setting Event Bits");
xEventGroupSetBits(controlGroupHandle, 1);
vTaskDelay(1);
log_i("Producer Function: Event Bits Set");
}
}
void consumerFunction(void *pvParameters) {
log_i("Consumer Task Started");
for (;;) {
xEventGroupWaitBits(controlGroupHandle, 1, pdTRUE, pdFALSE, portMAX_DELAY);
log_i("Consumer Task Received Event Bit");
}
}
void loop() {
vTaskDelete(NULL);
}
Hey, I think High priority task does not switch to low priority task until vTaskDelay(). You can try below code snippest. also you have used Events so 2nd task blocked until event set. So, after setting bit from high priority task without delay/vTaskDelay it will not switch task.
Yes, I tested it with the delay before I posted my question and got the same behavior as you show. But, IMO, it's not correct. A higher-priority task always preempt when it becomes ready (unblocked) as a result of a Task Notification, data posting to a Queue, etc. Why would it be different when an Event Group bit is set?
I received same results by using notification...
#include "Arduino.h"
void producerFunction(void *pvParameters);
void consumerFunction(void *pvParameters);
EventGroupHandle_t controlGroupHandle;
TaskHandle_t task1,task2;
void setup() {
Serial.begin(115200);
vTaskDelay(2000);
Serial.println("Starting");
controlGroupHandle = xEventGroupCreate();
xTaskCreatePinnedToCore(consumerFunction, "Consumer Task 1", 2000, NULL, 2, &task1, CONFIG_ARDUINO_RUNNING_CORE);
vTaskDelay(500);
xTaskCreatePinnedToCore(producerFunction, "Producer Task", 2000, NULL, 2, &task2, CONFIG_ARDUINO_RUNNING_CORE);
}
void producerFunction(void *pvParameters) {
Serial.println("Producer Task Started\n");
for (;;) {
vTaskDelay(2000);
Serial.println("Producer Function: Setting Event Bits");
// xEventGroupSetBits(controlGroupHandle, 1);
xTaskNotify(task1,1,eNoAction);
Serial.println("Producer Function: Event Bits Set");
}
}
void consumerFunction(void *pvParameters) {
Serial.println("Consumer Task Started");
uint32_t val;
for (;;) {
// xEventGroupWaitBits(controlGroupHandle, 1, pdTRUE, pdFALSE, portMAX_DELAY);
xTaskNotifyWait(1,0,&val,portMAX_DELAY);
Serial.println("Consumer Task Received Event Bit");
}
}
void loop() {
vTaskDelete(NULL);
}
I received same results by using notification...
That's because your Producer and Consumer tasks have the same priority, so there'll be no preemption. Try the code below. Note the relative priority of the 3 tasks (1 Producer + 2 Consumers). You'll see that reflected in the order of the printouts.
#include "Arduino.h"
void producerFunction(void *pvParameters);
void consumerFunction(void *pvParameters);
TaskHandle_t consumer0TaskHandle;
TaskHandle_t consumer1TaskHandle;
void setup() {
Serial.begin(115200);
vTaskDelay(2000);
Serial.println("Starting");
uint32_t ConsumerTaskNumber = 0;
xTaskCreatePinnedToCore(consumerFunction, "Consumer Task 0", 2000, &ConsumerTaskNumber, 2, &consumer0TaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
vTaskDelay(500);
ConsumerTaskNumber = 1;
xTaskCreatePinnedToCore(consumerFunction, "Consumer Task 1", 2000, &ConsumerTaskNumber, 6, &consumer1TaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
vTaskDelay(500);
xTaskCreatePinnedToCore(producerFunction, "Producer Task", 2000, NULL, 4, NULL, CONFIG_ARDUINO_RUNNING_CORE);
}
void producerFunction(void *pvParameters) {
Serial.println("Starting Producer Task");
vTaskDelay(2000);
Serial.printf("\nProducer Task: Sending Notifications\n");
xTaskNotifyGive(consumer0TaskHandle);
xTaskNotifyGive(consumer1TaskHandle);
Serial.printf("Producer Task: Notifications Sent\n");
for (;;) {
vTaskDelay(1000);
}
}
void consumerFunction(void *pvParameters) {
uint32_t taskNumber = *reinterpret_cast<uint32_t*>(pvParameters);
Serial.printf("Starting Consumer Task # %d\n", taskNumber);
for (;;) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
Serial.printf("Consumer Task %d Received Notification\n", taskNumber);
}
}
void loop() {
}
Yes, I forgot to change during testing. After changing to 6 output as expected.
I received same results by using notification...
That's because your Producer and Consumer tasks have the same priority, so there'll be no preemption. Try the code below. Note the relative priority of the 3 tasks (1 Producer + 2 Consumers). You'll see that reflected in the order of the printouts.
#include "Arduino.h" void producerFunction(void *pvParameters); void consumerFunction(void *pvParameters); TaskHandle_t consumer0TaskHandle; TaskHandle_t consumer1TaskHandle; void setup() { Serial.begin(115200); vTaskDelay(2000); Serial.println("Starting"); uint32_t ConsumerTaskNumber = 0; xTaskCreatePinnedToCore(consumerFunction, "Consumer Task 0", 2000, &ConsumerTaskNumber, 2, &consumer0TaskHandle, CONFIG_ARDUINO_RUNNING_CORE); vTaskDelay(500); ConsumerTaskNumber = 1; xTaskCreatePinnedToCore(consumerFunction, "Consumer Task 1", 2000, &ConsumerTaskNumber, 6, &consumer1TaskHandle, CONFIG_ARDUINO_RUNNING_CORE); vTaskDelay(500); xTaskCreatePinnedToCore(producerFunction, "Producer Task", 2000, NULL, 4, NULL, CONFIG_ARDUINO_RUNNING_CORE); } void producerFunction(void *pvParameters) { Serial.println("Starting Producer Task"); vTaskDelay(2000); Serial.printf("\nProducer Task: Sending Notifications\n"); xTaskNotifyGive(consumer0TaskHandle); xTaskNotifyGive(consumer1TaskHandle); Serial.printf("Producer Task: Notifications Sent\n"); for (;;) { vTaskDelay(1000); } } void consumerFunction(void *pvParameters) { uint32_t taskNumber = *reinterpret_cast<uint32_t*>(pvParameters); Serial.printf("Starting Consumer Task # %d\n", taskNumber); for (;;) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); Serial.printf("Consumer Task %d Received Notification\n", taskNumber); } } void loop() { }
All the testing I have done indicates that the context switching only happens at the end of the time slice (1ms in Arduino).
I can also use a delayMicroseconds()
which forces busy wait and no context switch happens.
delay()
uses vTaskDelay()
and then FreeRTOS changes the context on its execution.
The FreeRTOS documentation says that when PREEMPTION is enabled, xEventGroupSetBits()
should change the context at the end of its execution, preempting the low priority task, switching to a higher priority task that is waiting for that EventGroup. But it doesn't happen with Arduino...
My suggestion:
Try a pure IDF application and check the FreeRTOS setting to make sure that it has configUSE_PREEMPTION
enabled.
In case the application works correctly, that may mean that something is not set as expected when the Arduino FreeRTOS Libraries are built.
In case the application works correctly, that may mean that something is not set as expected when the Arduino FreeRTOS Libraries are built.
Like I said, it does work correctly when using Task Notifications instead of Event Group bits.
Like I said, it does work correctly when using Task Notifications instead of Event Group bits.
So this may be the necessary workaround.
@VojtechBartoska: According to further investigation in another forum: https://www.esp32.com/viewtopic.php?f=19&t=35450#p119563 The issue also happens using ESP-IDF. So, their conclusion is: "This is an Espressif freertos port issue; changing over to the Amazon kernel fixes the behaviour."
Thanks @gfvalvo! The given conclusion sounds really interesting... I'll also follow this with ESP_Sprite.
Hello, I have checked our internal tracking tool and the status is still open. I will double check it with ESP-IDF colleagues.
Board
ESP32 Dev Module, Adafruit ESP32 Feather Huzzah
Device Description
None
Hardware Configuration
Just the board connected to PC via USB.
Version
v2.0.11
IDE Name
Arduino IDE
Operating System
Windows 10
Flash frequency
80 MHz
PSRAM enabled
yes
Upload speed
921600
Description
I don't know if this is an Issue (bug) or simply a known limitation of FreeRTOS. It appears that when an Event Group bit is set with the intention of unblocking a high-priority task, that task does not immediately preempt the low-priority task that set the Event Group bit. Rather, it looks like the context switch does not occur until either the low-priority task is blocked or perhaps when the its Time Slice has expired.
In the MRE below, the Consumer Task has a higher priority than the Producer Task. So, when the Producer sets the Event Group bit, I would expect the Consumer Task to preempt it, do its printing, and then block itself again BEFORE the (lower priority) Producer Task resumes executing. Therefore, it seems that the printing sequence should be:
However, what I'm seeing is this:
However, the high-priority task DOES preempt if I use a FreeRTOS Notification instead of an Event Group bit.
Sketch
Debug Message
Other Steps to Reproduce
No response
I have checked existing issues, online documentation and the Troubleshooting Guide