TASK(3CC4)TASK(3CC4)NAMEtask - coroutines in the C++ task library
SYNOPSIS
#include <task.h>
typedef int (*PFIO)(int,object*);
typedef void (*PFV)();
class object {
public:
// exported constants and types
enum objtype { OBJECT, TIMER, TASK, QHEAD, QTAIL, INTHANDLER };
// exported constructor
object();
// exported data members
object* o_next;
static PFIO error_fct;
// exported virtual functions
virtual objtype o_type();
virtual int pending();
virtual void print(int, int=0);
// print flags, used as arguments to print function
#define CHAIN 1
#define VERBOSE 2
#define STACK 4
// exported misc functions
void alert();
void forget(task*);
void remember(task*);
// exported static member functions
static int task_error(int, object*);
static task* this_task();
};
class sched: public object {
public:
// exported constants and types
enum statetype { IDLE=1, RUNNING=2, TERMINATED=4 };
protected:
// protected constructor
sched();
public:
// exported data members
static task* clock_task;
static PFV exit_fct;
// exported virtual functions
virtual int pending();
virtual void print(int, int =0);
virtual void setwho(object*);
// exported misc functions
void cancel(int);
int dont_wait();
sched* get_priority_sched();
int keep_waiting();
statetype rdstate();
long rdtime();
int result();
// exported static member functions
static long get_clock();
static sched* get_run_chain();
static int get_exit_status();
static void set_exit_status(int i);
static void setclock(long);
};
class task: public sched {
public:
// exported constants and types
typedef unsigned char _Uchar;
enum modetype { DEDICATED=1, SHARED=2 };
#define DEFAULT_MODE DEDICATED
#define SIZE 3000
protected:
// protected constructor
task(char* = 0, modetype = DEFAULT_MODE, int = SIZE);
public:
// exported data members
task* t_next;
_Uchar* t_name;
// exported virtual functions
virtual objtype o_type();
virtual void print(int, int =0);
virtual void setwho(object*);
// exported misc functions
void cancel(int);
void delay(int);
int preempt();
void resultis(int);
void sleep(object* =0);
void wait(object*);
int waitlist(object* ...);
int waitvec(object**);
object* who_alerted_me();
// exported static member function
static task* get_task_chain();
};
class timer: public sched {
public:
// exported constructor
timer(int);
// exported virtual functions
virtual objtype o_type();
virtual void print(int, int =0);
virtual void setwho(object*);
// exported misc functions
void reset(int);
};
DESCRIPTION
A task is an object with a control thread running its associated pro‐
gram. More specifically, a user task is an object of a type derived
from class task. The constructor of that object is the main program of
the task. The task does not survive the completion of the constructor.
The task system works in terms of operations which may be performed
immediately or which must wait, and objects which are ready or pending.
If a task requests a service which may be performed immediately, or
waits on an object which is ready, the task continues execution. If
the service cannot be performed immediately or if the object being
waited on is not ready, the task is blocked. When a running task is
blocked, the scheduler selects the next task which is ready to run and
gives it control. A task is never preempted, but may be blocked, thus
allowing other tasks to run. Control proceeds in a round-robin
sequence. (It is possible for a task to be given priority and moved to
the head of the list of tasks to be run.) Whenever an object becomes
newly ready, any tasks which were waiting for it are notified, and
those tasks become ready to run.
A task may be in one of three states:
RUNNING
Running now or ready to be run.
IDLE Not ready to run; waiting for some object.
TERMINATED
The task has finished and cannot be resumed. The task's return
value may be retrieved.
The task system is rooted in class object. Anything derived from this
class may be put on a queue or waited on. Virtual member function
pending() returns non-zero if the object is not ready. Each type of
object may have its own version of this function, and thus its own cri‐
teria for determining whether it is ready or pending, but there may be
only one such function.
Each object has a list of objects which are waiting on it, the remember
chain. When a task waits on an object which is not ready, the task is
suspended (IDLE) and added to the object's remember chain. When the
object next becomes ready (not pending), each waiting task is notified
via member function object::alert(). This function changes the state
of these tasks to RUNNING and puts them back on the list of ready
tasks, the scheduler's run chain.
Class sched, derived from object, provides the functionality common to
task-like objects. The scheduling functions are provided here. Rather
than a separate scheduler, the scheduling operations are part of class
sched, and tasks cooperatively provide scheduling by means of its mem‐
ber functions. This class may not be instantiated, but may be used
only as the base class of tasks and timers.
Class sched also provides the facilities for the simulated passage of
time. Units of simulated time need have nothing to do with real time.
The simulated system clock is initialized to zero, and can be set at
most once via sched::setclock(). Thereafter, the clock advances only
by calls to task::delay(), whose parameter is the number of simulated
time units to delay. When that amount of simulated time has elapsed,
delay() returns. The call to delay() causes the clock to advance to
the earlier of the amount of delay and the next simulated time at which
something is scheduled to happen. The current value of the clock may
be read via function sched::getclock().
Class timer is a stripped-down task which simply delays the specified
amount of simulated time, then terminates. A timer may be waited on,
and when its time has elapsed any waiting tasks are alerted. A timer
can be either RUNNING or TERMINATED, but never IDLE.
Class task is derived from sched and provides the basic functionality
of user tasks. No object of type task may be instantiated. All user
tasks must be derived directly from class task; that is, you may not
further derive from a user task type. Examples:
task mytask; // ERROR: cannot create a task object
class consumer : public task { ... } // OK
class glutton : public consumer { ... } // ERROR: cannot derive
again
The first error is diagnosed by the compiler, since the task construc‐
tor is protected. The error in the third line cannot be diagnosed, but
the program will not work.
A task may not terminate by simply exiting the constructor, nor may the
function return mechanism be used to return a value from a task. A
task must instead set its return value by calling either task::resul‐
tis() or task::cancel(). This puts the task in the TERMINATED state.
Other tasks may retrieve the result value by calling task::result(),
which will suspend the calling task until the queried task has termi‐
nated. Even if a task does not need to return a value, it must call
resultis() or cancel() to ensure the task is properly terminated before
it is destroyed.
The task constructor takes three optional arguments: a name, a mode,
and a stack size.
The name should be a character string allocated statically or on the
heap, not a character array local to some function. The name appears
only in printouts, and has no effect on the task operation.
The mode specifies what sort of stack the task will use. The mode may
be DEDICATED (the default) or SHARED. Usually you want a dedicated
stack, meaning the task has a reserved area of memory for its own
stack. Shared stacks are expensive, because tasks may need to swap
their own stacks between the shared area and a save area as they become
active and inactive. If you have hundreds of small tasks, it makes
sense for them to share stack space.
The stack size argument specifies the maximum amount of stack space the
task may use. The default is 3000 bytes. There is no foolproof test
for overflowing the stack space, and the stack cannot be dynamically
expanded. The stack size is checked when a task is swapped in, and
overflow is a fatal error. If the stack overflows during execution due
to depth of function call nesting and amount of local variable storage,
this may cause bizarre program behavior or a crash.
When you derive a class from task, its constructor becomes a new task
which runs along with others which may already exist. Function main()
becomes a task with the creation of the first task in the system. If
several tasks are started sequentially in main(), the second task can‐
not be created until the first becomes blocked and main() regains con‐
trol to create a new task, and so on for additional tasks. This also
means that main() must invoke resultis() or cancel() after starting all
its tasks, so that it doesn't exit and terminate the entire program
while tasks are running.
Any task constructor may itself create new tasks.
Class object
Class object has only one constructor, which takes no arguments.
object o;
Constructs an object o (which is illegal, since the constructor
is protected), which is not on any list.
object *p = o.o_next;
An object may be on at most one list (run chain, queue, etc) at
at time. The next object on the list is pointed to by public
data member o_next. This member will be zero if there is no
next object, and, in particular, if the current object is not on
any list.
objtype t = p->o_type();
This virtual function returns the enumeration constant of type
objtype corresponding to the type of object which p points to.
int i = p->pending();
Returns non-zero (true) if the object pointed to by p is not
ready. Derived classes must define a suitable version of this
virtual function if they are to be waited on. The default ver‐
sion in class object always returns true (not ready). The other
predefined classes have their own versions of this function.
o.print(how);
Prints basic information about the object pointed to by p on
stdout (not cout). If the first int parameter, how, has bit
VERBOSE set, prints information about all the tasks on the
object's remember chain. The second argument is for internal
use and defaults to zero. This virtual function is normally
called by the print() functions from derived classes.
o.alert()
Scans the remember chain of task o, changes each task from IDLE
to RUNNING mode and puts it on the run chain. When a pending
object is waited on, the waiting task is made IDLE, taken off
the run chain, and put on the object's remember chain. When the
object becomes no longer pending, this function is called auto‐
matically by the predefined classes to wake up all the waiting
tasks.
o.forget(p)
Removes all instances of the task pointed to by p from the
remember chain of task o.
o.remember(p)
Adds the task pointed to by p to the remember chain of task o.
int user_err_func(int, object*);
object::error_fct = user_err_func;
Allows the user a measure of control over error recovery. If a
user error function is defined, it must take an int parameter
which is one of the error numbers in the DIAGNOSTICS section
below, and a pointer to an object. The latter will be the
object which called function object::task_error(), or which is
otherwise responsible for the error, as explained below. Static
data member object::error_fct may be set to point to such a user
error function. If it is set, task_error() will call the user
function. If the user function returns zero, task_error() will
return, and the operation which resulted in an error will pre‐
sumably be retried. If the user function returns non-zero,
task_error() will call exit().
int i = o.task_error(err, p);
int i = object::task_error(err, p);
This static member function is called by task system functions
when a run-time error occurs. The first int parameter is one of
the error codes listed in the DIAGNOSTICS section below. The
second parameter is intended to be the object which called the
function, or may be zero if there is no identifiable object
responsible. If static data member error_fct is zero,
task_error() prints a message on stderr (not cerr) and calls
exit(err). If error_fct is non-zero, the user function is
called with parameters err and p. If the user function returns
0, then so does task_error() . Otherwise, it calls exit(err) as
above. If this function returns 0, its caller is intended to
retry the operation which failed.
task *tp = o.this_task();
task *tp = object::this_task();
This static member function returns a pointer to the task which
is currently running.
Class Sched
Class sched provides the basic scheduling functions, and classes task
and timer are each derived from it. No object of this class may be
created; it serves only as a base class.
sched s;
Constructs an object s of type sched (which is illegal, since
the constructor is protected). The constructor has no argu‐
ments. The object is initialized to be IDLE and have no delay.
task* tp = ...;
sched::clock_task = tp;
If static data member sched::clock_task is non-zero, the task it
points to will be scheduled before any other task each time the
clock advances. When the current task, if any, is blocked, this
clock task will be resumed. The clock task must be IDLE when it
is resumed, and may place itself in that state by calling
task::sleep().
void user_exit_func(void);
sched::exit_fct = user_exit_func;
You may declare a function taking no parameters and returning
void and assign it to static data member sched::exit_fct. This
function will then be called just before final exit from the
task system.
int i = s.pending();
The version of this function for class sched returns non-zero
(true) if the object (a task or timer) is TERMINATED, and zero
(false) otherwise.
s.print(how);
Prints data about the sched portion of the object on stdout (not
cout). The first int parameter, how, is passed to
object::print(). The second argument is for internal use and
defaults to zero. This virtual function is normally called by
the print() functions from derived classes.
tp->setwho(objp)
This virtual function is provided for classes task and timer.
See the writeup for those classes. It is an error to call this
version.
s.cancel(val);
Puts task or timer s in the TERMINATED state, and uses int value
val as its returned value. Unlike resultis(), cancel() does not
suspend the caller. This enables one task to terminate another
while retaining control.
int i = s.dont_wait();
Returns the difference between the number of times keep_wait‐
ing() and the number of times dont_wait() have been called, not
counting this call. The count is then decremented. This count
is kept in a static variable, so it represents the system-wide
difference. The count is supposed to represent the number of
objects which are waiting for external events. See keep_wait‐
ing() below.
sched *tp = s.get_priority_sched();
A special system task, the interrupt alerter, is priority-sched‐
uled when a signal has occurred which was being waited for.
This function returns a pointer to that task if it has been
scheduled, zero otherwise. See also interrupt(3CC4).
int i = s.keep_waiting();
Returns the difference between the number of times keep_wait‐
ing() and the number of times dont_wait() have been called,
including this call. That is, the count is first incremented.
This count is kept in a static variable, so it represents the
system-wide difference. The count is supposed to represent the
number of objects which are waiting for external events. In
particular, the constructor for class Interrupt_handler calls
keep_waiting() and the destructor calls dont_wait(). The sched‐
uler uses this information so the task system doesn't exit when
interrupt handlers are active.
statetype t = s.rdstate();
Returns the state of task or timer s: RUNNING, IDLE, or TERMI‐
NATED.
long l = s.rdtime();
Returns the simulated time at which task or timer s is scheduled
to run.
int i = s.result();
Returns the ``result'' of task s, which is set by sched::can‐
cel(), task::cancel(), or task::resultis(). If task s has not
terminated, the calling task will be suspended until it does
terminate. It is an error for a task to call result() on
itself.
long l = s.getclock();
long l = sched::getclock();
Returns the value of the current simulated time.
sched* p = s.get_run_chain();
sched* p = sched::get_run_chain();
Returns a pointer to the run chain, the list of all tasks and
timers which are ready to run. These objects are linked through
the o_next field.
int i = s.get_exit_status();
int i = sched::get_exit_status();
When the task system exits normally (not because of a call to
task_error()), the value last given to set_exit_status() is
passed to the system exit() routine. This is by default zero,
the indicator of succesful completion. Function get_exit_sta‐
tus() returns the value currently slated to be passed to exit().
s.set_exit_status(i);
sched::set_exit_status(i);
The int value i is saved, and will be passed to the system func‐
tion exit() if the task system terminates normally (not because
of a call to task_error()). If set_exit_status() is never
called, that value will be zero, the indicator of succesful com‐
pletion. Otherwise, the last value passed to set_exit_status()
will be used.
s.setclock(l);
sched::setclock(l);
Sets the simulated clock to long value l. System time starts at
zero by default. It is an error to call this function more than
once.
Class Task
task t(name, mode, size);
Constructs a task. The parameters are described above. As also
noted above, the constructor is protected, and thus task may
serve only as a base class for user tasks.
task *tp = t.t_next;
The next task on the master list of all tasks. See
get_task_chain() below.
unsigned char* p = t.t_name;
A pointer to the name of the task as assigned by the task con‐
structor.
objtype o = t.o_type();
This virtual function defined in object returns the kind of
object. For a task, the object kind is TASK.
t.print(how);
Prints data about task t on stdout (not cout). The first int
parameter, how, may have any combination of the VERBOSE and
CHAIN bits set. If VERBOSE is set, addtional information is
printed. If CHAIN is set, information on every task in the sys‐
tem is printed. This parameter is passed to sched::print().
The second argument is for internal use and defaults to zero.
t.setwho(objptr);
This virtual function remembers the object pointed to by objptr
as the object which alerted task t. The intent is that task t
was waiting on object *objptr, and when it went from pending to
ready, the object alerted this task and changed its state from
IDLE to RUNNING. The object which was remembered may be
retrieved with who_alerted_me().
t.cancel(val);
Puts task t in the TERMINATED state, and uses int value val as
its returned value. Unlike resultis(), cancel() does not sus‐
pend the caller. This enables one task to terminate another
while retaining control. See also resultis().
t.delay(n);
Suspends task t for n simulated time units, leaving it RUNNING.
The RUNNING task with the least delay left gets control, and the
simulated clock is advanced to its continuation time. When the
clock has advanced by n units, task t will continue execution.
The use of delay() is the only way to advance the clock.
int i = t.preempt();
Suspends RUNNING task t, making it IDLE. It returns the number
of time units left before t was scheduled to continue execution.
It is an error to call preempt() for an IDLE or TERMINATED task.
t.resultis(val);
Puts task t in the TERMINATED state, and uses int value val as
its returned value. This value may be examined by calling
t.result(). Any tasks which have called result() on task t and
are thus waiting for the task to terminate will be alerted. A
task may not return a value via the ordinary function return
mechanism, but must call either resultis() or cancel() to termi‐
nate. Every task is pending until it is terminated.
t.sleep(objptr);
Unconditionally suspends task t, making it IDLE. The optional
argument points to an object that will ``remember'' task t and
alert it when the object becomes ready. Unlike wait(), sleep()
does not first check the object to see whether it is pending;
the task is always suspended.
t.wait(objptr);
Suspends task t (makes it IDLE) if the object pointed to by
objptr is pending. In this case, t will be alerted by the
object (made RUNNING and put back on the run chain) when the
object becomes ready. If the object is not pending, task t is
not suspended, but retains control.
int which = t.waitlist(op1, op2, ... , NULL);
This function takes a list of pointers to objects, terminated by
a null pointer. If all of the objects pointed to are pending,
task t is suspended until one of them becomes ready. Waitlist
returns when at least one of the tasks is ready, returning the
index (starting from 0) in the list of objects that caused the
return. Other objects in the list might also be ready at this
point. If none of the objects are pending as of the call to
waitlist(), task t is not suspended and waitlist() returns imme‐
diately.
int which = t.waitvec(objarray);
This works in exactly the same way as waitlist(), except that
this function takes an an array of pointers to objects instead
of a variable-length list. The array is terminated by a null
pointer.
task *tp = t.get_task_chain();
task *tp = task::get_task_chain();
This static member function returns the head of the list of all
tasks. Every task in the system, RUNNING, IDLE, and TERMINATED,
is on this list, linked via the t_next data member.
Class Timer
timer tm(d);
Constructs a timer which will expire in d simulated time units.
The timer is placed on the run chain.
objtype o = tm.o_type();
This virtual function defined in object returns the kind of
object. For a timer, the object kind is TIMER.
tm.print(how);
Prints data about timer tm on stdout (not cout). The first int
parameter, how, is passed to sched::print(). The second argu‐
ment is for internal use and defaults to zero.
tm.setwho(objptr);
This virtual function has no effect for timers, since they can‐
not be alerted.
tm.reset(d);
Resets the delay of timer tm to d simulated time units. A timer
may be reset even if it has been terminated. This means that a
timer may be reused; they need not be continually created and
destroyed.
DIAGNOSTICS
When the task system detects a run time error, it calls
object::task_error() as described above. The following table lists the
possible error values, associated messages, and meanings.
┌─────────────────┬──────────────────────────┬─────────────────────────┐
│ Error Name │ Message │ Explanation │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_ERROR │ (no message) │ Undefined error. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_OLINK │ object::delete(): │ Attempt to destroy │
│ │ has chain │ an object which │
│ │ │ ``remembers'' one │
│ │ │ or more objects. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_ONEXT │ object::delete(): on │ Attempt to destroy │
│ │ chain │ an object which is │
│ │ │ on a list. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_GETEMPTY │ qhead::get(): empty │ Attempt to get from │
│ │ │ an empty queue. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_PUTOBJ │ qtail::put(): object │ Attempt to put an │
│ │ on other queue │ object on a queue │
│ │ │ which is already on │
│ │ │ a queue. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_PUTFULL │ qtail::put(): full │ Attempt to put an │
│ │ │ object on a full │
│ │ │ queue. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_BACKOBJ │ qhead::putback(): │ Attempt to put back │
│ │ object on other │ onto a queue an │
│ │ queue │ object which is │
│ │ │ already on a queue. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_BACKFULL │ qhead::putback(): │ Attempt to put back │
│ │ full │ an object to a full │
│ │ │ queue. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_SETCLOCK │ sched::setclock(): │ Attempt to set the │
│ │ clock!=0 │ clock twice. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_CLOCKIDLE │ sched::schedule(): │ The clock task was │
│ │ clock_task not idle │ not IDLE when it │
│ │ │ was scheduled to be │
│ │ │ run. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_RESTERM │ sched::insert(): │ Attempt to resume a │
│ │ cannot schedule ter‐ │ TERMINATED task. │
│ │ minated sched │ │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_RESRUN │ sched::schedule(): │ Attempt to resume a │
│ │ running │ RUNNING task. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_NEGTIME │ sched::schedule(): │ Attempt to set the │
│ │ clock<0 │ clock to a negative │
│ │ │ value or use a neg‐ │
│ │ │ ative delay. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_RESOBJ │ sched::schedule(): │ Attempt to resume │
│ │ task or timer on │ task already on a │
│ │ other queue │ queue. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_HISTO │ histogram::his‐ │ Inconsistent or │
│ │ togram(): bad argu‐ │ illegal arguments │
│ │ ments │ to histogram con‐ │
│ │ │ structor. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_STACK │ task::task() or │ The run time task │
│ │ task::resume(): │ stack has over‐ │
│ │ stack overflow │ flowed. │
├─────────────────┼──────────────────────────┼─────────────────────────┤
│ E_STORE │ new: free store │ No more free store │
│ │ exhausted │ available for task │
│ │ │ bookkeeping. │
continued...──────┴──────────────────────────┴─────────────────────────┘
┌─────────────────┬───────────────────────────┬────────────────────────┐
│ Error Name │ Message │ Explanation │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_TASKMODE │ task::task(): bad │ Illegal mode argu‐ │
│ │ mode │ ments for the task │
│ │ │ constructor. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_TASKDEL │ task::~task(): not │ Attempt to destroy │
│ │ terminated │ a non-TERMINATED │
│ │ │ task. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_TASKPRE │ task::preempt(): not │ Attempt to preempt │
│ │ running │ a non-RUNNING task. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_TIMERDEL │ timer::~timer(): not │ Attempt to destroy │
│ │ terminated │ a non-TERMINATED │
│ │ │ timer. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_SCHTIME │ sched::schedule(): │ Run chain cor‐ │
│ │ runchain corrupted: │ rupted, not in time │
│ │ bad time │ order. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_SCHOBJ │ sched object used │ Attempt to use a │
│ │ directly (not as │ sched object rather │
│ │ base) │ than a derived task │
│ │ │ or timer. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_QDEL │ queue::~queue(): not │ Attempt to destroy │
│ │ empty │ a non-empty queue. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_RESULT │ task::result(): │ A task attempted to │
│ │ thistask->result() │ call result() on │
│ │ │ itself. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_WAIT │ task::wait(): wait │ A task attempted to │
│ │ for self │ wait on itself. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_FUNCS │ FrameLayout::Frame‐ │ (not used) │
│ │ Layout(): function │ │
│ │ start │ │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_FRAMES │ FrameLayout::Frame‐ │ (not used) │
│ │ Layout(): frame size │ │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_REGMASK │ task::fudge_return(): │ (not used) │
│ │ unexpected register │ │
│ │ mask │ │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_FUDGE_SIZE │ task::fudge_return(): │ (not used) │
│ │ frame too big │ │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_NO_HNDLR │ signal_handler - no │ A signal occurred │
│ │ handler for signal │ but no handler was │
│ │ │ registered. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_BADSIG │ illegal signal number │ Attempt to register │
│ │ │ an illegal signal │
│ │ │ number. │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_LOSTHNDLR │ Interrupt_handler:: │ Internal error: a │
│ │ ~Interrupt_handler(): │ signal handler was │
│ │ signal handler not on │ lost. │
│ │ chain │ │
├─────────────────┼───────────────────────────┼────────────────────────┤
│ E_RUNCHAIN │ sched::sched(): run │ All tasks have ter‐ │
│ │ chain empty │ minated and there │
│ │ │ are no interrupt │
│ │ │ handlers. │
└─────────────────┴───────────────────────────┴────────────────────────┘
NOTE
As of the date of this release, the coroutine library will not be sup‐
ported beyond the current version.
SEE ALSOtask.intro(3CC4), interrupt(3CC4), queue(3CC4), tasksim(3CC4),
exit(3C).
08 August 2000 TASK(3CC4)