home *** CD-ROM | disk | FTP | other *** search
- <!-- Forthmacs Formatter generated HTML output -->
- <html>
- <head>
- <title>Multitasking</title>
- </head>
- <body>
- <h1>Multitasking</h1>
- <hr>
- <p>
- This chapter describes the Risc-OS Forthmacs multitasking scheme which is
- slightly different to the previous implementations. The multitasking system has
- been included into the kernel, so there is no "tasking.fth" any more (at least
- you don't have to load it any more). Also all tasks get their memory allocated
- at bootup time, so in multitasking applications you just have to <code><A href="_smal_AH#7"> start </A></code>
- them.
- <p>
- This multitasking system is cooperative, not preemptive. A task switch occurs
- only when the currently-running task explicitly gives up control not at random
- times. This works fine so long as each task is courteous and relinquishes
- control every so often. This does not preclude the use of interrupts; it just
- means that when an interrupt routine finishes executing, the task that resumes
- execution is always the one that was interrupted.
- <p>
- A task relinquishes control by executing the word <code><A href="_smal_BN#295"> pause </A>,</code>
- hence from now on the act of giving up control will be called pausing. Note
- that some words like <code><A href="_smal_BS#23a"> key </A>,</code> <code><A href="_smal_BT#23b"> key? </A></code>
- and <code><A href="_smal_BS#1da"> emit </A></code> already call <code><A href="_smal_BN#295"> pause </A>.</code>
- <p>
- <p>
- Tasks are kept in a circular queue and are scheduled in a round-robin fashion.
- When the current task pauses, the next task in the queue is given a chance to
- run until it pauses, and so on. You may of course write your own task priority
- managing software. This could be done by an interrupt driven queue-sorting or
- by a <code><A href="_smal_AK#a"> sleep </A></code> <code><A href="_smal_AL#b"> wake </A></code>
- supervisor or whatever mechanism you like.
- <p>
- Tasks may be currently in one of three (formerly two) states: "awake", "waiting"
- or "asleep". If a task is awake, it will be given control when its turn in the
- round-robin queue comes along.
- <p>
- If a task is asleep, it will remain in the queue but will be skipped over.
- <p>
- If it is waiting, it will be given control after a given time has elapsed.
- <p>
- It is reasonable to let sleeping or waiting processes remain in the queue,
- because skipping over them is very cheap; it takes only 4 ARM machine
- instructions to skip over a sleeping task (waiting takes a little longer).
- <p>
- All tasks share the same global address space and can execute the same code if
- necessary. Each task has its own private data stack, return stack, floating
- stack and user area. The user area is where the task-specific <code><A href="_smal_BK#322"> user </A></code>
- variables are stored. The CPU state that defines a task is the contents of the
- Data Stack Pointer <code><A href="_smal_BB#19"> sp </A>,</code> the Return
- Stack Pointer <code><A href="_smal_BF#1d"> rp </A>,</code> the Floating Stack
- Pointer <code><A href="_smal_BC#1a"> fsp </A>,</code> the User Pointer <code><A href="_smal_BD#31b"> up </A></code>
- and the Instruction Pointer <code><A href="_smal_BE#1c"> ip </A>.</code>
- <p>
- The dictionary, which contains executable Forth code, is global and is shared by
- all tasks. Any <code><A href="_smal_BN#325"> variable </A>s</code> or other
- data areas within the dictionary, such as those allocated by <code><A href="_smal_BT#14b"> allot </A>,</code>
- are global and may be accessed by all tasks. This allows tasks to communicate
- using shared memory.
- <p>
- In the initial state of the Forth system, there is only one task, called <code><A href="_smal_BN#265"> main-task </A>.</code>
- This is the task that is executing the Forth text interpreter. Other tasks may
- be subsequently created.
- <p>
- There are several steps involved in making a new task:
- <p>
- <p>
- <h2>Naming the task.</h2>
- <p>
- A task is initially created with either <code><A href="_smal_AA#0"> "task: </A></code>
- or <code><A href="_smal_AB#1"> task: </A>.</code> The difference between the
- two is that <code><A href="_smal_AA#0"> "task: </A></code> takes the name of
- the new task and the <strong>task-size</strong> from the stack, whereas <code><A href="_smal_AB#1"> task: </A></code>
- takes the name from the input stream and uses <code><A href="_smal_AC#2"> default-task-size </A>.</code>
- See the glossary for precise definitions.
- <p>
- The new task gets it's private memory allocated in the heap, the <code><A href="_smal_BN#265"> main-task </A>s</code>
- user area will be copied to the new task's and the new task will be set to
- sleeping state. (This was formerly done by "fork"ing the task.)
- <p>
- <strong>Note:</strong> there are now some differences to remember: <code><A href="_smal_BK#322"> user </A></code>
- variables will be copied (and so are the definitions of deferred words) when
- defining the task or at bootup time, always the <code><A href="_smal_BN#265"> main-task </A>s</code>
- user area is copied.
- <p>
- <p>
- <h2>Setting the program that the task is to execute.</h2>
- <p>
- The task must be told what to do. This is done by setting the task's
- Interpreter Pointer <code><A href="_smal_BE#1c"> ip </A></code> to the address
- of an appropriate bit of Forth code. The Interpreter Pointer in Forth plays a
- role in Forth that is analogous to the role that the Program Counter plays in a
- machine language program. The word that sets the Interpreter Pointer is <code><A href="_smal_AH#7"> start </A>.</code>
- For an already active task, <code><A href="_smal_AH#7"> start </A></code> may
- also be used to make the task start over or even do something entirely
- different.
- <p>
- <p>
- <h2>Awakening the task.</h2>
- <p>
- Now the task may be actually set to work. Waking the task, by using the word <code><A href="_smal_AL#b"> wake </A>,</code>
- causes the task to be executed when its turn in the queue comes, instead of
- being skipped. Once awakened, a task will continue running until it pauses. If
- a task pauses, it will continue to execute each time that its turn in the queue
- comes around. If a task does not want to run any more, it may put itself to
- sleep so that it will be skipped. Tasks may also control each other, so that
- one task may put another to sleep.
- <p>
- <p>
- <h2>Waiting ...</h2>
- <p>
- A waiting task behaves very much like a sleeping one, it will be skipped for a
- specified time. After this time has elapsed it is awake.
- <p>
- <p>
- <h2>Glossary</h2>
- <p>
-
- <hr><h3><A name="0">"task:</A> ( size name -- )</h3>
- <br>
- A defining word used to create a new task. Name is the address of a packed
- string which is entered into the dictionary as the name of the new task. Size
- is the number of bytes to allocate for the task's private data areas. Size must
- be large enough to include space for a data stack, a return stack, and a user
- area. <code><A href="_smal_AC#2"> default-task-size </A></code> is a good
- choice.
- <p>
- When name is subsequently executed, the address of the new task's data area is
- left on the stack. This data area lies within the Risc-OS Forthmacs heap, it is
- allocated immediately when <code><A href="_smal_AA#0"> "task: </A></code> is
- executed and when booting Risc-OS Forthmacs.
-
- <hr><h3><A name="1">task:</A> ( -- )</h3> <kbd>name</kbd>
- <br>
- A defining word executed in the form:
- <br><code> TASK: <name></code><br>
- Creates a dictionary entry for name which is a new task. The size of the
- private data storage area for that task is <code><A href="_smal_AC#2"> default-task-size </A>.</code>
- <p>
- Tasks thus created behave exactly like those created by <code><A href="_smal_AA#0"> "task: </A></code>
-
- See: <code><A href="_smal_AA#0"> "task: </A></code>
-
- <hr><h3><A name="2">default-task-size</A> ( -- size )</h3>
- <br>
- Size is an appropriate number to use for the size of a new task's private data
- areas. Currently it is (hex) 1800, which gives (hex) 1000 bytes for User Area,
- and (hex) 200 bytes each for the Return Stack and the Data Stack plus an extra
- $400. If a task is created with a size other than <code><A href="_smal_AC#2"> default-task-size </A>,</code>
- the difference will be reflected in the size of the Data Stack.
-
- <hr><h3><A name="3">task-ps-size</A> ( -- u )</h3>
- <br>
- u is the number of bytes that will be allocated for the Parameter Stack from the
- private data area for a task. Currently it is (hex) 200.
-
- <hr><h3><A name="4">task-rs-size</A> ( -- u )</h3>
- <br>
- u is the number of bytes that will be allocated for the Return Stack from the
- private data area for a task. Currently it is (hex) 200.
-
- <hr><h3><A name="5">user-size</A> ( -- u )</h3>
- <br>
- u is the number of bytes that will be allocated for the User Area from the
- private data area for a task. Currently it is (hex) 1000.
-
- <hr><h3><A name="6">fork</A> ( task-apf -- )</h3>
- <br>
- obsolete now!
-
- <hr><h3><A name="7">start</A> ( acf task -- )</h3>
- <br>
- task is the address of the private data area of a task, as left by the <name>
- of a task. acf is the compilation address of a high-level Forth word that the
- task is to execute. The data and return stacks of the task are cleared, and the
- task is set so that it will begin execution of that word when it is awakened and
- its turn comes.
- <p>
- The word that a task executes should never return. It should either contain an
- endless loop, or should execute <code><A href="_smal_AO#e"> stop </A></code>
- when it is done, thus putting itself to sleep.
- <p>
- <code><A href="_smal_AH#7"> start </A></code> should only be executed by one
- task in order to initialise a different task. A task should not try to <code><A href="_smal_AH#7"> start </A></code>
- itself (this effect may be had from within a task by explicitly clearing the
- stacks and executing the desired word).
- <p>
- A task may <code><A href="_smal_AH#7"> start </A></code> another at any time.
- STARTing an already-running task will make it immediately stop what it is doing
- and instead execute the new action (acf).
-
- <hr><h3><A name="8">activate</A> ( task -- )</h3>
- <br>
- Starts "task" with the code that immediately follows this <code><A href="_smal_AI#8"> activate </A>,</code>
- this is the shortest way to define and run a task. Example:
- <br><code> </code><br>
- <br><code> variable demoticker</code><br>
- <br><code> task: democounter</code><br>
- <br><code> : counting multi democounter activate</code><br>
- <br><code> multi begin 1 demoticker +! 1000 msec</code><br>
- <br><code> again ;</code><br>
- <br><code> counting</code><br>
-
- <hr><h3><A name="9">set-task</A> ( ip task -- )</h3>
- <br>
- Similar to <code><A href="_smal_AH#7"> start </A>,</code> except that instead
- of giving the acf of the word to execute, the actual address of the task's new
- interpreter pointer is given. This is useful as an implementation word for the
- multitasking system, but it is not recommended for use by user programs.
-
- <hr><h3><A name="a">sleep</A> ( task -- )</h3>
- <br>
- task is the address of the private data area of a task, as left by the <name>
- of a task. The task is put to sleep, so that it will be skipped each time that
- its turn in the round robin queue comes up. This is the appropriate way to
- suspend the activity of another task.
-
- <hr><h3><A name="b">wake</A> ( task -- )</h3>
- <br>
- task is the address of the private data area of a task, as left by the <name>
- of a task. The task is awakened, so that it will continue execution when its
- turn in the round robin queue comes up.
-
- <hr><h3><A name="c">wait</A> ( n task -- )</h3>
- <br>
- task is the address of the private data area of a task, as left by the <name>
- of a task. The task will be waiting for n msec from now on.
-
- <hr><h3><A name="d">ms</A> ( n -- )</h3>
- <br>
- The currently executing task puts itself to waiting for n milli-seconds. This
- is much better than
- <br><code> begin test-flag</code><br>
- <br><code> while pause</code><br>
- <br><code> repeat</code><br>
- because the waiting primitive has been optimized in speed exactly for this
- purpose. Waiting tasks don't cost any significant time this way.
-
- <hr><h3><A name="e">stop</A> ( -- )</h3>
- <br>
- The currently executing task puts itself to sleep and relinquishes control.
- This is the way for a task to stop itself when it is finished with its job. It
- is usually not a good idea to execute <code><A href="_smal_AO#e"> stop </A></code>
- interactively, since the interactive task will be then go to sleep and will stop
- listening to the keyboard.
-
- <hr><h3><A name="f">(pause</A> ( -- )</h3>
- <br>
- The multitasking scheduler, installed by <code><A href="_smal_AS#12"> multi </A></code>
- in <code><A href="_smal_BN#295"> pause </A>.</code>
-
- See: <code><A href="_smal_BN#295"> pause </A></code>
-
- <hr><h3><A name="10">pause</A> ( -- )</h3>
- Extra: Deferred
- <br>
- The currently executing task relinquishes control and the round-robin task queue
- is scanned for the next task that is awake. When other tasks have been given
- their chance to run, eventually control will be returned to this task, which
- will begin execution immediately following the <code><A href="_smal_BN#295"> pause </A>.</code>
- <p>
- Pause is deferred so that multi-tasking may be disabled by setting <code><A href="_smal_BN#295"> pause </A></code>
- to execute <code><A href="_smal_AL#27b"> noop </A>.</code> To enable
- multitasking, <code><A href="_smal_AP#f"> (pause </A></code> is installed in <code><A href="_smal_BN#295"> pause </A>.</code>
- <p>
- <code><A href="_smal_AP#f"> (pause </A></code> is itself the multitasking
- scheduler. It is only 5 ARM instructions long!
-
- <hr><h3><A name="11">single</A> ( -- )</h3>
- <br>
- Disables multitasking by installing <code><A href="_smal_AL#27b"> noop </A></code>
- in <code><A href="_smal_BN#295"> pause </A>.</code>
-
- <hr><h3><A name="12">multi</A> ( -- )</h3>
- <br>
- Enables multitasking by installing <code><A href="_smal_AP#f"> (pause </A></code>
- in <code><A href="_smal_BN#295"> pause </A>.</code> For safety's sake, <code><A href="_smal_AS#12"> multi </A></code>
- also ensures that the main task is awake.
-
- <hr><h3><A name="13">local</A> ( task user-variable -- adr )</h3>
- <br>
- task is the address of the private data area of a task, as left by the <name>
- of a task. user-variable is the address of a user variable in the currently
- executing task's user area. adr is the address of the other task's copy of that
- user variable.
- <p>
- <code><A href="_smal_AT#13"> local </A></code> allows one task to poke around
- in another task's private data space, to see what is going on there.
-
- <hr><h3><A name="14">up@</A> ( -- adr )</h3>
- <br>
- adr is the address of the private data area of the currently executing task,
- which happens to be the starting address of its user area. This allows a task
- to refer to itself without knowing its name.
-
- <hr><h3><A name="15">main-task</A> ( -- task )</h3>
- <br>
- task is the address of the private data area of the only task that was around in
- the beginning. Usually this task is executing the Forth interpreter, but it
- doesn't necessarily have to be.
-
- <hr><h3><A name="16">.task</A> ( -- )</h3>
- <br>
- Gives information about a task identified by the tasks code-field-address.
-
- <hr><h3><A name="17">.tasks</A> ( -- )</h3>
- <br>
- full information about all installed tasks is displayed.
- <p>
- <h2>Implementation Notes</h2>
- <p>
- Aborting results just in aborting the currently active task, but <code><A href="_smal_BN#265"> main-task </A></code>
- will be running in any case. Aborted tasks can be restarted only by <code><A href="_smal_AH#7"> start </A></code>
- or <code><A href="_smal_AJ#9"> set-task </A></code> for security reasons.
- <p>
- </body>
- </html>
-