Be careful when using xTaskAbortDelay() along vTaskDelayUntil()

This is an advice.

xTaskAbortDelay() breaks vTaskDelayUntil() unless you have a way to recompute lastWakeUp. Let’s look at this example:

void task1( void* pvParameters )
{
   pinMode( 13, OUTPUT );
   TickType_t last_wakeup = xTaskGetTickCount();
   bool state = false;

   while( 1 )
   {
      vTaskDelayUntil( &last_wakeup, pdMS_TO_TICKS( 500 ) );
      digitalWrite( 13, (state=!state) );
      xTaskNotifyGive( task0_handler );
   }
}

void task0( void* pvParameters )
{
   uint16_t cont = 6;
   bool state = false;

   while( 1 )
   {
      ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
      digitalWrite( 2, (state=!state) );
      if( --cont == 0 )
      {
         cont = 6;
         vTaskDelay( pdMS_TO_TICKS( 250 ) );
         xTaskAbortDelay( task1_handler );
      }
   }
}

Task0 (that has priority 0) wakes up Task1 (that has priority 1) earlier every 6 counts, as seen. However, there’s no way that Task1 is aware it was woke up before time, so strange things might happen. My system (Arduino UNO using the 8bit chip atmega328) resets or hangs, nobody knows which one will happen!

Output:

31
32
33
4081516544S4
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  OUT OF RESET
1
2

It always resets at the count of 48, and after some resets the system hangs.

And don’t even think about using xTaskDelayUntil():

void task1( void* pvParameters )
{
   pinMode( 13, OUTPUT );
   TickType_t last_wakeup = xTaskGetTickCount();
   bool state = false;

   while( 1 )
   {
      if( xTaskDelayUntil( &last_wakeup, pdMS_TO_TICKS( 500 ) == pdFALSE ) )
      {
         last_wakeup = xTaskGetTickCount();
      }

      digitalWrite( 13, (state=!state) );
      xTaskNotifyGive( task0_handler );
   }
}

It just doesn’t work at all! The system doesn’t even start.

Yes, I know that this example is a corner case, but you can’t say I didn’t tell you so.

BTW, there is not an interrupt safe version of xTaskAbortDelay(). Developers say they don’t see why should it exist (look for it in the forum).

There are a good news, after all: It seems thatxTaskAbortDelay() works correctly along vTaskDelay().

Happy coding!

Is this just an eccentric way of reporting a bug, or are you asking for help? In either case you will need to be more explicit.

In regards to vTaskDelayUntil() working and xTaskDelayUntil() not working at all - you will see here that they are the same function. One is just a macro that calls the other to maintain backward compatibility.

In regards to vTaskDelayUntil() behavior when unblocked using xTaskAbortDelay() - if your application is coded to wake the task early then your application will also need to manually adjust the time at which the task should next unblock on subsequent calls. The function itself is designed for periodic execution - if it adjusted the next unblock time itself when it was forced out of the blocked state it would be arbitrary as to whether it adjusted it to the time it should have unblocked or the time it should unblock after that - in both cases it would not be running with a fixed period.

My thought on this is that if you abort the delay, the recorded wake up time might be in the future, which gets interpreted as the distant past, and the function will go into ‘catch-up’ mode for a very long time (especially with a 32-bit tick) which might seem like it is broken.

Hi,

Now I’m able to debug the atmega328 chip stable and fluently and I found something weird: when calling the function xTaskAbortDelay() along the function vTaskDelayUntil() the system halts (fires an assertion) because of the variable uxSchedulerSuspended got a value other than 0:

(gdb) monitor reset halt
Resetting MCU...
(gdb) c
Continuando.
^C                             // HERE THE SYSTEM HALTS SO I EXECUTE A CTRL-C
Program received signal SIGINT, Interrupt.
0x00001cca in xTaskDelayUntil (pxPreviousWakeTime=0x8003cf, xTimeIncrement=500)
    at /home/fjrg76/arduino-1.8.13/libraries/FreeRTOS/src/tasks.c:1257
1257	        configASSERT( uxSchedulerSuspended == 0 );
(gdb) p *pxPreviousWakeTime 
$9 = 8000
(gdb) p xTimeIncrement 
$10 = 500
(gdb) p uxSchedulerSuspended 
$11 = 43 '+'                  // IS THAT NORMAL?
(gdb) backtrace 
#0  0x00001cca in xTaskDelayUntil (pxPreviousWakeTime=0x8003cf, xTimeIncrement=500)
    at /home/fjrg76/arduino-1.8.13/libraries/FreeRTOS/src/tasks.c:1257
#1  0x000001a2 in task1 (pvParameters=0x0 <__vectors>) at main.cpp:58
#2  0x00006644 in ?? ()
(gdb) list
1252	        TickType_t xTimeToWake;
1253	        BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
1254	
1255	        configASSERT( pxPreviousWakeTime );
1256	        configASSERT( ( xTimeIncrement > 0U ) );
1257	        configASSERT( uxSchedulerSuspended == 0 ); // THIS IS THE ASSERT THAT FIRES
1258	
1259	        vTaskSuspendAll();
1260	        {
1261	            /* Minor optimisation.  The tick count cannot change in this
(gdb) 

This is the complete code (well, only the main.cpp file where I coded the sample): main.cpp (3.5 KB)

Both =) Posts ago I asked you for the atmega323 maintainer so I can address my issue directly with the programmer but I received no answers.

I try to be as explicit as I can, but I know that sometimes isn’t enough.

Yeah, I saw that in the source code, so I have no idea why the system crashes. I want to create a video showing you what’s going on.

But the reason to exist for the xTaskAbortDelay() is to wake the task up before time, so from time to time one will need to call it, but if one isn’t aware of such situation, problems will arise.

Thank you!

Could this be just a data corruption caused by something like a stack overflow? What value does uxSchedulerSuspended get - if it were something other than 1 corruption would be a likely cause.

Hi,

(Source code at the end.)

The Arduino’s serial print utility (Serial.print()) seems to be causing a stack overflow, so I’ve eliminated it from the project… but the problem still exists.

Facts:

If I use vTaskDelay(), instead of vTaskDelayUntil(), along xTaskAbortDelay(), the program runs smoothly. :slight_smile:

If I use vTaskDelayUntil() without xTaskAbortDelay(), the program runs smoothly. :slight_smile:

If I use vTaskDelayUntil() along xTaskAbortDelay(), the program fails. :frowning:

In such case apparently there’s not any stack overflow. After I execute a Ctrl-c in GDB I got this:

(gdb) p *task1_handler 
$6 = {pxTopOfStack = 0x8002b7 "\034", xStateListItem = {xItemValue = 32568, pxNext = 0x800160 <pxReadyTasksLists+12>, 
    pxPrevious = 0x800160 <pxReadyTasksLists+12>, pvOwner = 0x8002f3, pvContainer = 0x80015d <pxReadyTasksLists+9>}, xEventListItem = {xItemValue = 2, 
    pxNext = 0x0 <__vectors>, pxPrevious = 0x0 <__vectors>, pvOwner = 0x8002f3, pvContainer = 0x0 <__vectors>}, uxPriority = 1 '\001', 
  pxStack = 0x8001f1 "", pcTaskName = "T1\000\000", ulNotifiedValue = {0}, ucNotifyState = "", ucStaticallyAllocated = 0 '\000', 
  ucDelayAborted = 0 '\000'}
(gdb) p/x (*task1_handler->pxStack)@32
$17 = {0xa5 <repeats 32 times>}            // I might presume there aren't any overflows

But more important, the variable uxSchedulerSuspended gets corrupted somewhere. The program runs as expected for some time, and then it fails:

...
Breakpoint 2, xTaskAbortDelay (xTask=<optimized out>) at /home/fjrg76/arduino-1.8.13/libraries/FreeRTOS/src/tasks.c:2720
2720	    }
1: uxSchedulerSuspended = 0 '\000'
(gdb) c
Continuando.

Breakpoint 2, xTaskAbortDelay (xTask=<optimized out>) at /home/fjrg76/arduino-1.8.13/libraries/FreeRTOS/src/tasks.c:2720
2720	    }
1: uxSchedulerSuspended = 0 '\000'
(gdb) c
Continuando.

Breakpoint 2, xTaskAbortDelay (xTask=<optimized out>) at /home/fjrg76/arduino-1.8.13/libraries/FreeRTOS/src/tasks.c:2720
2720	    }
1: uxSchedulerSuspended = 0 '\000'
(gdb) c
Continuando.
^C                  // HERE THE PROGRAM HAS FAILED
Program received signal SIGINT, Interrupt.
0x00000eda in xTaskDelayUntil (pxPreviousWakeTime=0x8002e4, xTimeIncrement=500) at /home/fjrg76/arduino-1.8.13/libraries/FreeRTOS/src/tasks.c:1257
1257	        configASSERT( uxSchedulerSuspended == 0 );
1: uxSchedulerSuspended = 169 '\251'
(gdb)

Unfortunately the GDB for the atmega328 is limited and watchpoints don’t work.

Any hint why or where uxSchedulerSuspended gets corrupted or how I can catch the moment in which it gets corrupted without using the “watching” GDB’s utility?

I cannot write any application for this chip as long as this problem isn’t solved.

Thank you!

Source code:

#include <Arduino.h>
void initVariant() __attribute__((weak));
void setupUSB() __attribute__((weak));
int atexit(void (* /*func*/ )()) { return 0; }
void initVariant() { }
void setupUSB() { }

#include <FreeRTOS.h>
#include <task.h>

TaskHandle_t task0_handler;
TaskHandle_t task1_handler;

void task1( void* pvParameters )
{
   pinMode( 13, OUTPUT );
   uint16_t cont = 0;
   TickType_t last_wakeup = xTaskGetTickCount();
   bool state = false;

   while( 1 )
   {
      vTaskDelayUntil( &last_wakeup, pdMS_TO_TICKS( 500 ) );

      digitalWrite( 13, (state=!state) );

      xTaskNotifyGive( task0_handler );
   }
}

void task0( void* pvParameters )
{
   uint16_t cont = 5;
   bool state = false;

   while( 1 )
   {
      ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

      digitalWrite( 2, (state=!state) );

      --cont;
      if( cont == 0 )
      {
         cont = 6;
         vTaskDelay( pdMS_TO_TICKS( 100 ) );

         xTaskAbortDelay( task1_handler );  // HERE IS THE CALLING
      }
   }
}

int main(void)
{
   cli();
   init();
	initVariant();
#if defined(USBCON)
	USBDevice.attach();
#endif

   if( xTaskCreate( task1, "T1", 128 * 2, 0, tskIDLE_PRIORITY+1, &task1_handler ) != pdPASS ) 
      configASSERT( 0 );

   if( xTaskCreate( task0, "T0", 128 * 4, 0, tskIDLE_PRIORITY, &task0_handler ) != pdPASS ) 
      configASSERT( 0 );

   pinMode( 2, OUTPUT );
   digitalWrite( 2, LOW );
   vTaskStartScheduler();

   while( 1 )
   {
   }

   return 0;
}

#ifdef __cplusplus
extern "C"
{
#endif

   void vApplicationStackOverflowHook( TaskHandle_t task, char* pcTaskName )
   {
      digitalWrite( 2, HIGH );
   }

   void vApplicationIdleHook()
   {
   }

   void vApplicationMallocFailedHook()
   {
      cli();

      pinMode( 13, OUTPUT );

      while( 1 )
      {
         digitalWrite( 13, HIGH );
         for( size_t i = 65000; i > 0; --i);
         digitalWrite( 13, LOW );
         for( size_t i = 65000; i > 0; --i);
      }
   }

#ifdef __cplusplus
}
#endif

In your case this line is going to see that the last wake time is ahead of the current time (because the task’s delay was aborted) and so assume the tick count overflowed since the task last entered the function.

What is it you are actually trying to achieve - there is probably a different method you can use.

I have some experimental code that attempts to handle aborted delays within xTaskDelayUntil() - it makes your code work but I’m not sure of other implications - corner cases in particular.

I already know why, how and when to use the delay functions; however, incredibly I did’nt know that the function xTaskAbortDelay() did exist (at least I haven’t paid so much attention to it) so I wanted to experiment with it in simple programs, and I never thought it would turn into a nightmare.

I can think of scenarios where xTaskDelayUntil() and xTaskAbortDelay() can coexist in peace, but, as my example has shown, I can’t even think to use them along in the same program.

If a task is aware it can be waken-up earlier then it’s easy to implement code that re synchronize it; otherwise such situation might cause serious problems (you might include a warning in the documentation).

Thank you anyway for your time :slight_smile: . I’ll come back sooner than later with more awkard questions. Meanwhile I’ll give up for the atmega328 port (it had potential).