Multiple Task Synronisation

tabulous2011 wrote on Monday, November 19, 2012:

What is the best way to achieve this ?

Say i have 5 tasks, task 2,3,4,5  should not run until task 1 as completed.

jdurand wrote on Monday, November 19, 2012:

A couple of ways…

You could create the new tasks inside task 1 when it’s ready.

Something I do (left over from pre-FreeRTOS days) is I have an INT defined globally that’s full of flags.  You could have a Task 1 Done flag that they wait for.

at the start of each of the other tasks (or after the task does some other initialization)…

do {
} while(Flags & Flag_Task1_Busy);

richard_damon wrote on Tuesday, November 20, 2012:

I would NOT use the flags and vTaskDelay loop for this. The biggest issue is that the task doing this will NOT start up as soon at the flag is raised, but will need to wait for the next tick interrupt.

For the original problem, if it REALLY means that task 1 has FINISHED, and isn’t going to run again, then create the tasks in task 1.  If, more likely, it means that tasks 2-5 need to wait for task 1 to finish its processing for a loop before they have the information that they need, then I would define a semaphore (or a queue) for each of the tasks 2-5, and task 1 would signal each task as the data that task needs is available.

jdurand wrote on Tuesday, November 20, 2012:

I did say there were more than one way to do it.

Something to note, if the tasks being started are equal or higher priority than TASK-1, then using semaphores or creating the new tasks will switch to TASK-2 as soon as the semaphore is sent or the task created even if TASK-3 to whatever are higher priority.  By using a single flag like I often do (I also use semaphores), none of the tasks get the OK to run until the next tick and they will start in the proper priority sequence.

I often use this on startup.  Each task does its initialization and then parks, waiting for the flag.  One main task does whatever initialization it needs (ex:  current project puts serial number, version, etc. on the LCD for a few seconds before telling everything else to GO).

richard_damon wrote on Tuesday, November 20, 2012:

The biggest issue I have with the delay/poll loop is that it tends to artificially make you up the tick rate.  Unless you really have operations that work on a periodic basis, and not in response to something periodic in hardware (for instance, I tend to set up my A/D converter to run off a hardware timer to start sampling to make the sampling truly accurate in sampling period and then work off of the completion interrupt), the tick rate only needs to be as fast as the precision needed in timeouts or blink rate of displays. This can easily be 50-100ms in length, which is likely much longer than you want to wait for these sorts of poll loops. it is loops like that that cause the question of how do a set the period less than a millisecond.

Setting multiple semaphores does require a bit of discipline in needing to signal the tasks from highest to lowest priority, or temporarily raising your priority to super high to signal everyone, and then dropping priority. It also lets the tasks start at once, and with no wasted processor time.

With your method, not only do you need to raise the tick rate, but then every tick you cause a lot of tasks to wake up just to decide that they don’t have anything to do and go back asleep, causing a lot of wasted processor time.

jdurand wrote on Tuesday, November 20, 2012:

Since I only do this to delay starting tasks, I don’t worry about the tick rate at all.    Not EVERYTHING has to happen with microsecond response time.  Humans won’t notice a 10mS delay in startup or mode change.

Once tasks are running, I either use semaphores (often from inside ISRs) or use the flags for very low response items like change system overall mode (standby/run master/run slave/shutdown).  In the case of things like mode change, I only poll them in my main outer loop and when I hit a flag, I often use something like 100mS polls until the flag changes back.

tabulous2011 wrote on Wednesday, November 21, 2012:

thanks guys for the comments…………makes me think should freertos support something like OS Event Flags ?

I agreed that this could be done with semaphores or a Queue. But thinking about my application (GSM modem control) where
task 2 = SMS
task 3 = FTP
task 4 = HTTP
task5 = SMTP

No of theses tasks can run until task 1 as configured and setup the modem. Also i need to be able to should the modem need to be reset, halt tasks 2,3,4,5 and restart them.

So from this view i’m thinking using a queue and state machine in each task to implement  this. Only thing i dont like is the fact that task 1 has to send individual messages to each task.

Is it possible to have a common queue that task 2,3,4,5 listern to ?

tabulous2011 wrote on Wednesday, November 28, 2012:

I’ll ask again seen as nobody has answered this.

Is it possible to have a common queue that task 2,3,4,5 listern to ?

travfrog wrote on Wednesday, November 28, 2012:

May I ask a little more about your architecture? I I read this correctly, you have a startup task that initializes things and once it has completed, gets the other tasks running.

Why is that first ‘task’ a task and not an initialization sequence executed before the scheduler is started? Does it continue to have run time requirements while the other tasks are running or is it truly mutually exclusive?

I don’t think you can have a single Q upon which all tasks pend. Q’s are by their nature applicable to the owning task only (from a blocking perspective) - though I suppose if you want to you could make the Q handle global and then anyone could use Peek to see what’s in it but then you have to have a periodic checking mechanism which defeats the purpose. A task should really only wait on a semaphore or a message in it’s Q depending on what approach you take.

If your first task needs to be an actual task then I can see no other way to signal the other tasks to run other than a separate message to each task. The message approach has the advantage of executing immediately and not waiting on the next RTOS tick.

Another question. Is the order of execution of the other tasks important? A multiple message approach could allow you to control that sequence.

What are the task priorities of the other tasks - are they all the same?

richard_damon wrote on Thursday, November 29, 2012:

You ask “Is it possible to have a common queue that task 2,3,4,5 listern to?”, and the answer is “Yes” but probably not in the way that you are looking for. If multiple tasks are waiting on a common queue, then when an item is added to the queue, only one of the tasks that are waiting (the highest priority one), will get the item, the rest will wait for another item to come to the queue. Thus you can load share a queue with multiple tasks to process it.

To send a single item to multiple tasks, you need a separate queue per task.

tabulous2011 wrote on Thursday, November 29, 2012:

Thanks gents for the comments i’ll try to expand

// here is my main, all it does is call vTaskInit() and then start the scheduler.

int main(void)
vTaskInit(); /* Start the Init Task */
vTaskStartScheduler(); /* Start the tasks and timer running. */
while( ETERNITY ){}; /* Never get here or we have problem. */

// all this does is create a task to start the initailaisation.

void vTaskInit(void)
xTaskCreate( vInitTask, ( signed char * ) “Init”,
     mainINIT_TASK_PRIORITY, &h_InitTask );

// this init task starts the system init some of the function calls will create new tasks. I.e. vAt_HandlerInit(); etc

portTASK_FUNCTION( vInitTask, pvParameters )
int i;

i = 6;
systemClockMs = 0;

eticket_bsp_LEDInit( LD1A );
eticket_bsp_LEDInit( LD1B );
eticket_bsp_LEDInit( LD2A );
eticket_bsp_LEDInit( LD2B );
eticket_bsp_LEDInit( LDSDC );

eticket_bsp_FETInit( FTSER );
eticket_bsp_FETInit( FTSDC );

eticket_bsp_LEDOff( LD1A );
eticket_bsp_LEDOff( LD1B );
eticket_bsp_LEDOff( LD2A );
eticket_bsp_LEDOff( LD2B );
eticket_bsp_LEDOff( LDSDC );

eticket_bsp_FETOn( FTSDC );
eticket_bsp_FETOn( FTSER );

eticket_bsp_M10Init( M10PWR );
eticket_bsp_M10Init( M10RST );
eticket_bsp_M10Init( M10DTR );
eticket_bsp_INPInit( M10MON );

eticket_bsp_M10low( M10DTR );
eticket_bsp_M10high( M10PWR );
eticket_bsp_M10high( M10RST );

eticket_bsp_EXTIInit( SERIO1 );
eticket_bsp_EXTIInit( SERIO2 );

eticket_bsp_CHRGInit( CHRGCE );
eticket_bsp_CHRGInit( CHRGTE );

eticket_bsp_EXTIInit( PWRGD );
eticket_bsp_INPInit( CHRG1 );
eticket_bsp_INPInit( CHRG2 );
eticket_bsp_INPInit( CHRG3 );

eticket_bsp_CHRG_CEOff( CHRGCE );
eticket_bsp_CHRG_TEOff( CHRGTE );

/* Configure EXTI Line */

/* NVIC configuration */

/* Enable PWR and BKP clocks */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR |
    RCC_APB1Periph_BKP, ENABLE );

/* Configure the PVD Level to 2.8V */

/* Enable the PVD Output */




while (i- > 0)
vTaskDelay( 100 );
eticket_bsp_LEDOn( LD2A );
eticket_bsp_LEDOff( LD2B );

vTaskDelay( 100 );
eticket_bsp_LEDOn( LD2B );
eticket_bsp_LEDOff( LD2A );

eticket_bsp_LEDOff( LD2A );
eticket_bsp_LEDOff( LD2B );

xTaskCreate( vMainTask, ( signed char * ) “Main”,
     mainQUEUE_RECV_TASK_PRIORITY, &h_MainTask );

vTaskDelete( h_InitTask );

Thus my issues around are around the startup and sync of one task to others, where until one task as completed, then others need to hold off from executing.

PS sorry for the ropey code, this is very much still testing and prototype :slight_smile:

richard_damon wrote on Thursday, November 29, 2012:

One comment, while doing all your init code in a task can work, I find it much more convenient to do as much of my initialization pre-startup as possible. The attribute here is that I like to know that any resources needed by interrupts or other tasks are ready before the system comes up, so those routines don’t need a lot of checks to see if the resources have been made yet. Pre-startup is a much simpler environment, as your code really is single threaded, so everything runs in order. Once you turn on the interrupts in vTaskStartScheduler(), you need to think about all the possible (even if very unlikely) orders that things might happen in.

Some init code, may need to be in a task, if it is more complicated and needs, for instance, interrupts to wait for steps or longer time delays. I do NOT  normally create a separate “init” task for this, but place this as code at the beginning of the task that is in charge of this device. This allows multiple tasks that might need to do this to interleave each other getting me up faster, and saves ram. Note that the stack space for your init task is basically wasted, as it is freed after almost everything else has been created. You MIGHT be able to use some of it for dynamic memory allocations during operations, but those are always risky, as you either need to code failure paths for all of them, or go through a lot of analysis to make sure you will always have enough ram, even in the face of possible memory fragmentation.  

tabulous2011 wrote on Thursday, November 29, 2012:

Hi Richard
thanks for your comments, there taken on-board. The reason for having an init task was to enable the use of OS calls like vTaskDelay() in the init sequence. I appreciate that it is slightly wasteful of system resourses, but it enables a nice startup with out any blocking loops etc.

What it does mean it that any other tasks will also start running, now this is not a problem for some task as the will run and then wait on a queue for system messages. But others need to be held off, and this is where i’m having thought issues.

tabulous2011 wrote on Thursday, November 29, 2012:

to add the simplest way to get around this would be to have as jdurand mentioned a global INT for flags, but i then ask myself should this not be the job of the OS ? but as it does support OS Event flags, what other options do i have available. Queues/Semaphores.

I just think something is missing for freertos here, as other OS do support OS Events.

travfrog wrote on Thursday, November 29, 2012:

Why do you need to hold off the other tasks? When I create a task it is entirely message driven so unless there is a message the task does nothing. I create all the tasks first then start the scheduler.  If a task has a periodic requirement, I use a timer interrupt to generate a timer message to the task. You could gate the timer messages to the other tasks until the first task is complete.

mdkendall wrote on Thursday, November 29, 2012:

My understanding is that you have a task responsible for initialising a GSM modem and other tasks that depend on it (FTP, SMS, SMTP, etc.). The modem is initialised at start-up but may go up and down thereafter.

What I would do it this. Have each of the tasks that needs to know the status of the modem send the GSM task a message to register to receive status updates. Have the GSM task keep a list of all tasks that are interested in the modem status. Whenever the modem status changes (it goes up or down) have the GSM task send each of these registered tasks a message giving the new status.

Your service tasks (FTP, SMS, SMPT, etc.) can deal with these messages in the normal way. In particular, at start-up they will register to receive updates then wait to receive a message indicating that the modem is up before proceeding.

travfrog wrote on Friday, November 30, 2012:

How about this idea - haven’t tried it myself.

Have the other tasks all blocked on the same semaphore (they would be blocked as soon as they enter their task functions but before they enter their task loops) and when the first task is complete, the first task can give the semaphore n-times, where n is the number of other tasks you wish to start. If the tasks are the same priority, I think the OS will simply round robin them. I don’t how you define the order of that round robin but I suspect it has something to do with the creation sequence.

richard_damon wrote on Friday, November 30, 2012:

Yes, needing to use interrupt for some of the initialization is one reason to put PART of the initialization into a task. I still create all queues before starting up the task system. I also find very little need for vTaskDelay() in part of an initialization sequence as that is for much slower things than most hardware needs. I am used to hardware needing delays of the microsecond to 10s of microseconds of delay, while vTaskDelay is for things needing more on the order of 10s of milliseconds of delay.

I suppose one exception to this (which might be what you are hitting) is if you are turning on some other device and need to give it time to wake up and be ready for you to talk to it. 

As I said before, I tend to place this code at the beginning of the task “responsible” for that device, as I have found that this sort of device tend to benefit from having a task that takes requests for operations from a queue and processing them. This also means that other tasks that want to access the device are free to send their requests as soon as they want, as they will just be placed in the queue for that task, and processed when the device is ready. If some tasks need to notified that the device is now on line, you can build up a list of them (or more precisely of their queue) and post a notice of this event to their event queues.

tabulous2011 wrote on Friday, November 30, 2012:

Thanks gents, i think i’ll go down the Queue route, and then get the init task to signal to the others its status, abit like what Mkendall as derived.

Sometimes its good to get your thoughts out, as it helps to see clearer :slight_smile: