|
|
| Author |
Message |
stevech
Joined: 23 Feb 2006
Posts: 656
|
|
Posted: 05 November 2006, 1:55 AM Post subject: Task switching problem example #1 |
|
|
Don or other guru...
I have a 1000 line program that uses ZBasic tasks in a state machine. A finite state machine is in one state at a time, of course. So for any instance of the state machine, there's one ZBasic task handling that state, and deciding when to change states. To change states, the task creates a new task which is a different ZBasic "sub()", then the current state terminates itself as a task.
To simplify this, I'm using task stacks that are obtained at run-time via system.alloc().
The code, below, demonstrates either a logic flaw that I just cannot see, or something about ZBasic's scheduler than I misunderstand, or there's a bug. The code, here, is the simplified case from my larger program.
If you run this (latest compiler and VM) as shown, the correct display on the console port appears - three tasks running in sequence.
If you comment-out the "sleep(0)" near the last line of code, it does not run correctly. I don't think the sleep(0) should be necessary, i.e., it is masking the root error.
| Code: |
option targetCPU ZX24
dim task1Stack(100) as byte
dim taskno as byte
Sub Main()
dim x as byte
console.writeline("Main START")
sleep(1.0)
calltask task1(0), System.Alloc(sizeof(task1stack))
x = -1
do
sleep(0)
if (taskno <> x) then
x = taskno
console.write(cstr(x))
end if
loop
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
sub task1(byval priorTaskSP as unsignedInteger)
call releaseTaskStackMemory(priorTaskSP)
taskno = 1
calltask task2(Register.TaskCurrent), System.Alloc(sizeof(task1stack))
end sub ' implied exit task here
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
sub task2(byval priorTaskSP as unsignedInteger)
call releaseTaskStackMemory(priorTaskSP)
taskno = 2
calltask task3(Register.TaskCurrent), System.Alloc(sizeof(task1stack))
end sub ' implied exit task here
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
sub task3(byval priorTaskSP as unsignedInteger)
call releaseTaskStackMemory(priorTaskSP)
taskno = 3
calltask task1(Register.TaskCurrent), System.Alloc(sizeof(task1stack))
end sub ' implied exit task here
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
sub releaseTaskStackMemory(byval addr as unsignedInteger)
if (addr <> 0) then
system.free(addr)
end if
''''console.write("T" & cstr(taskno))
sleep(0)
end sub
|
Last edited by stevech on 05 November 2006, 2:54 AM; edited 1 time in total |
|
| Back to top |
|
 |
stevech
Joined: 23 Feb 2006
Posts: 656
|
|
Posted: 05 November 2006, 2:52 AM Post subject: |
|
|
I edited the code above, a couple of hours after first posting.
changed the callTask() inside Main(). |
|
| Back to top |
|
 |
mikep
Joined: 24 Sep 2005
Posts: 765
Location: Austin, TX
|
|
Posted: 05 November 2006, 2:57 AM Post subject: |
|
|
| stevech wrote: | I edited the code above, a couple of hours after first posting.
changed the callTask() inside Main(). | Yes that makes more sense. I'm experimenting with your code right now and will post soon.
Last edited by mikep on 05 November 2006, 5:12 AM; edited 1 time in total |
|
| Back to top |
|
 |
stevech
Joined: 23 Feb 2006
Posts: 656
|
|
Posted: 05 November 2006, 3:06 AM Post subject: |
|
|
| Perhaps BasicX set the precendent for the keyword "CallTask". Since the task cannot "return" to the task that invoked it, the keyword might better be named "forkTask" or some such. |
|
| Back to top |
|
 |
mikep
Joined: 24 Sep 2005
Posts: 765
Location: Austin, TX
|
|
Posted: 05 November 2006, 3:28 AM Post subject: |
|
|
I have seen previous references to your task-based state machine but decided not to ask you further. However you have now piqued my interest. Why do you need each state to be a separate task - this seems unwieldy, slow and redundant. If a state needs a task then roll off the task, wait for it to end or kill it and then change to the next state. You can kick off a separate discussion thread on design approaches to your problem or we can discuss offline.
I'm getting different results depending on which chip I'm using (mega32 or mega644) and what code I have instead my console.writes. All of this points to timing issues between tasks. I believe the basic problem seems to be that you are deallocating the memory for the task before the task has completely finished. After the caltask is called there is still one more statement that ends the task and frees up the internal VM task header. Without the sleep (or even the debug.output), the task never actually ends and quite possibly the System.Free() also fails. Either the VM runs out of memory or task headers/RAM/SPs are getting overwritten causing a freeze.
As suggested above, use one task for the state machine and then decide within each state subroutine if a separate task is needed. When that task "ends/terminates" then the state is finished and the state machine can choose the next state. You can always use a program memory data array to determine the size of the stack for each state.
As a further addition if ZBasic supported compiler-built calltables, you can then have a generic state machine that is completely table driven.
Last edited by mikep on 05 November 2006, 5:12 AM; edited 1 time in total |
|
| Back to top |
|
 |
stevech
Joined: 23 Feb 2006
Posts: 656
|
|
Posted: 05 November 2006, 4:02 AM Post subject: |
|
|
Thanks for consorting.
I suspected that I'm deallocating before the task fully termnates, i.e., something within the VM has to run. I had asked Don about this previously.
I know - state machines are often done as a function pointer (state) or a switch statement. There are no function/sub pointers in ZBasic (yet). As an equivalent, and as an academic curiosity of how far one can take ZBasic's quasi-RTOS, on a $5 chip, I wanted to do this task switching state machine.
So I'll adjust the code based on this dialog.
That the timing is chip-dependent is curious too. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR
|
|
Posted: 05 November 2006, 4:35 AM Post subject: |
|
|
I'm out of town currently and will be travelling on Sunday. It'll likely be Monday before I can study this issue in detail.
Without contemplating the issue much, I would suggest that a task should not attempt to free it own task stack. As long as the task is running, it needs the task stack. A task will terminate when it executes the "RET" at then end of its task Main() or if it is forcibly terminated via ExitTask(). |
|
| Back to top |
|
 |
stevech
Joined: 23 Feb 2006
Posts: 656
|
|
Posted: 05 November 2006, 5:44 AM Post subject: |
|
|
Don - no need to spend time on this.
And - the code I'm using has task B freeing the stack memory for task A, given A created B and then A exited. It's just a question of when the exit processing within the VM is actually complete.
with a sleep(0) prior to system.free(), the code seems reliable so far. |
|
| Back to top |
|
 |
|