Bug or strange behaviour in TaskResume ?

markuskrug wrote on Friday, November 18, 2011:

Hello,

First my Example Application. Both Tasks have the same priority. No other Tasks are existing. The Task vTestTask1 gets started by the scheduler first. ‘a’ is a global variable just to have a dummy job for the tasks.

static portTASK_FUNCTION( vTest1Task, pvParameters )
{
/* The parameters are not used. */
( void ) pvParameters;
    for(;:wink: {
   
      vTaskSuspend(NULL);
       a++;
      vTaskResume(xTest2_TaskHandle);
      vTaskSuspend(NULL);
    }
}

static portTASK_FUNCTION( vTest2Task, pvParameters )
{
/* The parameters are not used. */
( void ) pvParameters;

       for(;:wink: {
          vTaskResume(xTest1_TaskHandle);
          vTaskSuspend(NULL);
          a++;
           }
}

The idea ist that the two tasks suspend themself just after resuming the other. Therefore they should run somhow in a ping-pong matter and increment a. However the application does not behave like this. I discovered that the call of TaskResume(…) does not increase the value of xSuspendedTaskList.uxNumberOfItems. Therefore after the second call of TaskSuspend(NULL) the number of suspended Tasks is 2. Additionally does the value of pxReadyTasksLists.uxNumberOfItems decrease by each call of TaskSuspend, but does not increase by calling TaskResume().
Therefore my application ends in the idle Task very soon.
Can anyone explain my problem ? I read in this forum that there have been some bugs with TaskSuspend in the past. Is this another one ?

Best Regards
Markus

markuskrug wrote on Friday, November 18, 2011:

An additional information. I just upgraded to v7.0.2 without any change to my described behaviour.

rtel wrote on Friday, November 18, 2011:

Scenario 1:

1) vTest2Task runs first, attempts to unsuspend vTest1Task (which is not suspended), then suspends itself.
2) vTest1Task runs for the first time.  It suspends itself.

Now both task are suspended.

Scenario 2:

1) vTest1Task runs first, and suspends itself.
2) vTest2Task runs.  It resumes task 1.
3) vTest1Task runs and increments a, before suspending itself again.
4) vTest2Task runs again, the last line it executed was resuming vTestTask1, the next line it executes it suspends itself.

Now both tasks are suspended.

There are probably more complex scenarios where this deadlock can occur too, where preemptive context switches happen at various times, but basically I think you have a test where the correct behaviour would be for both tasks to end up in the suspended state.

Regards.

markuskrug wrote on Friday, November 18, 2011:

Hi Richard,

thanks for you quick response. The Scenario 2 is true due to the calling order of TaskCreaste.
Step 1 and  Step 2 I can confirm. However Step 3 is different. vTest1Task does never start again although it is resumed (that’s why I pointed out the the xSuspendedTaskList.uxNumberOfItems is not changed after calling TaskResume(task1)). I double checked this by looking at the varialble a that is not incremented at all. The variable a is a global static variable. So it is really allocated by the compiler in the memory. Step 4 I can obvious confirm.

Best Regards
Markus

rtel wrote on Friday, November 18, 2011:

However Step 3 is different. vTest1Task does never start again although it is resumed

How are you coming to that conclusion? 

It is possible that, if you are stepping through the code in vTaskResume(), and you step over the line portYIELD_WITHIN_API(), then the yield interrupt and the other task have both executed without you knowing it before you break on the next line in the debugger, as the next implied break point (the line after portYIELD_WITHIN_API()) will get hit first by vTestTask2.

Regards.

markuskrug wrote on Friday, November 18, 2011:

Hi Richard,

I am aware that between brakepoints on succesive code-lines in multitasking enviroment some other code can be executed. However, I come to that conclusion because regardless if I step through the code or if I run it with full speed and stop it after a while the variable a in the memory is never increased. By the way it doesn’t also make any difference if I use  the Codewarrior Simulator (I am using a HCS12 board) or the real hardware.

Best Regards
Markus

rtel wrote on Friday, November 18, 2011:

I will have to set up the exact same scenario to see what I observe.  I will report back when this is done, but it won’t be immediately.

Regards.

markuskrug wrote on Friday, November 18, 2011:

Hi Richard,

thanks for offering you help.
I played around a little bit around by adding a third task to have a better insight what happens during calling TaskSuspend and TaskResume. The third task will never be suspended, has low priority and is just calling TaskResume for the previous suspended Task1+Task2.
For me it comes down to the following thing I am observing. Calling TaskSuspend updates in the different parts of the Kernel data structures comprehensible by decreasing the counters of the ready task and increasing the counters for the suspended tasks. However if I call TaskResume no changes to this counters seems to happen. Is this a correct behaviour ?

Best Regards
Markus

richard_damon wrote on Friday, November 18, 2011:

Part of the problem is that the definition of Resume is to be a hop if the task is already running, so a suspend-resume is different than a resume-suspend (if the task is currently Ready. This means that your program has a race condition is the suspend and resume for a task are coming from different tasks, and they don’t have anything to make sure that they happen in the right order.

Better to use a semaphore given by one task and taken by the other to do the suspension, as it doesn’t matter if the give happens before of after the take, you just one proceed from the take until both happen.

Even better (unless this is just a learning exercise) if you have two operations at the same priority, that are to alternate operations, just put them in one task.and save resources.

rtel wrote on Friday, November 18, 2011:

Ok, I have run this and looked at it in detail, and cannot see anything wrong with the kernel, but can with your test code.

Before showing you the scenario I observed, an explanation on the behaviour:  When a task is removed from the suspended list, it is added to the end of the ready list for tasks of that priority.  This is so it is the last task, of that priority, that is selected to run.  It has to be this way to prevent a task from continuously being selected to run.  That means, the scenario 2 I described above was not quite correct.  When there are only two tasks of a given priority, and a start condition that has one task in the Running state and one task in the Suspended state, then the running task resuming the suspended task will not result in the resumed task running immediately.  When it is resumed, it is added to the end of what was a list of 1 task, and the following yield therefore re-selects the task that was already Running.  Below is the corrected scenario 2, which is what I observed, and at all times the number of items in each list was shown as I would expect:

1) Task 1 runs and suspends itself.

2) Task 2 runs and resumed Task 1.  It continues running though, and suspends itself.

3) Task 1 is now the only task that can run, so runs, resumes task 2, carries on running, and suspends itself.

4) Task 2 is now the only task that can run, so runs, resumes task 1, carries on running, and suspends itself.

5) Task 1 runs, once again, loops back to the top of its for( ;; ) loop, and suspends itself without ever resuming Task 2.

Now both tasks are in the suspended state, although ‘a’ has been incremented (once by each task I think).

I think this is quite simply an error in the logic of your code.

Regards.