problems with task switch after queue send!

biker126 wrote on Tuesday, October 02, 2007:

hi all!

I got a problem with task switch after posting a msg into a queue. I have a simple task called "myTask" that executes every 10 OS ticks and posts a msg into a queue:

void myTask( void* pvParameters)
{
    portTickType xLastWakeTime;
    const portTickType xFrequency = 10;
    uint16 ui16_webMessage = 0x00;
   
    xLastWakeTime = xTaskGetTickCount();
       
    for (;:wink:
    {
        // not sure if that’s needed. maybe when debugging?
        if (xLastWakeTime < xTaskGetTickCount())
        {
            xLastWakeTime = xTaskGetTickCount();
        }
        vTaskDelayUntil( &xLastWakeTime, xFrequency );   

        ui16_webMessage = 0xC074;
        xQueueSendToBack(msg2WebTask, (void*) &ui16_webMessage, (portTickType)0);
        numberOfMsgs = uxQueueMessagesWaiting(msg2WebTask);
        portYIELD();
    }
}

the other task is reading from the queue with:
if (xQueueReceive(msg2WebTask, (void*) &ui16_webMessage, (portTickType) configTICK_RATE_HZ / 2))

in the main() I create the queue with: msg2WebTask = xQueueCreate(5, sizeof(uint16));

when I debug the programm, that is set a breakpoint at "portYIELD()", then the programm seems to get "trapped" inside the "myTask" function (maybe thats just an GDB problem?). the "myTask" just filles the queue until its full…

when I set a breakpoint at the line where I receive the msg’s then the queue is already full when I get there for the first time! (initial breakpoint is at start of main, then i jump to the queue-receive and the queue is already full!!!)
so it seems to me that there’s a problem with scheduling after the msg has been posted into the queue.
I’ve set the priority of the receiver task higher (actually highest overall) than the priority of the sending task (2nd highest overall). so if I understand it right the receiver task should get scheduled first (at startup), read the queue, its empty, block on w8ing for the queue.
then the sending task is scheduled, puts a msg into queue, context switch to w8ing receiver task…

but as it seem’s thats not what’s happening…

does anyone got an idea what is "messing up" the scheduling? maybe the sending task aint blocking at the "DelayUntil"? or the receiver task doesnt get scheduled at start? (and thus aint w8ing for the queue?) or anything else? :-/

thanks in advance
D. Holzwarth

sotd wrote on Tuesday, October 02, 2007:

If the receiver is higher priority than the sender then you should not need the portYIELD() call as a task switch will (should) occur within xQueueSendToBack() before the function exits.

I should remove the code that resets the xLastWakeTime variable too.

Neither of these things explains what you are seeing.  Are you sure you got the priority assignments as you say?  Do you have a decent debug setup that allows you to step through the xQueueSendToBack() function?

biker126 wrote on Tuesday, October 02, 2007:

I know that I _shouldnt need_ the portYIELD() - as it should make a task switch anyway. but as it seems it doesn’t so thats why I added the portYIELD(). but it doesn’t seem to help :-/

bout the xLastWakeTime reset:
I wasn’t should if the xTaskDelayUntil() might get “bugged” when I debug the program. actually when you set a breakpoint somewhere, everything (including OS tick) should be stopped so it shouldn’t be a problem. I simply wasn’t sure :wink:

about the debug setup: I haven’t tried to step into the xQueueSendToBack as it is full of kernel code which I dont understand anyway ^^
also I thought the context switch should occur AFTER xQueueSendToBack is finished? (in my opinion the program should jump to some scheduler function after xQueueSendToBack, but instead it just back to the start of the for(;:wink: loop…).

still need some more idea’s pls :-/

sotd wrote on Tuesday, October 02, 2007:

Just a note for clarity.  If you step through the code for a task a context switch can happen without you knowing it.  The debugger will simply place a break on the next execution line within the same task so another task could execute for some time within the single step but all you would see happen is the step onto the next line in the same task.

biker126 wrote on Tuesday, October 02, 2007:

are you sure about that?

it would explain why I get trapped inside "myTask" when I do only single-steps in there :slight_smile:

however, it still doesn’t explain why after the startup the queue is already full when I break at the receiver task for the first time (I do a “run to breakpoint” from main() so it should execute whatever code and eventually break at my breakpoint - for the first time).

as it seems to me the sender task is definitly executing BEFORE the receiver task (when i single step inside the sender task I can see with uxMessagesWaiting() how the queue gets filled…).

rtel wrote on Tuesday, October 02, 2007:

Here is a little test program that should demonstrate what you are trying to do.  Try running this (there maybe some compilation errors as I have just written this without testing it).

-------------
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

xQueueHandle xQueue;
int iTest = 0;

void main( void )
{
    /* Create the queue. */
    xQueue = xQueueCreate( 5, sizeof( unsigned short ) );

    /* Create the sender at a low priority. */
    xTaskCreate( vTxTask, "Tx", configMINIMAL_STACK_SIZE, NULL, 0, NULL );

    /* Create the receiver at a higher priority. */
    xTaskCreate( vRxTask, "Rx", configMINIMAL_STACK_SIZE, NULL, 1, NULL );

    /* Start the scheduler. */
    vTaskStartScheduler();
}

void vRxTask( void *pvParamters )
{
unsigned short us = 0;

    for( ;; )
    {
        /* Block to wait for a message on the queue.  We should unblock
        as soon as a message arrives. */
        xQueueReceive( xQueue, &us, portMAX_DELAY );

        /* Increment the test variable to show that we have unblocked. */
        iTest++;

        /* Loop back around so we block on the queue again.  We will
        therefore increment the test variable once for each time a
        message arrives on the queue.  [the data from the queue is just
        discarded. */
    }
}

void vTxTask( void *pvParameters )
{
unsigned short us = 0;
int iLastTest = 0;

    for( ;; )
    {
        /* Write to the queue, this should wake the Rx task. */
        xQueueSend( xQueue, &us, 0 );

        /* Even though we did not block, the Rx task should have
        executed by the time we get here as it is a higher priority
        and was blocked waiting for data to arrive on the queue.  If
        it executed before we get here then it will have incremented
        the test variable before blocking again. */
        if( iTest != ( iLastTest + 1 )
        {
            /* Error!  Sit here. */
            for( ;; );
        }

        /* Remember the test value now so we can check it has been
        incremented by one the next time around. */
        iLastTest = iTest;
    }
}
------------

The common/demo directory contains a lot of tests like this, but a bit more complex than this very simple example.

Regards.

rtel wrote on Tuesday, October 02, 2007:

Here it is again with indenting:

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

xQueueHandle xQueue;
int iTest = 0;

void main( void )
{
____/* Create the queue. */
____xQueue = xQueueCreate( 5, sizeof( unsigned short ) );

____/* Create the sender at a low priority. */
____xTaskCreate( vTxTask, "Tx", configMINIMAL_STACK_SIZE, NULL, 0, NULL );

____/* Create the receiver at a higher priority. */
____xTaskCreate( vRxTask, "Rx", configMINIMAL_STACK_SIZE, NULL, 1, NULL );

____/* Start the scheduler. */
____vTaskStartScheduler();
}

void vRxTask( void *pvParamters )
{
unsigned short us = 0;

____for( ;; )
____{
________/* Block to wait for a message on the queue.  We should unblock
________as soon as a message arrives. */
________xQueueReceive( xQueue, &us, portMAX_DELAY );

________/* Increment the test variable to show that we have unblocked. */
________iTest++;

________/* Loop back around so we block on the queue again.  We will
________therefore increment the test variable once for each time a
________message arrives on the queue.  [the data from the queue is just
________discarded. */
____}
}

void vTxTask( void *pvParameters )
{
unsigned short us = 0;
int iLastTest = 0;

____for( ;; )
____{
________/* Write to the queue, this should wake the Rx task. */
________xQueueSend( xQueue, &us, 0 );

________/* Even though we did not block, the Rx task should have
________executed by the time we get here as it is a higher priority
________and was blocked waiting for data to arrive on the queue.  If
________it executed before we get here then it will have incremented
________the test variable before blocking again. */
________if( iTest != ( iLastTest + 1 )
________{
____________/* Error!  Sit here. */
____________for( ;; );
________}

________/* Remember the test value now so we can check it has been
________incremented by one the next time around. */
________iLastTest = iTest;
____}
}

biker126 wrote on Tuesday, October 02, 2007:

thank you a lot!

I’ll test that demo and will report the result in here :slight_smile:

biker126 wrote on Wednesday, October 03, 2007:

update:

I found the reason why my original project isn’t working (I’ve also made a little test project similar to richad’s and that one worked properly…).

my receiver task is a modification of the original uIP_Task of the demo project. this task initialises the EMAC at startup. so after startup my receiver task gets scheduled (highest priority), calls EMACinit() and then gets blocked for 100ms (cause after one EMACinit() call the PHY ain’t ready yet, so system needs to wait before another call to EMACinit()).
since the receiver task is now blocked the sender task (2nd highest priority) is scheduled now which starts sending messanged with a 10ms intervall --> 10 messages in 100ms, with the queue storing max. 5 message :slight_smile:

so after those 100ms the receiver task gets scheduled again and the does a xQueueReceive for the first time but the queue is already filled ^^

so what I did is I simply suspended the sender task until the receiver task is done with initialising the EMAC… then it all works :slight_smile: