FreeWPC implements a runtime, round-robin, non-preemptive task scheduler. The system manages multiple tasks, each of which has its own call stack. You can create a new task when you want to run some code in parallel with the current thread of execution. The current task has complete control of the CPU and will continue to run until it explicitly exits or sleeps (waiting for a certain amount of time). The minimum sleep time is defined by IRQS_PER_TICK and is currently 16ms.
Contrast this with desktop operating systems, which use timeslicing and preemption to allow multiple threads to run in parallel. There, the OS switches between the tasks. In FreeWPC task switches only happen explicitly; this provides for more deterministic behavior, it requires less overhead, and it eliminates the need for mutexes (locks) between two tasks because task switches cannot happen at arbitrary times.
If a task does not give up control, either by sleeping, exiting, or yielding
after a certain amount of time, the fatal error ERR_FCFS_LOCKUP
will be asserted.
When a new task is created, it does not begin running immediately; control always continues with the task that started it. This allows you to configure the new task before it can run.
Each task is identified by a process ID, or pid. The PID for a task is assigned by the system when it is created. A task also has a group ID, or gid. The GID is assigned by the programmer, and multiple tasks can share the same GID. GIDs allow you to control a group of related tasks, or to refer to a task using a compile-time ID as opposed to a run-time ID.
In native mode, the multitasking APIs are implemented using the GNU Pth library. A good description of this library can be found at http://www.gnu.org/software/pth/. Note this is not the same as pthreads, which is the predominant preemptive threading library.
For Windows programmers, this threading model is very similar to what Win32 calls fibers.
After all tasks have been given a chance to run in a 16ms timeslice, the system runs the periodic functions. These have equal priority to tasks, but they do not have a stack and they cannot sleep. In the code, these are also referred to as idle functions, although that term is slightly incorrect (they are guaranteed to be called even on a fully busy system).
You can register a periodic function at various rates: either every 16ms, 100ms, 1 second, or 10 seconds. Handlers may not be called at the exact rate, but it will be as close as possible. Slippage occurs when it takes longer than 16ms for all tasks to be given a chance to run. If you need exact timing, use a realtime function.
For example, assume that a 1 second handler does not get called until after 1.5 seconds from the previous call, because the system is busy running other tasks. Then the next time it will be called just 500ms later; the scheduler will realize that it is behind and try to catch up.
You cannot dynamically register or deregister periodic handlers at runtime.
task_create_gid
task_set_rom_page
immediately after this call to say where
it was placed.
task_create_gid1
task_recreate_gid
task_sleep
TIME
defines which is given in units of approximately 16ms. Because it is 8-bit, this limits it
to about 4 seconds.
If you want to sleep for the absolute minimum, instead of task_sleep (0)
it is recommended
to use task_yield()
.
task_sleep_sec
task_exit
task_find_gid
task_kill_pid
task_kill_gid
task_set_arg
task_get_arg