Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ task_intro(A) — Apollo

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

ec2_$intro

fault_$intro

mutex_$intro

pfm_$intro

TASK_$INTRO                       Domain/OS                        TASK_$INTRO


NAME
     intro - the Domain/OS task library

SYNOPSIS (C)
     #include <apollo/base.h>
     #include <apollo/task.h>

SYNOPSIS (Pascal)
     %include "/sys/ins/base.ins.pas"
     %include "/sys/ins/task.ins.pas"

DESCRIPTION
     The task_$ calls create and manage a multitasking environment within a
     single process.  In a multitasking environment, multiple threads of exe-
     cution (tasks) run within a single address space.  Because task creation
     and switching are less expensive than process creation and switching,
     tasking is a useful way of breaking down any complex operation into
     separate pieces that run concurrently.  It is particularly useful in
     Remote Procedure Call (RPC) servers that manage multiple remote requests
     in parallel.

     The following is a list of the task_$ calls.

       task_$blast             kill a task without cleanup
       task_$create            create a task
       task_$exit              exit a task
       task_$get_ec            get a completion eventcount
       task_$get_handle        return the task handle
       task_$get_info          get information about a task
       task_$release           release a task and report
       task_$set_name          name a task
       task_$set_result        change the completion status and output value
       task_$signal            signal a task
       task_$tasking_enabled   determine whether tasking is enabled
       task_$yield             yield the processor

   Creating a Task
     A process creates a task by calling task_$create, which returns a task
     handle to identify the task.  You can obtain the handle of the currently
     running task by calling task_$get_handle.  (You can also optionally
     assign to the task an ASCII-string task name, up to 32 characters, by
     calling task_$set_name.)

     The first task in a process, the one that first calls task_$create, is
     known as the Distinguished Task (DT).  After the first call to
     task_$create, tasking is enabled in the process, and any task can create
     a new task.

   Time Slicing and Priority Levels
     Tasks are able to run concurrently because of a scheduling scheme based
     on time slicing and priority levels.  An initial priority level (from 1
     to 9, where 9 is the highest) is assigned to a task when it is created.
     The Task Scheduler uses the priority level to determine which task (of
     the tasks that are ready) to run when one of the following conditions
     occurs:

     ⊕  A time-slice interval ends.

     ⊕  A task voluntarily yields the processor by calling task_$yield.

     ⊕  A task blocks in user space by calling one of the following:
        ec2_$wait, ec2_$wait_svc, time_$wait, msg_$wait, or mutex_$lock.
        (When a task blocks in the operating system the entire process is
        blocked, and another task is not started until the process returns to
        user space.)

     As a task runs without blocking, its priority level is gradually lowered
     until it reaches the lowest priority level.  It then runs at the lowest
     priority level until it blocks.  Each time a task wakes after being
     blocked, the Task Scheduler reassigns the initial priority level to the
     task.  The Task Scheduler algorithm favors tasks that run a short time
     between blocking, but ensures that all tasks eventually run:  no task can
     lock out other tasks forever.

     A task exists until one of the following events occurs:

     ⊕  The task routine returns.

     ⊕  The task routine calls task_$exit.

     ⊕  An unhandled fault occurs.

     ⊕  The task's lifetime, as defined by task_$create, expires.

   Completion Eventcount
     If a task is created with the task_$intend_to_wait option, the system
     maintains an eventcount that advances when the task completes.  A task
     can wait for the completion of another by calling task_$get_ec to get the
     eventcount, and then using ec2_$wait to wait for it to advance.  If a
     task is created with a completion eventcount, then some other task must
     call task_$release to release the task after it completes.  The
     task_$release call will also supply the completion status and output
     value of the completed task to the caller.

   Fault Inhibiting
     Inhibiting faults in a tasking process inhibits all asynchronous signals,
     including those used for task switching.  To inhibit all signals, includ-
     ing those that cause task switching, use pfm_$inhibit and pfm_$enable.
     For example, a tasking program could use pfm_$inhibit and pfm_$enable
     pairs to prevent other tasks from running during critical sections of
     code.

     Fault inhibiting is always per task:  one task can inhibit faults while
     another doesn't.  If a fault is pending when a non-inhibited task begins
     to run, the fault is signaled then.  Also, pfm_$inhibit does not prevent
     a task switch from occurring when a task blocks or voluntarily yields the
     processor.

   Fault Handlers
     Because fault handlers are established on a per-process basis, the same
     fault handler executes regardless of the task that established it or the
     task that is running when the fault occurs.  A handled fault is not pro-
     pagated to the DT.

   Clean-Up Handlers
     Clean-up handlers are established per task; that is, a clean-up handler
     is invoked only in the task that established it.  A synchronous fault is
     delivered to the clean-up handler in the task in which the synchronous
     fault occurs.  An asynchronous fault is delivered to the DT the next time
     it runs.  The Task Manager sets up a default clean-up handler at the base
     of a task's stack when the task is created.

     A task can exit with cleanup either by returning or by calling
     task_$exit.  A task can also cause another task to exit cleanly by cal-
     ling task_$signal.  As a last resort, a task can destroy another task
     without cleanup by calling task_$blast.

   Programming Restrictions
     Because tasks are time-sliced, an application using tasking must ensure
     that all code it uses is "re-entrant"; that is, designed so that multiple
     tasks can execute it concurrently.

     For an example of code that is not re-entrant, suppose an application
     (written in C) uses two tasks, T1 and T2.  Each task calls a function
     add_to_table that adds an integer to a table (array) of integers.  The
     procedure uses two global variables, num_of_entries and table.

          int num_of_entries = 0;
          int table[100];

          void
          add_to_table(int x)
          {
               table[num_of_entries] = x;
               num_of_entries = num_of_entries + 1;
          }

     Task T1 assigns x to table[num_of_entries] but is then suspended.
     Meanwhile, task T2 starts running and calls add_to_table.  Since task T1
     has not yet updated num_of_entries, task T2 overwrites the entry that
     task T1 has made.  Presumably, the application intends that the table and
     entry count should reflect the calls made by both tasks; consequently,
     add_to_table is not re-entrant.

     It is often possible to safely use code that is not re-entrant.  For
     example, if a system library that is not re-entrant is called solely from
     one task, then it does not matter that the library is not re-entrant.
     Also, some libraries return handles to static data structures, where the
     handle is the only pathway to that storage.  Consequently, if only one
     task ever uses a particular handle when it makes calls that are not re-
     entrant, no problems will occur.  For example, ios_$open is not re-
     entrant, but a single task may safely use it.

     If you create a library and you intend it to run with tasking applica-
     tions, you should make the manager re-entrant by using Mutual Exclusion
     (mutex) locks.  Mutex locks ensure that only one task at a time has
     access to the manager's data structures.  In order to use a data struc-
     ture, the task must first request and receive the lock associated with
     it.  For example, to make the add_to_table procedure re-entrant, add
     mutex locks to it as follows:

          int num_of_entries = 0;
          int table[100];
          mutex_$lock_rec_t table_lock;

          void
          add_to_table(int x)
          {
               mutex_$lock(table_lock, mutex_$wait_forever);
               table[num_of_entries] = x;
               num_of_entries = num_of_entries + 1;
               mutex_$unlock(table_lock);
          }

     Note that the application must call mutex_$init before it makes the first
     call to the add_to_table procedure.

     Not all Domain/OS libraries are re-entrant.  The following table categor-
     izes Domain/OS calls into those that are probably re-entrant (that is,
     they are most likely re-entrant but have not been thoroughly tested for
     this feature), partially re-entrant (they return handles as described
     above), not re-entrant, and those that are definitely re-entrant.

                ___________________________________________________
                |    Re-entrant Properties of Domain/OS Calls      |
                |__________________________________________________|
                |           | Probably   | Partially  |    Not     |
                |Re-entrant | Re-entrant | Re-entrant | Re-entrant |
                |___________|____________|____________|____________|
                |ec2        | cal        | error      | ctm        |
                |ms         | fpp        | gmf        | ev         |
                |mutex      | pbu        | ios        | gmr        |
                |name       | proc2      | ipc        | gmr3d      |
                |pfm        | sio        | pad        | gpr        |
                |proc1      | smd        | stream     | mbx        |
                |rws        | time       | vfmt       | pbufs      |
                |sfcb       | tpad       |            | pgm        |
                |task       | vec        |            | prf        |
                |           |            |            | trait      |
                |___________|____________|____________|____________|

     Domain/OS calls without side effects (time_$clock for example) are gen-
     erally re-entrant.  Calls that open, create, or close objects (ios_$open
     and ipc_$delete for example) are not re-entrant.

     The following additional restrictions apply to tasking:

     ⊕  UNIX system and library calls are not supported.  They may work, but
        they are not guaranteed to.

     ⊕  Asynchronous fault handlers must not make any task_$ calls.

     ⊕  Ec2_$wait_svc behaves exactly like ec2_$wait when tasking is enabled.
        The reason is that the Task Manager does not know which task to notify
        when an asynchronous fault handler has returned to the point at which
        the fault occurred.

   Constants
     task_$max_priority
          The highest task priority.

     task_$name_max_len
          The maximum number of bytes in a task name.

     task_$dt_handle
          The task handle for the Distinguished Task (DT).

   Data Types
     task_$handle_t
          A task handle.

     task_$info_pt
          A pointer to type task_$info_t.

     task_$info_t
          A record type for passing task information.  The diagram below
          illustrates the task_$info_t data type.


              15                                                           0
              ______________________________________________________________
              |                   routine_to_run_in_task                    |
              |_____________________________________________________________|
              |                   routine_to_run_in_task                    |
              |_____________________________________________________________|
              |                          arg_ptr                            |
              |_____________________________________________________________|
              |                          arg_ptr                            |
              |_____________________________________________________________|
              |                          arg_len                            |
              |_____________________________________________________________|
              |                          arg_len                            |
              |_____________________________________________________________|
              |                         stack_size                          |
              |_____________________________________________________________|
              |                         stack_size                          |
              |_____________________________________________________________|
              |                      initial_priority                       |
              |_____________________________________________________________|
              |                      initial_priority                       |
              |_____________________________________________________________|
              |                      current_priority                       |
              |_____________________________________________________________|
              |                      current_priority                       |
              |_____________________________________________________________|
              |                          lifetime                           |
              |_____________________________________________________________|
              |                          lifetime                           |
              |_____________________________________________________________|
              |                       task_name_ptr                         |
              |_____________________________________________________________|
              |                       task_name_ptr                         |
              |_____________________________________________________________|
              |                       task_name_len                         |
              |_____________________________________________________________|
              |                       task_name_len                         |
              |_____________________________________________________________|
              |                           state                             |
              |_____________________________________________________________|
              |                       level_created                         |
              ______________________________________________________________
              15                                                           0


          routine_to_run_in_task
               A pointer to the task routine.

          arg_ptr
               Pointer to the task's argument vector.

          arg_len
               The number of bytes in the task's argument vector.

          stack_size
               The number of bytes in the task's stack.

          initial_priority
               The initial priority of the task.

          current_priority
               The priority at which the task is currently running.

          lifetime
               The lifetime of the task.  It is a value of type
               task_$lifetime_t.

          task_name_ptr
               A pointer to the name of the task.

          task_name_len
               The number of bytes in the task name.

          state
               The run state of the task.  It is a value of type
               task_$run_state_t.

          level_created
               The program level at which the task was created.

     task_$lifetime_t
          An enumerated type for specifying the lifetime of a task.  (In Pas-
          cal, the values of type task_$lifetime_t are constants.)  It takes
          one of the following values:

          task_$forever
               Create a task that lives independent of the process that
               created it.

          task_$until_level_exit
               The task lives until the level at which it was created is
               released.

          task_$until_exec_or_level_exit
               The task lives until the level at which it was created is
               released or overlaid by a UNIX exec(2) call.

     task_$name_pt
          A pointer to a task name.

     task_$option_set_t
          A small set type for specifying options to task_$create.  There is
          currently only one value:

          task_$intend_to_wait
               Create a task completion eventcount that advances when the new
               task terminates.  When created with the task_$intend_to_wait
               option, a task is suspended when it completes pending a
               task_$release call.

          When created without the task_$intend_to_wait option, a task is not
          suspended when it completes; instead it terminates immediately on
          completion, and the completion status and output value of the task
          will not be accessible.

     task_$routine_p
          A pointer to a task routine.  A task routine must not return a value
          and must take two arguments:  a pointer to an argument vector, fol-
          lowed by the number of bytes in the argument vector.  The argument
          vector may contain anything.

          In C, a valid task routine declaration is
          void task_routine(
              void *arg_pointer,
              int arg_length)

          In Pascal, a valid task routine declaration is
          procedure task_routine(
              in arg_ptr: univ_ptr;
              in arg_len: integer32);
              options(val_param);

     task_$run_state_t
          An enumerated type for describing the run state of a task.  It takes
          one of the following values:

          task_$ready
               The task is ready to run.

          task_$waiting
               The task is waiting.

   Errors
     task_$bad_$ec_wait
          Internal error:  bad status from ec2_$wait.

     task_$bad_handle
          Invalid task handle.

     task_$cant_blast_dt
          Attempted to blast the distinguished task.

     task_$cant_blast_yourself
          Task attempted to blast itself.

     task_$exit_fault
          Task exited.

     task_$invalid_lifetime
          Invalid task lifetime.

     task_$invalid_name
          Task name is too long.

     task_$invalid_priority
          Invalid task priority.

     task_$lib_init_failed
          Internal error:  library initialization failed.

     task_$no_backgrnd_from_foregrnd
          A foreground task attempted to create a background task.

     task_$no_completion_ec
          Task does not have a completion eventcount.

     task_$no_room
          Not enough virtual memory left to create task.

     task_$not_completed
          Task not yet completed.

     task_$not_found
          Task does not exist.

     task_$not_init
          Task Manager has not been initialized.

     task_$stack_corrupted
          A task stack was corrupted.

     task_$stack_overflow
          A task stack overflowed.

     task_$too_many_tasks
          Attempted to exceed the maximum allowable number of tasks in a pro-
          cess.

SEE ALSO
     ec2_$intro, fault_$intro, mutex_$intro, pfm_$intro.

Typewritten Software • bear@typewritten.org • Edmonds, WA 98026