Tasks
A pyRTOS task consists of a task object and a function that contains the task code. The task function takes only one argument, the task object that contains it. The task function is created by Python. Any code before the first yield is setup code, and anything returned by this yield will be ignored. The main task loop follows this yield, and this is the code that will be executed when the scheduler allocates CPU time to tasks. The
main task loop is usually an infinite loop. If a task needs to terminate, it should call return, and perform any necessary operations before returning. But typically, tasks never return.
Preemption in pyRTOS is completely voluntary. This means that all tasks must periodically hand control back to the operating system, otherwise other tasks will not get CPU time, messages cannot be passed between tasks, and other management functions of the operating system will never be executed. There are two functions for yield in pyRTOS, one of which is to simply pass control back to the operating system, which allows the operating system to reevaluate task priorities and pass control to higher priority ready tasks, and allows the operating system to handle management such as messaging and locking. Yields should be fairly frequent, but not so frequent that more time is spent in the OS than in the task. For small tasks, once per main loop is sufficient. For larger tasks, yields should be placed between important subsections. If a task has a section of timing-dependent code, do not place yields where they might interrupt the time-critical flow, as there is no guarantee that the yield will resume in the allotted time.
Yields are also used to make certain blocking API calls. The most common is probably to delay. Higher priority processes need to be given priority, because even frequent yields will not give CPU time to lower priority processes, the default scheduler always gives CPU time to the highest priority ready task. The only way for a lower priority task to get time is for the higher priority task to block when it does not need CPU time. Usually this means delays, which in pyRTOS is implemented by timeout generators. When the timeout generator expires, the task will become ready again. Until then, lower priority tasks will be allowed to be allocated CPU time. Tasks can also block while waiting for messages or mutex locks. In the future, there may be more tolerant non-real-time schedulers.
There are also some places where tasks should always yield. Whenever a message is delivered, it is placed in the local queue. When a task yields, the message in the local task outgoing queue is delivered. Other places where yield is needed will be noted in the document.
|