Let's try out the multi-tasker Mar 2017
The multi-tasker lets us do multiple things at once, or more precisely: it can quickly switch between tasks, each with their own data and return stacks, and their own program counter. The result is that you can write a thread of code as if nothing else is of interest… as we’ll see.
But this is not quite the multi-tasking you might expect from an RTOS, which usually does everything based on interrupts from timers and other hardware peripherals. The multi-tasking traditionally used in Forth is cooperative, not pre-emptive: instead of using interrupts to force tasks to relinquish control, cooperative multi-tasking relies on tasks voluntarily passing control to other tasks from time to time. If everyone plays nice, everyone will get a fair deal.
This has some major implications:
- cooperatve multi-tasking is much simpler (under 100 lines of Forth)
- there is no risk of being interrupted, your code always runs to completion
- the only time when a task loses control is when it calls
- it is upon the programmer to make sure that this happens “often enough”
- tasks are either “active” (running every so often) or “idle” (not running)
- interrupts can be used to change the active/idle state of any task
- new tasks can be dynamically added or removed from the list of all tasks
- lastly, the multi-tasker can be started and stopped at any time
There’s a lot to take in when it comes to even just this simple form of multi-tasking, but as you will see, it’s very effective and an excellent match for Forth’s interactive nature. Let’s dive in!
Here’s a small example to blink an LED on pin PA12 as a background task:
PA12 constant LED1 OMODE-PP LED1 io-mode! task: blinker : blink& ( -- ) blinker activate begin LED1 iox! \ toggle LED1 200 ms \ wait 200 ms again ;
So far, nothing has been activated: this merely sets up the LED, defines a task
blinker, and defines a word which becomes the “handler” for this task
when it’s running. So
blinker is the task descriptor, while
blink& is the
actual task code and logic.
There is always one
boot task, as we can see by entering the
Task @ 20004AC0 Next: 20004AC0 State: FFFFFFFF Stack: 00000000 Handler: 00000000
To insert the
blinker task into the task list, we need to call
tasks will show:
Task @ 20004AC0 Next: 20000BA8 State: FFFFFFFF Stack: 20000278 Handler: 00000000 Task @ 20000BA8 Next: 20004AC0 State: FFFFFFFF Stack: 20000CB8 Handler: 00000000
Note how the “Next” chain is a circular list. But… wait a minute… the LED is not blinking!
Oops, we need to enable multi-tasking: Forth is listening for commands and
executing them, but multi-tasking dispatch has not been enabled yet. To start it
multitask - the LED starts blinking and our command prompt is still
responsive. Forth is now dual-tasking!
To disable the multi-tasker, enter
singletask. This is a very useful feature:
when developing, you often need to stop all the background activity -
especially if it’s generating its own output.
For a larger example, see g6s/ex/tasks.fs, which starts up 4 tasks in the background, each controlling its own LED and at a different rate.
stop word, which causes the current task to make itself inactive
(idle). If you type
stop at the command prompt, you’ll have deactivated the
command prompt, but the LED(s) will keep on blinking. There are ways to get the
prompt back, but it’s usually not a good idea.
Recall that this is collaborative multi-tasking, which only switches tasks
pause is called - so how come it’s switching tasks, even though there are
no calls to
pause in our code?
Well, the calls are in there, in a few places in the embello source code so far, in fact:
- when calling “
ms”, i.e. when requesting millisecond delays (but not in “
- when calling “
key?” (or “
key”, which calls
Knowing exactly where these
pause calls are made is essential if you need to
stay in control in your code. The above two cases are fairly logical ones, since
they both indicate that a task has nothing else to do, at least for a little
while. Eventually, it’ll be given control again.
And that’s really the gist of it: with just the above very basic introduction to
the Forth multi-tasker, you can start to write code which does a lot of things
at “more or less” the same time. Each of the tasks can have loops and perform
its work as if nothing else needs to happen - as long as you make sure that
pause gets called regularly (within a few milliseconds is usually fine, but it
really depends on the application).