Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Real time embedded FreeRTOS mailing list 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT Training




Loading

calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by oprs on September 26, 2015

Hi,

The following code causes FreeRTOS 8.2.2 to crash (Hard Fault observed on STM32):

~~~~

include
include

static void task( void *x ) { (void)x;

for( ;; ) vTaskDelay( 1000 / portTICKRATEMS ); }

int main( void ) { TaskHandle_t t1, t2;

(void)xTaskCreate( task, "task1", 128, NULL, 1, &t1 ); (void)xTaskCreate( task, "task2", 128, NULL, 1, &t2 );

vTaskSuspend( t1 ); vTaskResume( t1 ); // <- crash

vTaskStartScheduler();

for( ;; ); } ~~~~

(This is only a minimal, complete / verifiable example; basically I need to create a bunch of suspended tasks ready to be resumed later on, depending on the state my application finds itself in).

I've traced the problem down to vTaskResume(), specifically around line 1443 in tasks.c

~~~~ /* We may have just resumed a higher priority task. / if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) { / This yield may not cause the task just resumed to run, but will leave the lists in the correct state for the next yield. */ taskYIELDIFUSING_PREEMPTION(); } ~~~~

In my use case above, taskYIELDIFUSING_PREEMPTION() gets called, eventually leading to a context switch before the scheduler is started which, in turn, leads to...

~~~~ *** HARD FAULT HANDLER ***

R0: 0x00000000 R1: 0x20006f00 R2: 0x20001ce8 R3: 0x20001ce8 R12: 0xa5a5a5a5 LR: 0x00000000 PC: 0x00000000 PSR: 0x4000000e BFAR: 0xe000ed38 CFSR: 0x00020000 HFSR: 0x40000000 DFSR: 0x00000009 AFSR: 0x00000000 ~~~~

note: the fact that both PC and LR show as NULL here sure didn't help tracking down the problem. I didn't bother investingating much further though, as I'm not familiar enough with FreeRTOS internals to appreciate the possible ramifications of a context switch occuring outside the supervision of the scheduler.

Anyway, the following patch seems to fix the issue for vTaskResume(), but then again I guess other functions (xTaskResumeAll(), and possibly xTaskResumeFromISR()) should be fixed as well.

~~~~ diff --git a/sat/sys/FreeRTOS/tasks.c b/sat/sys/FreeRTOS/tasks.c index 2fb4c2f..1534855 100644 --- a/sat/sys/FreeRTOS/tasks.c +++ b/sat/sys/FreeRTOS/tasks.c @@ -1439,7 +1439,7 @@ StackType_t *pxTopOfStack; prvAddTaskToReadyList( pxTCB );

                /* We may have just resumed a higher priority task. */
  • if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
  • if( ( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) && ( xSchedulerRunning != pdFALSE ) ) {
    /* This yield may not cause the task just resumed to run, but will leave the lists in the correct state for the ~~~~

Does this workaround look reasonable to you guys ? Should I create a ticket about this issue ?

Cheers, -Olivier.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by heinbali01 on September 27, 2015

Hi Olivier,

Here below you find another way to create a task which will only start running when it is said to do so.

It is a fragment from one of the examples in http://freertos.org/tcp It uses the couple xTaskNotifyGive() / ulTaskNotifyTake() to keep a task in a 'suspended' state.

~~~~~

/* Handle of the task that runs the FTP and HTTP servers. */ static TaskHandle_t xServerWorkTaskHandle = NULL;

void main() { /* Create the task that handles the FTP and HTTP servers. This will initialise the file system then wait for a notification from the network event hook before creating the servers. The task is created at the idle priority, and sets itself to mainTCPSERVERTASKPRIORITY after the file system has initialised. */ xTaskCreate( prvServerWorkTask, "SvrWork", mainTCPSERVERSTACKSIZE, NULL, tskIDLE_PRIORITY, &xServerWorkTaskHandle );

/* HT : 
prvServerWorkTask() has been created, it will run, but only up until the
ulTaskNotifyTake() statement. */
vTaskStartScheduler();

for( ;; );

}

static void prvServerWorkTask( void *pvParameters ) { xTCPServer *pxTCPServer;

....
/* Wait until the network is up before creating the servers.  The
notification is given from the network event hook. */
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

/* Create the servers defined by the xServerConfiguration array above. */
pxTCPServer = FreeRTOS_CreateTCPServer( xServerConfiguration, sizeof( xServerConfiguration ) / sizeof( xServerConfiguration[ 0 ] ) );

}

/* Called by FreeRTOS+TCP when the network connects or disconnects. Disconnect events are only received if implemented in the MAC driver. / void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent ) { ... / If the network has just come up.../ if( eNetworkEvent == eNetworkUp ) { #if( ( mainCREATEFTPSERVER == 1 ) || ( mainCREATEHTTPSERVER == 1 ) ) { / Let the server work task now it can now create the servers. */ xTaskNotifyGive( xServerWorkTaskHandle ); } #endif } }

~~~~~

A more general remark: FreeRTOS is a Real Time OS, and therefor it is as lean as possible. It hardly contains any code that protects the programmer against making mistakes, except the configASSERT() calls.

I think that no configASSERT() was added to check the schedulerstate because then it should be added to hundreds of API's and situations.

/* Check if the scheduler is running. */
configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING );

/* Check for the scheduler being started. */
configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED );

Your 'patch' would help avoid the problem, but it is not lean enough :-)

I hope the example helps for you.

Regards.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by heinbali01 on September 27, 2015

In my prevoious message I put a wrong URL for the tcp section, it should have been http://freertos.org/tcp of course

Regards, Hein


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by oprs on September 27, 2015

Hello Hein,

Ha, you just hit the nail on the head here; obviously I need to wrap my head against the paradigms of Real Time OS programming. At least should I have considered using binary semaphores, though task notifications seem even more appropriate indeed.

Thanks a lot for your time and for your helpful insights.

Regards, -Olivier.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by heinbali01 on September 27, 2015

Hi Olivier,

Finally some information about the speed of semaphores (queues), event-groups and task-notification.

I created three tasks, running on the highest priority, and all constantly blocked on a call to:

1) xSemaphoreTake();
2) xEventGroupWaitBits();
3) ulTaskNotifyTake();

Then I unblocked each of the tasks and measured how long it took before the task actually became active. I tested on a Cortex M4, 120 MHz.

Timings are in us (micro-seconds):

[oops - sorry to be a party pooper - but we don't like numbers to be quoted so I removed them. Rationale in a separate post]
  • Event-groups can be unblocked from an interrupt, but it's less optimal: it uses a timer-task to actually call xEventGroupSetBits(). The reason for this is that the duration of xEventGroupSetBits() may vary: in the worst case, several tasks are waiting for the event-group and more than one task must be woken-up. You don't want to do this from within an ISR :-)

The fastest and cheapest way to unblock a task is to use xTaskNotifyGive().

Here below some snapshots of the code that I used. I measured time with a TC timer running on 120 MHz.

~~~~~ /* ------------------------------------------------------------------ * Giving from a task: */ xSemaphoreGive( xTestingSemaphore );

/*
 * Giving from an interrupt:
 */
xSemaphoreGiveFromISR( xTestingSemaphore, &xWoken );
portYIELD_FROM_ISR( xWoken );

xResult = xSemaphoreTake( xTestingSemaphore, portMAX_DELAY );

/* ------------------------------------------------------------------
 * Setting bits from a task:
 */
xEventGroupSetBits( xTestingEventGroup, uxBitsToSet );

/*
 * Setting bits from an interrupt:
 */
xEventGroupSetBitsFromISR( xTestingEventGroup, uxBitsToSet, &xWoken );
portYIELD_FROM_ISR( xWoken );

xResult = xEventGroupWaitBits( xTestingEventGroup, uxBitsToWaitFor,
    pdTRUE, pdFALSE, portMAX_DELAY );

/* ------------------------------------------------------------------
 * Notifying bits from a task:
 */
xTaskNotifyGive( xTaskNotifyHandle );

/*
 * Notifying bits from an interrupt:
 */
vTaskNotifyGiveFromISR( xTaskNotifyHandle, &xWoken );
portYIELD_FROM_ISR( xWoken );

ulResult = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

~~~~~

Regards.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by rtel on September 27, 2015

...going back to your original post:

calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Yes, that is not surprising - calling any function that attempts to switch to a task before the scheduler is running will crash because.....the scheduler is not running. As a general rule of thumb don't call any API function other than ones that create objects (tasks, semaphores, etc.) before the schedular has been started. There are certain exceptions, notable vTaskSuspend(), because it is common to want to start a task in the suspended state, but the sequence:

vTaskSuspend( t1 ); vTaskResume( t1 ); // <- crash vTaskStartScheduler();

Is not a sequence of code that would normally exist as it does not achieve anything - suspending a task then resuming it again before any scheduling has happened. Either suspend it then resume it after the scheduler has started (which is a common thing to do to get start up sequences right), or don't suspend it in the first place.

I think you have worked this through with Hein's help - but sometimes it is helpful when posting questions to mention what it is you are actually trying to acheive, then we can suggest alternatives.

Regards.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by oprs on September 27, 2015

Hi rtel,

My motivation was to present a minimal example, stripped out of all context, in order to trigger the issue (with no or little concern on how ugly/stupid the code may end up looking like). Sorry if I didn't make my point clear about that.

The issue I'm raising is the following:

This sequence does induce a crash:

~~~~ (void)xTaskCreate( task, "task1", 128, NULL, 1, &t1 ); (void)xTaskCreate( task, "task2", 128, NULL, 1, &t2 );

vTaskSuspend( t1 ); vTaskResume( t1 ); ~~~~

This one does not:

~~~~ (void)xTaskCreate( task, "task1", 128, NULL, 1, &t1 );

vTaskSuspend( t1 ); vTaskResume( t1 ); ~~~~

Nor does this one (note the priority change on t2):

~~~~ (void)xTaskCreate( task, "task1", 128, NULL, 1, &t1 ); (void)xTaskCreate( task, "task2", 128, NULL, 2, &t2 );

vTaskSuspend( t1 ); vTaskResume( t1 ); ~~~~

Thus, the crash occurs where vTaskResume(t1) is called under the following conditions:

  • the scheduler hasn't started yet,
  • t1 is in xSuspendedTaskList,
  • t2 is in pxReadyTasksLists[n], with n <= t1's uxPriority

Furthermore, I was able to observe that the crash only occurs after control is given to t1. In my original example (top post), t1 calls vTaskDelay() in a loop; unsurprisingly, this is where it all goes wrong.

Perhaps this is just me splitting hairs here. As you said, this sequence does not achieve anything anyway.


A quick word on the context since it might be relevant to the discussion.

My application (micro-satellite) involves running several tasks, each in charge of one aspect of the mission: power monitoring, ground communication, attitude control, etc... Which tasks are active at one given time depends on the mode the satellite is in. The mission plan can be expressed in terms of a directed graph, with each node representing a mode. Transitions between modes are triggered in response to external events (sensor readouts, commands from the ground, etc...).

Also running along those tasks are (higher priority) driver tasks, essentially implementing a one-to-one threaded model; I won't bother you with this since I don't believe it adds much to the discussion.

The first code sequence above pretty much illustrates all that. Say t1 and t2 are applicative tasks (initially suspended). The dimwitted suspend/resume sequence that prompted my first post occurs during the setting up of the tasks associated to the initial state of my mission graph.

Of course there are a million ways around this, the most obvious to me being to create a parent task (think "init" in unix terms) and to manage everything from there. No more fiddling with task management before the scheduler is started -> problem gone.

My initial point was just about what I thought was (and still think is ;-) a slight inconsistency in how FreeRTOS deals with vTaskSuspend()/vTaskResume() before control is passed to the scheduler (admittedly very marginal case).

Thanks a lot for your input (to yourself and to Hein). I have to say that getting a glimpse of the internals of FreeRTOS only increased my confidence in it.

Regards, -Olivier.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by oprs on September 27, 2015

Hello Hein,

These are interesting figures indeed, not to mention that task notifications seem to be much easier on memory than binary semaphores and event groups. In fact, task notifications are free (memory-wise) since they're part of the TCB (assuming configUSETASKNOTIFICATIONS is defined). Great alternative to semaphores in many cases.

Actually, that's a feature I've been kind of overlooking up until now, but I'll probably be using it soon for notifications from ISRs, as per this example (I'm still using binary semaphores at the moment).

Thanks for mentioning those :-)

Regards, -Olivier.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by rtel on September 27, 2015

Sorry people - I don't mean to be a stick in the mud - but the numbers have been removed. Quoting numbers has led to many problems in the past as they are open to deliberate mis-interpretation and mis-information by third parties, which in turn leads to bad feeling in the community.

There are so many variables effecting timing that people will get different results, so the advice is just to measure it yourself. For example, if configASSERT() is defined (and how it is defined), what the optimisation level was, the FreeRTOSConfig.h settings were, the version of the compiler was used, the trace macros used, whether stack overflow detection was on, etc. will all effect the measurements.

Regards.


calling vTaskResume() before the scheduler is started may lead to a call to portYIELD()

Posted by heinbali01 on September 28, 2015

@Richard,

so the advice is just to measure it yourself. For example, if configASSERT() is defined (and how it is defined), what the optimisation level was, the FreeRTOSConfig.h settings were

Sorry about that but you're 100% right. Especially these defines influence the latencies, as I just saw:

~~~~~ configUSETRACEFACILITY configCHECKFORSTACKOVERFLOW configASSERT() / USEFULLASSERT configGENERATERUNTIMESTATS ~~~~~

The reason that I'm interested in the latency between Give() and Take() is my desire to keep ISR's as short as possible. Ideally an ISR will just clear a flag and signal some task.

@Olivier :

A quick word on the context...

That sounds like some great project.

Of course there are a million ways around this

I often realise that when reading posts on this forum, there are many good ways to reach a target.

... the most obvious to me being to create a parent task (think "init" in unix terms) and to manage everything from there. No more fiddling with task management before the scheduler is started -> problem gone.

I'm much on the same line: avoid the problem by starting-up the scheduler as soon as possible. What I also like is to be able to have a task sleep (block) while initialising hardware and use that CPU-time for other useful things. The aim is often the same: short boot- and response-times.

Regards.


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2016 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd.. See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.

Latest News:

FreeRTOS V9.0.0 is now available for download.


Free TCP/IP and file system demos for the RTOS


Sponsored Links

⇓ Now With No Code Size Limit! ⇓
⇑ Free Download Without Registering ⇑


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Renesas Electronics Gold Alliance RTOS Partner.jpg

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Atmel RTOS partner supporting ARM Cortex-M3 and AVR32 microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Xilinx Microblaze and Zynq partner

Silicon Labs low power RTOS partner

Altera RTOS partner for Nios II and Cortex-A9 SoC

Freescale Alliance RTOS Member supporting ARM and ColdFire microcontrollers

Infineon ARM Cortex-M microcontrollers

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

Cypress RTOS partner supporting ARM Cortex-M3

Fujitsu RTOS partner supporting ARM Cortex-M3 and FM3

Microsemi (previously Actel) RTOS partner supporting ARM Cortex-M3

Atollic Partner

IAR Partner

Keil ARM Partner

Embedded Artists