StatusTask()

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
pjc30943
Posts: 220
Joined: 01 December 2005, 18:45 PM

StatusTask()

Post by pjc30943 »

The documentation mentions that StatusTask(taskStack) returns 0 if the task with stack taskStack is already running, or is ready to run.

Before a task is invoked, however, I still get StatusTask(taskStack)=0 at the very start of a program.

The goal isjust to figure out whether a task has begun or not, and whether to start it or not (if it's already running).

Anyone who has used statusTask(), perhaps you could please clarify why I'm not getting 255, i.e. task terminated or not running?
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Re: StatusTask()

Post by mikep »

pjc30943 wrote: Before a task is invoked, however, I still get StatusTask(taskStack)=0 at the very start of a program.
I think the result is non-determinant if you haven't started a task for a given task stack.

Back to the original question. Why are you trying to determine if a task is running or not? Many of the tasks I have written are started at the beginning of the app and have an infinite loop. There is no need to determine if the task was started or not. Perhaps you can explain some more on what you are trying to achieve (rather than the how)?
Mike Perks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: StatusTask()

Post by dkinzer »

pjc30943 wrote:[P]erhaps you could please clarify why I'm not getting 255, i.e. task terminated or not running?
The documentation for StatusTask() gives the reason why: "If this function is invoked using an array other than one that is or was being used for a task stack the result is undefined." The reason for this is because prior to invoking a task using a given task stack, the content of the task stack is undefined.

If you want StatusTask() to return 255 when given a yet-to-be-used task stack you can initialize the task stack to the value &Hff.

Code: Select all

Dim ts(1 to 50) as Byte
Call MemSet(ts.DataAddress, SizeOf(ts), &Hff)
An alternate strategy is to use TaskIsValid() to determine if a task stack is in use.
- Don Kinzer
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Re: StatusTask()

Post by Don_Kirby »

mikep wrote:Why are you trying to determine if a task is running or not?
There are quite a few instances where one may need to determine if a particular task is running. I often use non looping tasks to replace a subroutine if I need to return to the caller immediately while the task performs some lengthy operation. In that case, I may need to check if the task is running before calling it to avoid having multiple concurrent instances of the same task. I could also use a semaphore or other means to flag that the task is currently running, by why bother if the ability to do the same is available without declaring another variable?

Another instance isl to determine if a particular task is running is when there are tasks that are dependant on other tasks. If one task fails to [insert failure mode here] for whatever reason, the other task(s) can take the appropriate action (or inaction as the case may be).

I agree, it's not very common to use tasks in this manner, but I've found that it can be quite useful on a number of occasions.

-Don
pjc30943
Posts: 220
Joined: 01 December 2005, 18:45 PM

Post by pjc30943 »

Thanks guys. TaskIsValid() works well.

As Don Kirby mentioned, the first function may invoke a task based on certain inputs; a second function can be made more self-contained if it has means for determining whether it needs to invoke this same task upon another input to second function, or do nothing if the task is already running.
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Re: StatusTask()

Post by mikep »

Don_Kirby wrote:There are quite a few instances where one may need to determine if a particular task is running. I often use non looping tasks to replace a subroutine if I need to return to the caller immediately while the task performs some lengthy operation. In that case, I may need to check if the task is running before calling it to avoid having multiple concurrent instances of the same task. I could also use a semaphore or other means to flag that the task is currently running, by why bother if the ability to do the same is available without declaring another variable?

Another instance is to determine if a particular task is running is when there are tasks that are dependant on other tasks. If one task fails to [insert failure mode here] for whatever reason, the other task(s) can take the appropriate action (or inaction as the case may be).

I agree, it's not very common to use tasks in this manner, but I've found that it can be quite useful on a number of occasions.
I have some alternatives to suggest that you might want to think about.

Idea 1
Rather than invoke a task each time some calculation or operation is needed, use a looping task with an input work queue. New work is simply added to the queue and the task idles if there is no work to be done. This is a simple "producer-consumer" pattern that separates out the function. The consumer is in a separate module with its own private data and a public routine to add work to the queue. Getting the results from the task could be a simple public query (e.g. GetDistance()) or there could be an output queue. Note that the queue(s) does not need to be large as addresses of heap memory can be used instead of queuing up large data. The advantage of this approach is that the overhead of starting and stopping a task each time is eliminated and encapsulation of work requests into a single routine. This idea also facilitates requesting background work where the result is not needed until later.

Idea 2
As a variation of (1), use a public method in the task module to place work on the queue for processing. This method can determine if the task is already busy (using a "isbusy" flag) and discard or keep the work as appropriate. The advantage here is encapsulation of the function of adding work and the ability to ensure that work is queued in an orderly manner. This avoids having replicated code everywhere that tests some condition before invoking a task.

Idea 3
If you think you need multiple tasks for different work, this could be collapsed into a single task that takes a "command" indicator to decide what to do. Again using idea (1), everything can be nicely encapsulated using multiple work request routines and the actual implementation that this is all done by a single task is hidden.

Idea 4
Don't end a task because it "failed" for some reason. This can lead to not very robust code and checks everywhere to make sure the task is running. It is better to log the failure somewhere, perform recovery within the task and then continue execution with the next work item.

Why the stress on encapsulation and modules - because it makes the code easier to understand, maintain and of course less buggy and easier to debug when something does go wrong.
Mike Perks
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

Interesting approach Mike. I can see where those techniques would make for a cleaner implementation.

Most of the time, when I use a non-looping task, the reason is not nearly so complex though. For example, I have a buzzer output that needs to go active high for 0.05 seconds when requested by task A. In order to not hold up task A (it loops very fast), the buzzer timing and pin logic is put into non-looping task B. There is only one caller, so the level of encapsulation required is low in regard to avoiding those pesky little bugs that sneak in when there are many pieces of code using the same variables.

On the other hand, your comments about code readability and robustness are quite valid, and the methods you propose are probably better code writing habits to get into.

I'm working on changing some of my code now to see how your methods look and function in an actual application. I think that I'll end up with the same functionality, but perhaps more reusable/robust code.


-Don
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

Don_Kirby wrote:I'm working on changing some of my code now to see how your methods look and function in an actual application. I think that I'll end up with the same functionality, but perhaps more reusable/robust code.
I'll be interested in hearing about your results. Your conclusion is exactly on target. I don't really need to convince the professional programmers out there like yourself - you just need a little prod now and again :). This discussion is mostly to help less experienced people write better code.
Mike Perks
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

mikep wrote:I don't really need to convince the professional programmers out there like yourself[...]
You might be pushing the envelope a bit here Mike...

I've converted the code to your method, and it works just fine. It does require a bit more memory, as the queue needs more than the 1 byte I was passing to the previously implemented non-looping task. On the other hand, it operates faster due to the reduces task start/stop overhead. The alternative method would be to use a global variable that the task could check periodically, but would entirely defeat the intended purpose of the encapsulation.

As I said before, the functionality is the same. In this particular (and simplistic) instance, neither method has any advantage nor disadvantage, except perhaps, that your method offers the ability to add more features in the future, at the expense of some minor added RAM usage.

At first glance, the original code just looked like a regular sub routine, and it that manner, was a little ambiguous. Unless you were privy to the fact that it was a separate task (and hence the multitasking environment), you would think that the sub would hold up the rest of the application. The looping method makes it very apparent what the actual program flow is just by looking at the code. This is an advantage no matter how well commented the code is.

My original solution was born out of necessity, and I had no previous example to work from. I like your method though, and agree that it's a more correct way to get the required effect.


-Don
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

Don_Kirby wrote:I've converted the code to your method, and it works just fine. It does require a bit more memory, as the queue needs more than the 1 byte I was passing to the previously implemented non-looping task. On the other hand, it operates faster due to the reduces task start/stop overhead. The alternative method would be to use a global variable that the task could check periodically, but would entirely defeat the intended purpose of the encapsulation.
It shouldn't be a global variable but it can be a private variable to the module. A public method can be used to set the variable and the next time the task loop wakes up, it will read the variable to do the right thing. The task could then reset the variable to behave as a one-shot. Note that in this case a semaphore should be used and in fact the variable could be the boolean semaphore. All of this detail is of course hidden by the implementation of the module and the caller is none the wiser.

If you want the expire the sleep timer of the waiting task then call ResumeTask(). If you want the task to run immediately and then perhaps sleep after turning on the I/O port, you can use RunTask().
Don_Kirby wrote:At first glance, the original code just looked like a regular sub routine, and it that manner, was a little ambiguous. Unless you were privy to the fact that it was a separate task (and hence the multitasking environment), you would think that the sub would hold up the rest of the application. The looping method makes it very apparent what the actual program flow is just by looking at the code. This is an advantage no matter how well commented the code is.
Rename the method to something like XxxxNoWait or AsyncXxxx and then it is self-documenting what it does e.g. RingBuzzerNoWait().
Mike Perks
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

mikep wrote:If you want the expire the sleep timer of the waiting task then call ResumeTask(). If you want the task to run immediately and then perhaps sleep after turning on the I/O port, you can use RunTask().
I decided to provide an example of how to do this.
Mike Perks
Post Reply