Main task blocks others

Discussion of issues related specifically to writing code for native mode devices. This includes ZBasic code as well as assembly language code and C code, both inline and standalone.
Post Reply
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Main task blocks others

Post by wwwoholic »

I've seen this strange behaviour several times and always attributed it to errors in my code. Now I am beginning to think there is a problem with task switching.

My program requires PID for actuators and physics calculations for acceleration curves. Both are very sensitive to timing, especially differential part of PID. Here is how it is implemented:

- Main task executes in a loop calling some logic and I/O procedures. It prepares sensor readings in global variables and sends serial commands using values from other global variables.
- Additional WaitForInterval task takes sensor readings from main task, calculates PID and curves and puts results into global variables.

Now, unless I put sleep() into main loop the additional task becomes erratic and sometimes fails to execute at all.

In another program I had main task with loop and two additional tasks. This did not work at all untill I put sleep() into all tasks including main. However in main loop there was a branch activating when a button is pressed and I forgot to put sleep() into that branch. Every time I pressed button the other two tasks simply stopped.

Edit: forgot to mention, this is running on ZX24n
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Main task blocks others

Post by dkinzer »

wwwoholic wrote:Now I am beginning to think there is a problem with task switching.
I'm not sure I completely understand the circumstances that lead to the problem. Can you create a minimal test case that reproduces the issue?
- Don Kinzer
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

I don't have working device at the moment to check the example, but here is a very simplified snippet of the code that failed last time:

Code: Select all

public sub Main()
   call SetInterval(CURVE_PERIOD)
   CallTask MotorTask, MotorStack
   do
      input = GetADC(pin)
      call PutQueueByte(txQueue, output)
      call sleep(MAIN_PERIOD)
   loop
end sub

public sub MotorTask()
   do
      call WaitForInterval(0)
      call LockTask()
      output = ProcessCurves(input)
      call UnlockTask()
   loop
end sub
In the code above if I remove "call sleep(MAIN_PERIOD)" from the main loop then it often gets stuck sending the same output over and over, as if MotorTask() never called.
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

How fast do you expect the task to actually see a new value to report back to the main task?

The Main task will loop very fast, and there may not have been time for the task to report a new value before the main task has sent a LOT of messages withthe same value.

Alternatively, there is a stack problem. Either too small a task stack, or a stack overrun from too much RAM utilization.

-Tony
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

I think main task spends most of the time waiting for the queue. This actually might explain the behaviour if queue blocking somehow prevents other tasks from running.

I don't think this is stack problem since program works just fine with sleep(). Besides, in my estimate stack is about 50 bytes larger than necessary for the longest invocation chain.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

wwwoholic wrote:This actually might explain the behaviour if queue blocking somehow prevents other tasks from running.
It doesn't as can be shown with the simple test program below. In this test case, the main task floods a queue with data. The queue can either be the transmit queue for Com1 or an unserviced queue. The task "task1" simply toggles a pin as an indication that it is running. If you compile and run this program you'll find that the pin toggles irrespective of which queue is used proving that queue blocking in one task, whether temporary or permanent, does not prevent another task from running.

So far, I have not been able to construct a test case that behaves as you have described. If you can devise one, that would be most helpful.

Code: Select all

Const taskPin as Byte = A.7
Dim ts1(1 to 100) as Byte

#if 0
    ' define the queue as the Com1 transmit queue
    Dim oq() as Byte Based Register.TxQueue
#else
    ' define an unserviced queue
    Dim oq(1 to 20) as Byte
#endif

Dim i as Byte
Sub Main()
    # set the target pin to be an output
    Call PutPin(taskPin, 1)

    ' invoke the alternate task
    CallTask task1, ts1

    ' This loop floods the queue with data, faster than
    ' it can be removed (if at all).
    Do
        If &#40;i < 10&#41; Then
            Call PutQueueByte&#40;oq, i + &H41&#41;
            i = i + 1
        Else
            Call PutQueueByte&#40;oq, &H0d&#41;
            Call PutQueueByte&#40;oq, &H0a&#41;
            i = 0
        End If
    Loop
End Sub

Sub task1&#40;&#41;
    Do
        ' toggle a pin to indicate that this task is running
        Register.Pin&#40;taskPin&#41; = PortMask&#40;taskPin&#41;
    Loop
End Sub
- Don Kinzer
Post Reply