Scheduler performing context switch for the same task

Hello.
I noticed strange behavior when debugging the system I am analyzing. The system has three periodic tasks:

“Task-1”, with 10 milliseconds of periodicity. High priority.
“Task-2”, with 10 milliseconds of periodicity. Medium priority.
“Task-3” with 10 milliseconds of periodicity. Low priority.

The content of the three tasks is just a critical section, a critical section that is shared. The inversion of priorities occurs frequently, and I am forcing this situation to see how the system behaves temporally. The Systick has a period of 1ms.

From what I read about FreeRTOS, in Cortex-M the context change of a task is done through a pending bit, which in the scenario of periodic tasks can be activated through:

  1. Tick timer handler finds a task with the highest priority in the ready queue and sets the pending context switch bit;

  2. In the “vDelayTaskUntil” function, which waits for the next activation;

  3. When a semaphore is busy and the task that tries to acquire it.

That said, I found this behavior:

Sometimes, while the high priority task is within the semaphore, the context switching takes place for itself, as explained below.

As noted, task-1 does a context switch for itself.

Do you know how to tell me why this happens?

Thank you.

Edit:

Attaching the code of test:

Task-1:

void vTask1_handler(void *params) {
	TickType_t xLastWakeTime;
	const TickType_t xFrequency = 10;
	xLastWakeTime = xTaskGetTickCount();
	int i = 0, to =0;

for (;;) {
	vTaskDelayUntil(&xLastWakeTime, xFrequency);
	to = DWT->CYCCNT;
	for (i = 0; i < to % 500; i++);
	if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
		to = DWT->CYCCNT;
		for (i = 0; i < to % 5000; i++);
		xSemaphoreGive(xSemaphore);
	}

}

Task-2:

void vTask2_handler(void *params) {
	TickType_t xLastWakeTime;
	const TickType_t xFrequency = 10;
	xLastWakeTime = xTaskGetTickCount();
	int i = 0, to = 0;

for (;;) {
	vTaskDelayUntil(&xLastWakeTime, xFrequency);
	to = DWT->CYCCNT;
	for (i = 0; i < to % 200; i++);
	if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
		to = DWT->CYCCNT;
		for (i = 0; i < to % 10000; i++);
		xSemaphoreGive(xSemaphore);
	}
}

Task-3:

void vTask3_handler(void *params) {
	TickType_t xLastWakeTime;
	const TickType_t xFrequency = 10;
	xLastWakeTime = xTaskGetTickCount();
	int i = 0, to=0;

for (;;) {
	vTaskDelayUntil(&xLastWakeTime, xFrequency);
	to = DWT->CYCCNT;
	for (i = 0; i < to % 200; i++);
	if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
		to = DWT->CYCCNT;
		for (i = 0; i < to % 10000; i++);
		xSemaphoreGive(xSemaphore);
	}
}

From your diagram it looks like the scheduler executes, but there is no context switch, which is what I think you mean by ‘context switch to itself’ - a context switch would be to another task. What is the code doing when the scheduler runs between systicks? Is it in the semaphore give function?

Hi Richard.
Thank you for helping me with this question. I really appreciate the work of all of you at Amazon for maintaining FreeRTOS and for the support of you and your team to the community;

My master’s thesis is to analyze temporal influences that can cause disruptions in tasks running on FreeRTOS.

The system context for my question is this. I want to see how FreeRTOS behaves when there are a lot of priority inversions. For this, I use the trace macros to make the measurements in the figure above.

“What is the code doing when the scheduler runs between systicks? Is it in the semaphore give function?”

No.

I put a pin up when it gives the shared resource. This does not seem to be the cause of the behavior.

This behavior (where a task is interrupted by the scheduler) happens only when priority inversions start.

In what situations do you think this might happen?

To make sure I understand then, the sequence is something like this:

  1. A low priority task (LP task) has a mutex.
  2. A high priority task (HP task) starts to execute.
  3. The HP task attempts to take the same mutex, causing the LP task to inherit its priority.

Then at some point in step 3 there is a call to the scheduler, although no task switch occurs.

Is that correct?

Yes, you are in the way, 50%.

I will complete the observed behavior:

  1. A low priority task (LP task) has a mutex.
  2. A high priority task (HP task) starts to execute.
  3. The HP task attempts to take the same mutex, causing the LP task to inherit its priority
  4. The LP gives the semaphore to HP task.
  5. HP take the mutex.
  6. HP starts to execute.
  7. At some point, HP suffer a interrupt caused by scheduler (vSwitchContext function executes. This is where the pin trace this event. SCHEDULER on diagram).
  8. After the scheduler executes vSwitchContext, HP keep execution on CPU.

What i want to know is how behavior 7 happens. To me it’s not sense. Systick Handler function, vDelayTaskUntil and SemaphoreTake are not called to pend the bit of scheduler.

This is a much more helpful way of describing the issue you are seeing than posting a chart for people to have to try and interpret :o)

There are other places that the scheduler may run. For example, if an event happens when the scheduler is locked the event will be processed when the scheduler is unlocked.

Can you try and catch the execution of the vSwitchContext with a break point? For example, set a flag when the condition at which this occurs is true, then inside vSwitchContext check the flag, and if it is set, hit a break point on an __asm volatile( “NOP” ); instruction (which you would have to add in temporarily). When you hit the break point see the call stack. That would not work if you were inside an interrupt, as you will only see the interrupt stack, but from what you are saying it doesn’t sound like you are in an interrupt.