Subversion Repositories shark

Rev

Rev 1676 | Blame | Compare with Previous | Last modification | View Log | RSS feed

%----------------------------------------------------------------------------
\chapter{System Start-up and Termination}
%----------------------------------------------------------------------------

Each S.Ha.R.K. application starts as a sequential C program, with
the classical main funcion. The multitasking environment \index{system initialization}
is already initialized when the application starts. From the main
task you can call any system primitive.

The system finishes when a sys\_end or sys\_abort function is called,
or when the last user task is terminated. For more information, see
\emph{The Generic Kernel Internals} chapter of the S.Ha.R.K. Kernel
Architecture Manual.

The \texttt{sys\_atrunlevel()} primitive\index{sys\_atexit()} allows
to post some handlers, which are automatically executed by the kernel
when it changes runlevel. Such functions can be issued either in the
target execution environment (generally MS-DOS) or just before terminating
the S.Ha.R.K. kernel, depending on the third argument of the primitive.
The handlers posted through \texttt{sys\_atrunlevel()
} may also be
called on a kernel abort due to fatal errors.

%----------------------------------------------------------------------------
\section{Initialization File\label{sec:InitFile}}
%----------------------------------------------------------------------------

When the system starts, one of the things to be done before going
in multitasking mode is to initialize the devices, the resources and
the schedulers which will be used by the application. To do that,
the Kernel calls the \_\_kernel\_register\_levels\_\_ function, that
usually registers the following modules (see the S.Ha.R.K. Kernel
architecture Manual for more details):

\begin{description}
\item [Scheduling~Modules]A scheduling module implements a particular
Scheduling Algorithm (for example EDF, RM, Round Robin, and so on).
\item [Resource~Modules]A resource module implements a shared resource
access protocol (for example the semaphores, the mutexes, and so on).
\item [Other~devices]Such for example the File System, and other devices
that need to be initialized when the Multitasking Mode is not started
yet.
\end{description}

The function returns a TICK value (in microseconds) that is the time
that will be used for programming the periodic timer interrupt of
the PC. If a value of 0 is returned, the one-shot timer is used instead
(see the OSLib documentation for more informations). Typical return
values range from 250 to 2000 microseconds.

Here is a typical initialization function:

\begin{verbatim}
TIME __kernel_register_levels__(void *arg) {
    struct multiboot_info *mb = (struct multiboot_info *)arg;
    LEVEL EDF_level;

    EDF_level = EDF_register_level(EDF_ENABLE_ALL);
    RR_register_level(RRTICK, RR_MAIN_YES, mb);
    CBS_register_level(CBS_ENABLE_ALL, EDF_level);
    dummy_register_level();

    SEM_register_module();
    CABS_register_module();

    return 1000;
}
\end{verbatim}

As you can see, the system initialization function registers an EDF,
a Round Robin and a CBS module. Then, It register a dummy Module (that
usually is the last of the Scheduling Modules). For more informations
about the Scheduling policies, see Section \ref{sec:sched}. Finally,
Semaphores and CABS are registered, and a value of 1 ms Tick time
is returned to initialize the PC's real-time clock.

For a survey of the architecture of the Scheduling Modules and the
Resource Modules see theKernel Overview Chapter of the S.Ha.R.K. Kernel
Architecture Manual. A set of Initialization functions can be found
on the kernel/init directory of the S.Ha.R.K. source tree. An explanation
of each registration function for each Module can be found in the
S.Ha.R.K. Module Repository Manual.

After the registration of the modules in the system, the Kernel switch
in Multitasking mode, and starts the execution of the handlers that
the modules have posted with the \texttt{sys\_atrunlevel} primitive.
Usually at least one Module will create and activate a task (for example,
the Round Robin Scheduling Module does that) that will start when
all the handlers will be processed. The body of that task is usually
called \_\_init\_\_() and provides an initialization for the most
commonly used devices (such the keyboard, and so on) and modules.
As the last thing, the function simply call the main() function, that
is, the user application starts. A sample of a typical \_\_init\_\_()
function is showed below:

\begin{verbatim}
TASK __init__(void *arg) {
    struct multiboot_info *mb = (struct multiboot_info *)arg;

    HARTPORT_init();
    __call_main__(mb);
    return (void *)0;
}
\end{verbatim}

The source code of the \_\_init\_\_() function is usually inserted
in the initialization file after the \_\_kernel\_register\_levels\_\_
function. For more information on \_\_call\_main\_\_ see the \texttt{include/kernel/func.h}
include file.

Using the new driver layer the \_\_init()\_\_ function slightly change.
First is executed the HARTPORT\_init function that initialize the
Hartik Port layer (if required), then a task that close all drivers
is created. The next step is the initialization of all used drivers,
followed by the registration of the shutdown task that will be executed
during the system shutdown procedure. At the end the function 'main'
is executed. A tipical example with the new \_\_init()\_\_ function
is:

\begin{verbatim}
TASK __init__(void *arg) {
    struct multiboot_info *mb = (struct multiboot_info *)arg;

    HARTPORT_init();

    /* Create the shutdown task. */
    /* It will be activated at RUNLEVEL SHUTDOWN */
    set_shutdown_task();

    /* Init the drivers */
    device_drivers_init();

    /* Set the shutdown task activation */
    sys_atrunlevel(call_shutdown_task, NULL, RUNLEVEL_SHUTDOWN);
    __call_main__(mb);
   
    return (void *)0;
}
 \end{verbatim}

ATTENTION! In some initialization files the function that activate
the shutdown task is in the form:

\begin{verbatim}
#define SHUTDOWN_TIMEOUT_SEC 3

void call_shutdown_task(void *arg) {
    struct timespec t;
   
    sys_gettime(&t);
    t.tv_sec += SHUTDOWN_TIMEOUT_SEC;

    /* Emergency timeout to exit from RUNLEVEL_SHUTDOWN
    kern_event_post(&t,(void *)((void *)sys_abort_shutdown), (void *)0);

    task_activate(shutdown_task_PID);
}
\end{verbatim}

This implementation say that the task has 3 seconds to perform drivers
stop. After that interval the system is forced to close even is some
drivers are not closed. If a longer time is needed a greater value
for \texttt{SHUTDOWN\_TIMEOUT\_SEC} constant must be used. If a shutdown
without the timer is preferred the function could be in the simpler
form:

\begin{verbatim}
void call_shutdown_task(void *arg) {
    task_activate(shutdown_task_PID);
}
\end{verbatim
}

%----------------------------------------------------------------------------
\subsection{Information about the dependencies among modules}
%----------------------------------------------------------------------------

Some modules, drivers and ports need to use the semaphores managed
by specific resource sharing modules. In that case, the modules, drivers
and ports expect the semaphores to be initialized by the application
into the initialization file. Here there is a brief list of the modules
which require other module initialization \footnote{All those dependancies wish to be removed.}.

The HARTIK ports (module HARTPORT) use the semaphores, so you must
include and initialize the semaphores if your application or a module
used by your application uses the HARTIK ports.

The HARTIK ports are used by the following modules:

\begin{itemize}
\item ports/first
\item drivers/net
\item drivers/input
\item drivers/oldchar
\end{itemize}
The semaphores are also used by:

\begin{itemize}
\item ports/tftp
\item drivers/oldsnd.
\end{itemize
}

%----------------------------------------------------------------------------
\section{System primitives}
%----------------------------------------------------------------------------

Here is a list of primitives whose use is related to the system initialization.

%----------------------------------------------------------------------------
\begin{intest}
SYS\_ATRUNLEVEL\index{SYS\_ATRUNLEVEL}
\end{intest}

\begin{description}
\item [\texttt{int}]\texttt{sys\_atrunlevel(void (*func\_code)(void *),void
*parm, BYTE when);}
\item [Description:]The Generic Kernel supports the specification of the
functions to be called at system initialization and termination. These
functions can be registered through this system primitive; the parameters
for that function are:
\item [\texttt{f}]the function to be registered;
\item [\texttt{p}]the parameter to be passed to function \texttt{f} when
the function will be called;
\item [\texttt{when}]is the situation in witch that function will be called.
The correct values are the following:

\begin{description}
\item [\texttt{RUNLEVEL\_INIT}]Used when programming Modules;
\item [\texttt{RUNLEVEL\_SHUTDOWN}]The function will be called after a
call to \texttt{sys\_abort} or \texttt{sys\_end}; The system is still
in multitasking mode;
\item [\texttt{RUNLEVEL\_BEFORE\_EXIT}]The function will be called when
the Kernel exits from multitasking mode;
\item [\texttt{RUNLEVEL\_AFTER\_EXIT}]The function is called before the
system hangs (or returns to the host OS, if the proprietary extender
is used).

\end{description}

It is also possible to specify with an OR operator a flag \texttt{NO\_AT\_ABORT}
that disables the call to the functions if the system is exiting with
a \texttt{sys\_abort} function.

\end{description}

You can post at most \texttt{MAX\_RUNLEVEL\_FUNC} functions. See the
S.Ha.R.K. Kernel Architecture Manual for more details.

\begin{description}
\item [See]\textbf{also}: \texttt{sys\_init()}, \texttt{sys\_end()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
EXIT\index{exit}
\end{intest}

\begin{description}
\item [\texttt{void exit(int status);}]
\item [Description:]This function call terminates the Kernel. In this phase,
the Kernel tries to correctly close all the initialized modules and
drivers. The functions eventually posted with the \texttt{sys\_at\_runlevel}
call are also executed.

If called inside an event or inside an ISR, it does return to the
caller. The system shutdown will start when all the current interrupts
have been serviced, and the system has been rescheduled. Otherwise,
this function follows the POSIX specification.
\item [See]\textbf{also}: \texttt{\_exit()}, \texttt{sys\_panic()}, \texttt{sys\_atrunlevel()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
\_EXIT\index{\_exit}
\end{intest}

\begin{description}
\item [\texttt{void \_exit(int status);}]
\item [Description:]Same as \texttt{exit()}. functions posted through \texttt{sys\_at\_runlevel}
with \texttt{NO\_AT\_ABORT} set or functions posted with \texttt{atexit()}
are not executed.
\item [See]\textbf{also}: \texttt{exit()}, \texttt{atexit()}, \texttt{sys\_panic()},
\texttt{sys\_atrunlevel()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
SYS\_PANIC\index{sys\_end}
\end{intest}

\begin{description}
\item [\texttt{void sys\_panic(const char * fmt, ...);}]
\item [Description:]This function call print a message then call sys\_abort(333).
\item [See]\textbf{also}: \texttt{sys\_abort()}, \texttt{sys\_end()}, \texttt{sys\_atrunlevel()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
SYS\_SHUTDOWN\_MESSAGE\index{sys\_\_shutdown\_message}
\end{intest}

\begin{description}
\item [\texttt{int sys\_shutdown\_message(char *fmt,...);}]
\item [Description:]This function call saves a message in a reserved area,
that will be printed at system shutdown. It does not end the system.
\item [See]\textbf{also}: \texttt{sys\_panic()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
SYS\_ABORT\_SHUTDOWN\index{sys\_abort\_shutdown}
\end{intest}

\begin{description}
\item [\texttt{int sys\_abort\_shutdown(int err);}]
\item [Description:]This function will force the system to end the \texttt{SHUTDOWN}
runlevel if there are system tasks which cannot be stopped. If called
when the system is still in the \texttt{RUNLEVEL\_RUNNING} runlevel,
the function behaves like \texttt{exit()}. If called inside an OSLib
event or inside an IRQ,it does return to the caller. The system shutdown
will start when all the current interrupts have been serviced, and
the system has been rescheduled. Otherwise, this function does not
return.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
SYS\_SET\_REBOOT\index{sys\_set\_reboot}
\end{intest}

\begin{description}
\item [\texttt{int sys\_set\_reboot(int mode);}]
\item [Description:]This function sets the reboot \texttt{mode}, which
specifies what will happen after the system end. \texttt{mode} options
are:
\item [EXIT\_MODE\_HALT:]the system will call the halt (\texttt{HLT}) instruction.
\item [EXIT\_MODE\_COLD:]the system will perform the cold reboot (slow
reboot).
\item [EXIT\_MODE\_WARM:]the system will perform the warm reboot (fast
reboot).
\item [EXIT\_MODE\_REAL:]the system will return to the real mode (\textbf{default
selection}).
\end{description
}

%----------------------------------------------------------------------------
\chapter{Task Management}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\section{Task Model}
%----------------------------------------------------------------------------

S.Ha.R.K. tasks are defined using standard C functions which return
a \texttt{void *} type \footnote{for readability, that type has been called TASK.} and can have one void * argument, which is passed when the task
is created. A task is identified by a system-wide unique process identifier
(\texttt{PID}) and a consecutive number \footnote{a task with PID p has a consecutive number that is proc\_table{[}p{]}.task\_ID.}.

A task has tipically a set of Quality of Service requirements that
need to be fullfilled by the Kernel. The kernel uses its registered
Scheduling Modules to meet the QoS required by a specified task. The
QoS reuired is specified at creation time through a Task Model, that
is passed to the task creation primitives.

A Task, can be \textit{Periodic} or \textit{Aperiodic}. Periodic tasks
are automatically activated by the kernel with a desired period, whereas
aperiodic tasks can either be activated by an explicit system call
or upon the occurrence of a desired event.

The typical task code consists of an optional initialization of local
variables and resources, followed by a (finite or infinite) loop,
representing the task's body. The last instruction of such a loop
must be the primitive \texttt{task\_endcycle()}\index{task\_endcycle()}
or the primitive \texttt{task\_sleep()} \index{task\_sleep()} which
signals the end of a generic job.

The task can access a local and a global context by following the
C scoping rules; the local context is defined by the local variables
and the single optional input argument. The following example shows
a typical task code fragment:

\begin{verbatim}
void *my_task(void *par) {

    /* Local Context*/
    int a, b, c;

    /* Initialization */
    b = c = (int)par + 1;
    a = (int)par / 2;
    ...

    while (1) {

        /* Body here!*/
        ...

        task_endcycle();
    }
}
\end{verbatim}

\noindent \texttt{my\_task()} has just one integer input argument
(passed through the void * parameter) and three local variables\index{local task context}.
The life-cycle of the local variables is the same as the task one,
since they are allocated on the task's stack. Obviously they retain
their values between two consecutive jobs.

\noindent One of the most important parameters for a real-time task
$\tau_{i}$ is the \emph{deadline}, defined as the maximum time allowed
for a task job to terminate. More precisely, we distinguish between
the \emph{absolute deadline} (denoted by $d_{i}$) specified with
respect to time 0, and the \emph{Relative Deadline} (denoted by $D_{i}$)
specified with respect to the activation time $r_{i,k}$ of the $k$-th
job of task $\tau_{i}$. We have that:
\[
d_{i}=r_{i,k}+D_{i}.
\
]

\noindent Tasks can also have different level of criticality, for
example:

\begin{itemize}
\item \texttt{HARD}\index{HARD
} tasks are the most critical in the system.
For this reason, they are subjected to a guarantee algorithm at creation
time. The system enforces a strict compliance to the deadline constraint
for this kind of tasks%
\footnote{The guarantee algorithm tries to verify that both the newly activated
hard task and the previously existing ones will finish within their
deadlines%
}. If a hard deadline is missed, the system raises an exception which,
by default, results in the program termination. Recovery actions can
be programmed for this kind of exception (as shown below).
\item \texttt{SOFT}\index{SOFT} tasks can miss some deadline, and are scheduled
in order not to jeopardize HARD tasks' schedulability. This is done
through a service mechanism (see \ref{sec:sched}) which guarantees
each soft task a predefined bandwidth (i.e., a fraction of processor
utilization) while preserving the guarantee performed on hard tasks.
\item \texttt{NRT}\index{NRT} (Non Real-Time) tasks are scheduled in background
according to their relative fixed priority. Typically, they are used
for monitoring or debugging purposes.
\end{itemize}
The Task criticality, periodicity and the deadlines are coded into
the Task Model that is passed to the creation primitive. Each new
Scheduling Module can use its own Task Model to include the specific
task QoS requirements.

Each task can be in one of a set of states; the states that a task
can be in depend on each particular Module. For example, typical Task
states can be:

\begin{itemize}
\item \texttt{\textbf{EXE}}\index{EXE}: at any time, in the system there
is only one task in the EXE state, and it is the task actually executing.
\item \texttt{\textbf{READY}}\index{READY}: it includes all active tasks
ready to execute, except for the currently running task.
\item \texttt{\textbf{SLEEP}}\index{SLEEP}: it includes all aperiodic tasks
which terminated a job and are waiting for the next activation. Moreover,
each created task (periodic or aperiodic) that has not been activated
is put in the SLEEP state.
\item \texttt{\textbf{IDLE}}\index{IDLE}: is the state of those periodic
tasks which terminated a job and are waiting for the next activation.
\item \texttt{\textbf{BLOCKED}}\index{BLOCKED}: it includes all the tasks
blocked on a semaphore.
\end{itemize
}

%----------------------------------------------------------------------------
\section{The scheduling policy%
\footnote{This section is derived from the Kernel overview chapter of the S.Ha.R.K.
Architecture Manual.%
}\label{sec:sched} }
%----------------------------------------------------------------------------

The S.Ha.R.K. scheduling architecture is based on a \emph{Generic
Kernel}, which does not implement any particular scheduling algorithm,
but postpones scheduling decisions to external entities, the \emph{scheduling
modules
}. External modules can implement periodic scheduling algorithms,
soft task management through real-time servers, semaphore protocols,
and resource management policies.

The Generic Kernel provides the mechanisms used by the modules to
perform scheduling and resource management thus allowing the system
to abstract from the specific algorithms that can be implemented.
The Generic Kernel simply provides the primitives without specifying
any algorithm, whose implementation resides in external modules, configured
at run-time with the Initialization function.

Scheduling Modules are used by the Generic Kernel to schedule tasks,
or serve aperiodic requests using an aperiodic server. The Scheduling
Modules are organized into levels, one Module for each level. These
levels can be thought as priority scheduling levels (their priority
correspond to the order which they appear in the Initialization function).
When the Generic Kernel has to perform a scheduling decision, it asks
the modules for the task to schedule, according to fixed priorities:
first, it invokes a scheduling decision to the highest priority module,
then (if the module does not manage any task ready to run), it asks
the next high priority module, and so on. The Generic Kernel schedules
the first task of the highest priority non empty module's queue.

In this way, the Scheduling policy can be tuned simply modifying the
Initialization function. The standard distribution of the S.Ha.R.K.
Kernel includes a set of predefined Initialization functions that
can be used when developing a new application. For more informations
see the S.Ha.R.K. Module Manual, where each Scheduling Modules and
each predefined initialization functioon are described in detail.

%----------------------------------------------------------------------------
\section{Task Creation\index{task creation}}
%----------------------------------------------------------------------------

In order to run a S.Ha.R.K. task, three steps have to be performed:
parameters definition, creation, and activation. To improve the system
flexibility, each task can be characterized by a large number of parameters,
most of which are optional. For this reason, a set of structures derived
from \texttt{TASK\_MODEL}\index{MODEL} structure have been introduced
to simplify the parameters' definition phase. The first thing to do
in order to define a task is to declare a Model variable and initialize
it using the pmacro provided (see the S.Ha.R.K. Module Manual for
more informations).

\noindent Once the task's parameters have been set, the task can be
created using the \texttt{task\_create} or the \texttt{task\_createn
}
system call.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_CREATEN\index{task\_createn()} and TASK\_CREATE\index{task\_create()}
\end{intest}

\begin{description}
\item [\texttt{PID task\_createn(char *name, TASK (*body)(),
TASK\_MODEL *m, ...);}
]
\item [\texttt{PID task\_create(char *name, TASK (*body)(),
TASK\_MODEL *m, RES\_MODEL *r);}
]
\item [Description:]\texttt{task\_createn} creates a new task. \texttt{name}
is a pointer to a string representing the task name; \texttt{body()}
is a pointer to the task body (i.e. the name of the C function containing
the task code); \texttt{m} specifies the Model that contain the QoS
specification of the task (the value can not be equal to NULL). Then,
follow a list of Resource Models terminated with NULL (see the S.Ha.R.K.
Module Manual for the available Task Models and Resource Models).
\texttt{task\_create} is a redefinition of \texttt{task\_createn}
that can be used when there is at least one Resource Model to be passed
to the creation primitive.
\item [Return]\textbf{value}: The function returns the identifier of the
newly created task, or -1 if the task creation fails (in this case
the \texttt{errno()} system call can be used to determine the error's
cause).
\item [See]\textbf{also}: \texttt{task\_activate()}, \texttt{task\_kill()}.
\item [Example]~
\end{description}

\begin{verbatim}
int main(int argc, char **argv) {
    HARD_TASK_MODEL m;
    hard_task_default_model(m);
    hard_task_def_wcet(m,ASTER_WCET);
    hard_task_def_mit(m,10000);
    hard_task_def_group(m,1);
    hard_task_def_ctrl_jet(m);

    p1 = task_create("Aster", aster, &m, NULL);
    if (p1 == -1) {
        perror("Error: Could not create task <aster> ...");
        exit(1);
    }
}
\end{verbatim
}


%----------------------------------------------------------------------------
\section{Group Creation}
%----------------------------------------------------------------------------

Group creation is a feature provided by S.Ha.R.K. that allows a user
to create a set of tasks. The group creation differs from the creation
made by the task\_create and task\_createn primitives because in group
creation the acceptance test is done for the whole set of task (and
not for every task in sequence) only when every task which belong
to the set has been initialized in the system. After the acceptance
test, the user have to inquire the Scheduling Module to see the tasks
that have been accepted and successfully created in the system.

The primitives provided by S.Ha.R.K. to support group creation are:

\begin{itemize}
\item group\_create
\item group\_create\_accept
\item group\_create\_reject
\end{itemize}
The documentation about group creation can be found in the \emph{Group
creation HOWTO
} available on the S.Ha.R.K. website.

%----------------------------------------------------------------------------
\section{Task Activation and Termination}
%----------------------------------------------------------------------------

When a task is created, it is put in the \texttt{SLEEP} state, where
it is kept until activation, which can be triggered by an external
interrupt or by an explicit \texttt{task\_activate()} primitive).
Periodic jobs that complete execution are handled by the registered
Scheduling Modules (usually they are put in an \texttt{IDLE} state
or similar), from which they will be automatically re-activated by
the system timer. Aperiodic jobs that complete execution return to
the \texttt{SLEEP} state, where they wait for an explicit re-activation.

Some scheduling models (such as the EDF and RM modules) support release
offsets. In an offset is given in the task model, the \texttt{task\_activate()}
will put the task in the \texttt{IDLE} state, waiting for the first
release to occur.

A task can be destroyed using the \texttt{task\_kill()} system call,
that frees its descriptor. A task can kill itself using the \texttt{task\_abort()}
system call.

In S.Ha.R.K., tasks can be members of groups to allow simultaneous
activation or termination. A task can be put in a group through a
macro that works on the task model passed at task creation. The name
of the macro depends on the name of the Task Model used; usually its
name is like \texttt{XXX\_task\_def\_group(group\_number)}, where
XXX is the name of the task Model; the group\_number 0 indicates that
a task belongs to no groups.

Task cancellation, join, the cleanup handlers and the task specific
data works as the POSIX standard; only the name of the primitives
are changed from \texttt{pthread\_XXX} to \texttt{task\_XXX}. In any
case, the \texttt{pthread\_XXX} versions are available for POSIX compatibility.

\textbf{Warning}: \texttt{task\_kill()
} kills a task only if the cancellation
type of the task is set to asynchronous. If the cancellation type
is set to deferred, the task will terminate only when it reach a cancellation
point.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_ACTIVATE\index{task\_activate()}
\end{intest}

\begin{description}
\item [\texttt{int task\_activate(PID p);}]
\item [Description:]It activates task \texttt{p}. Usually the activation
will insert the task into the ready queue. (If the task has an offset,
the task will be put in the ready queue after the offset.) Returns
0 in case of success or -1 in case of error; the \texttt{errno} variable
is set to \texttt{EUNVALID\_TASK\_ID}.
\item [See]\textbf{also}: \texttt{task\_create(), task\_kill(), group\_activate()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_KILL\index{task\_kill()}
\end{intest}

\begin{description}
\item [\texttt{int task\_kill(PID p);}]
\item [Description:]It asks for a cancellation of the task p. It returns
-1 in case of error, 0 otherwise. If an error occurred, the errno
variable is set to \texttt{EINVALID\_KILL}. A task which has the NO\_KILL
flag set can not be killed. If the task has already been killed but
it is not died yet, the primitive does nothing. A task that has the
cancellation type set to asynchronous will die just when it will be
scheduled again by the system; instead, if the cancellation type is
set to deferred, the task will die only at the reaching of a cancellation
point. This function is the correspondent of the \texttt{pthread\_cancel()}
primitive.
\item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), group\_kill()}.
\end{description
}
\pagebreak

%----------------------------------------------------------------------------
\begin{intest}
TASK\_ABORT\index{task\_abort()}
\end{intest}

\begin{description}
\item [\texttt{void task\_abort(void *returnvalue);}]
\item [Description:]It aborts the calling task, removing it from the system.
If the task is joinable, the return value will be stored by the kernel
and given to any task that calls a task\_join primitive on the died
task.
\item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), task\_kill()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_BLOCK\_ACTIVATION\index{task\_block\_activation()}
\end{intest}

\begin{description}
\item [\texttt{int task\_block\_activation(PID p);}]
\item [Description:]It blocks all explicit activation of a task made with
\texttt{task\_activate} and \texttt{group\_activate}. The activations
made after this call are buffered (counted) in an internal counter.
It returns 0 in case of success or -1 in case of error. In the latter
case, \texttt{errno} is set to \texttt{EUNVALID\_TASK\_ID}. If the
activations were already blocked, it does nothing.
\item [See]\textbf{also}: \texttt{task\_unblock\_activation(), task\_activate()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_UNBLOCK\_ACTIVATION\index{task\_unblock\_activation()}
\end{intest}

\begin{description}
\item [\texttt{int task\_unblock\_activation(PID p);}]
\item [Description:]It unblocks the activations of a task after a call
to task\_block\_activation. After this call, the task can be explicitly
activated. It returns the number of buffered activations, or -1 if
an error occurred. If an error occurred, the errno variable is set
to EUNVALID\_TASK\_ID. If the activations were not blocked, it simply
returns 0. Note that the primitive simply returns the number of buffered
activations, \emph{without} activating the task.
\item [See]\textbf{also}: \texttt{task\_block\_activation(), task\_activate()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
GROUP\_ACTIVATE\index{group\_activate()}
\end{intest}

\begin{description}
\item [\texttt{int group\_activate(WORD g);}]
\item [Description:]It activates all tasks belonging to group \texttt{g}.
Returns 0 in case of success or -1 in case of error; the \texttt{errno}
variable is set to \texttt{EUNVALID\_GROUP}.
\item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), group\_kill()}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
GROUP\_KILL\index{group\_kill()}
\end{intest}

\begin{description}
\item [\texttt{void group\_kill(WORD g);}]
\item [Descrizione:]It kills all tasks belonging to group \texttt{g}. It
returns -1 in case of error, 0 otherwise. If an error occurred, the
errno variable is set to \texttt{EUNVALID\_GROUP}. The kill request
to a single task that belong to a group is done in a way similar to
that done in the primitive \texttt{task\_kill()}.
\item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), group\_activate()},
\texttt{task\_kill()}.
\end{description
}

%----------------------------------------------------------------------------
\section{Task Instances}
%----------------------------------------------------------------------------

S.Ha.R.K. supports the concept of instance for its task. A typical
task function is composed by an initialization part and a body part
that does the work for that the task was created; for example:

\begin{verbatim}
void *mytask(void *arg) {
    /* initialization part */

    for (;;) {
        /* body */
        ...

        task_endcycle();
    }
}
\end{verbatim}

In the example, the task will never terminate, and it also calls the
\texttt{task\_endcycle} primitive to say to the Kernel that the current instance
is terminated \footnote{The concept of instance is introduced into S.Ha.R.K.
because in that way the Kernel can directly support task Quality Of Service
parameters like deadlines, periods and so on in a native way. Note that the
concept of instance is not covered by the POSIX standard, that only support a
fixed priority scheduler. In POSIX, a periodic task can only be implemented
using the Real-Time extensions and in particular using the Timer feature.
S.Ha.R.K. implements also that approach, however the native primitives are
better in terms of efficiency.
}.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_ENDCYCLE\index{task\_endcycle()}
\end{intest}

\begin{description}
\item [\texttt{void task\_endcycle(void);}]
\item [Description:]It terminates the currently executing job of the calling
task. The behaviour of this primitive may sligtly change depending
on the Scheduling Module registered at initialization time. Tipically,
the \texttt{task\_endcycle} primitive suspends the task until an automatic
reactivation that is made internally by the Kernel. Moreover, the
\texttt{task\_endcycle()} primitive usually keeps track of pending
activations \footnote{There is a pending activation when a task is activated before the
current instance has finished. In this case, if the \texttt{task\_endcycle()}
primitive is called and there is a pending activation, it simply does
nothing.}. Previous versions of the kernel supported a \texttt{task\_sleep()}\index{task\_sleep()}
primitive with similar behavior. That function is currently unsupported.
The primitive is a cancellation point.
\item [Implementation:]This primitive is implemented as task\_message(NULL, 1);
\footnote{Note on the implementation: this primitive is implemented as
\texttt{task\_message(NULL, 1);}}
\item [See]\textbf{also:} \texttt{task\_activate}.
\end{description
}

%----------------------------------------------------------------------------
\section{Task (thread) specific data}
%----------------------------------------------------------------------------

These functions works in a way equal to their POSIX counterparts.
These primitives are used for managing task specific data, that are
a few data variables that can be referred in a common way independently
from the task that asks for it. The system also ensures a proper cleanup
when the task is killed. As an example, the \texttt{errno} variable
can be thought as a task specific data. In this manual only their
interfaces are described; for more informations, see the POSIX standard.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_KEY\_CREATE\index{task\_key\_create()}
\end{intest}

\begin{description}
\item [\texttt{int task\_key\_create(task\_key\_t *key, void
(*destructor)(void *));}
]
\item [Description:]It creates a task key that can be used to refer a task\_specific
data. The name of the POSIX counterpart is \texttt{pthread\_key\_create}.
\end{description
}
\pagebreak

%----------------------------------------------------------------------------
\begin{intest}
TASK\_GETSPECIFIC\index{task\_getspecific()}
\end{intest}

\begin{description}
\item [\texttt{void *task\_getspecific(task\_key\_t key);}]
\item [Description:]It gets the current value for the key (note that the
value of the key vary from task to task). The name of the POSIX counterpart
is \texttt{pthread\_getspecific}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_SETSPECIFIC\index{task\_setspecific()}
\end{intest}

\begin{description}
\item [\texttt{int task\_setspecific(task\_key\_t key, const void
*value); }
]
\item [Description:]It sets the current value for the key. The name of
the POSIX counterpart is \texttt{pthread\_setspecific}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_KEY\_DELETE\index{task\_key\_delete()}
\end{intest}

\begin{description}
\item [\texttt{int task\_key\_delete(task\_key\_t key);}]
\item [Description:]It deletes the current key. The name of the POSIX counterpart
is \texttt{pthread\_key\_delete}.
\end{description
}

%----------------------------------------------------------------------------
\section{Task cancellation}
%----------------------------------------------------------------------------

These primitives are used when managing task cancellation. They are
directly derived from the POSIX standard. Nothe that the POSIX interface
is also available.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_CLEANUP\_PUSH\index{task\_cleanup\_push()}
\end{intest}

\begin{description}
\item [\texttt{void task\_cleanup\_push(void (*routine)(void
*), void *arg);}
]
\item [Description:]It pushes the specified cancellation cleanup handler
routine onto the cancellation cleanup stack. The name of the POSIX
counterpart is \texttt{pthread\_cleanup\_push}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_CLEANUP\_POP\index{task\_cleanup\_pop()}
\end{intest}

\begin{description}
\item [\texttt{void task\_cleanup\_pop(int execute);}]
\item [Description:]It removes the routine at the top of the cancellation
cleanup stack of the calling thread. If \texttt{execute} is not equal
0, the routine previously pushed is called. The name of the POSIX
counterpart is \texttt{pthread\_cleanup\_pop}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_TESTCANCEL\index{task\_testcancel()}
\end{intest}

\begin{description}
\item [\texttt{void task\_testcancel(void);}]
\item [Description:]creates a cancellation point in the calling task. The
primitive has no effect if cancelability is disabled. The name of
the POSIX counterpart is \texttt{pthread\_testcancel}.
\end{description
}

\pagebreak

%----------------------------------------------------------------------------
\begin{intest}
TASK\_SETCANCELSTATE\index{task\_setcancelstate()}
\end{intest}

\begin{description}
\item [\texttt{int task\_setcancelstate(int state, int *oldstate);}]
\item [Description:]This primitive sets the cancelability state of the
calling thread to the indicate \emph{state} \emph{and} returns the
previous cancelability state at the location referenced by \emph{oldstate}.
Legal values for state are \texttt{TASK\_CANCEL\_ENABLE} and \texttt{TASK\_CANCEL\_DISABLE}.
\texttt{pthread\_setcancelstate} is the name of the POSIX counterpart.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_SETCANCELTYPE\index{task\_setcanceltype()}
\end{intest}

\begin{description}
\item [int task\_setcanceltype(int type, int *oldtype);]
\item [Description:]This primitive sets the cancelability type of the calling
thread to the indicate \emph{type} and returns the previous cancelability
type at the location referenced by \emph{oldtype}. Legal values for
state are \texttt{TASK\_CANCEL\_DEFERRED} and \texttt{TASK\_CANCEL\_ASINCHRONOUS}.
The name of the POSIX counterpart is \texttt{pthread\_setcanceltype}.
\end{description
}

%----------------------------------------------------------------------------
\section{Join}
%----------------------------------------------------------------------------

The join primitives allow a task to wait for the termination of another task
\footnote{not of an instance of a task!}. The return value of the terminated
task is passed to the join primitive and the caller can use the value. These
primitives are directly derived from the POSIX standard. It means that a join
can be done only on a joinable task. But note that when a task created with the
creation primitives \footnote{\texttt{task\_create}, \texttt{task\_createn},
\texttt{group\_create}} starts it is \emph{not} in the joinable state
\footnote{this is done to remain similar to the previous versions of the Hartik
Kernel...}. This behaviour differs from the standard behavior of the POSIX
standard, which specifies that every new task shall be in the joinable state.
However, S.Ha.R.K. provides also the \texttt{pthread\_create} primitive that is
\emph{fully compliant} with the standard. Finally, a S.Ha.R.K. task can switch
between the jonable and non-joinable state using the primitives
\texttt{task\_joinable} and \texttt{task\_unjoinable} \footnote{In the POSIX
standard, only the \texttt{pthread\_detach} primitive is available.
}.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_JOIN\index{task\_join()}
\end{intest}

\begin{description}
\item [\texttt{int task\_join(PID p, void **value);}]
\item [Description:]The primitive suspends the execution of the calling
task until the task p terminates, unless the task p has already terminated.
On return from a successful \texttt{task\_join} call with a non-\texttt{NULL}
\emph{value} argument, the value returned by the thread through a
task\_abort shall be made available in the location referenced by
\emph{value}. When the primitive returns successfully the target task
has been terminated. The primitive returns 0 in case of success, otherwise
it returns \texttt{EINVAL} if the value \texttt{p} does not refer
to a task that can be joined, \texttt{ESRCH} if the value \texttt{p}
does not refer to a valid task, and \texttt{EDEADLK} if a deadlock
was detected. The name of the POSIX counterpart is \texttt{pthread\_join}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_JOINABLE\index{task\_joinable()}
\end{intest}

\begin{description}
\item [\texttt{int task\_joinable(PID p);}]
\item [Description:]This function set the detach state of a task p to joinable.
This function is not present in Posix standard. It returns ESRCH if
p is non a valid task.
\end{description
}
\pagebreak

%----------------------------------------------------------------------------
\begin{intest}
TASK\_UNJOINABLE\index{task\_unjoinable()}
\end{intest}

\begin{description}
\item [\texttt{int task\_unjoinable(PID p);}]
\item [Description:]This function sets the detach state of a task to detached.
The name of the POSIX counterpart is \texttt{pthread\_detach}. The
function returns \texttt{EINVAL} if \texttt{p} can not be joined (or
currently a task has done a join on it), or \texttt{ESRCH} if \texttt{p}
is not correct.
\end{description
}

%----------------------------------------------------------------------------
\section{Preemption control}
%----------------------------------------------------------------------------

S.Ha.R.K. provides two primitives that set the preemptability of a
task. A non-preemptive task can not be preempted by another task;
interrupts are handled in the usual way. These primitives can be used
to implement short critical sections. Note the difference between
this kind of non -preemption and the interrupt disabling done using
kern\_cli and kern\_sti: in the latter case, interrupt can not preempt
the critical sections. A new task usually starts in a preemptive state.

%----------------------------------------------------------------------------
\begin{intest}
TASK\_NOPREEMPT\index{task\_nopreempt()}
\end{intest}

\begin{description}
\item [void task\_nopreempt(void);]
\item [Description:]After the call of this primitive, the task is non-preemptive.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
TASK\_PREEMPT\index{task\_preempt()}
\end{intest}

\begin{description}
\item [void task\_nopreempt(void);]
\item [Description:]After the call of this primitive, the task become again
preemptive.
\end{description
}

%----------------------------------------------------------------------------
\section{Suspending a task}
%----------------------------------------------------------------------------

The following system calls can be used by a task to suspend itself
for a known or unknown time. (Note: it is dangerous to use these system
calls in a hard real-time task.)

%----------------------------------------------------------------------------
\begin{intest}
TASK\_DELAY\index{task\_delay()}
\end{intest}

\begin{description}
\item [\texttt{void task\_delay(DWORD t);}]
\item [Description:]It causes the calling task to be blocked for at least
\texttt{t} microseconds. Note that \texttt{t} is the \emph{minimum}
delay time. In facts, after \texttt{t} microseconds the task is inserted
in the ready queue and can be delayed by higher priority tasks. This
function was inherited from the previous versions of Hartik. Please,
use the POSIX counterpart \texttt{nanosleep} instead!
\end{description
}

%----------------------------------------------------------------------------
\section{Job ExecutionTime (JET) estimation}
%----------------------------------------------------------------------------

S.Ha.R.K. provides a set of primitives that allows to precisely estimate
the system load. These primitives can be used to tune the parameters
that are given at task creation, and to get statistics about the system
load.

The execution time estimation is done on a task basis. That is, S.Ha.R.K.
provides three primitives that allows the user to estimate the JET
of every task. For every task, it is possible to know the mean execution
time, the maximum execution time, the time consumed by the current
instance and the time consumed by the last \texttt{JET\_TABLE\_DIM}
instances.

The user have to explicitly enable the Kernel to record the JET informations
for a specific task. This is done at task creation time; usually a
macro that enable the JET is provided in the definition of every task
model (see the S.Ha.R.K. Module Manual).

Here is an example of the use of the JET functions:

\begin{verbatim}
/* The Goofy Task */
void *goofy(void *arg) {
    int i;
    for (;;) {
        for (i = 0; i < 100; i++)
            kern_printf("Yuk!");
        task_endcycle();
    }
}

PID goofy_PID;

/* a NRT task that never finish */

void *jetcontrol(void *arg) {
    TIME sum, max, curr, last[5];
    int nact;
   
    for (;;) {
        if (jet_getstat(p, &sum, &max, &nact, &curr) == -1)
            continue;
        for (j = 0; j < 5; j++) last[j] = 0;
        jet_gettable(p, &last[0], 5);
        printf_xy(1, 20, WHITE,"goofy_PID=%d mean=%d max=%d nact=%d",
            goofy_PID, sum / (nact == 0 ? 1 : nact), max, nact);
        printf_xy(1, 21, WHITE, "L1=%d L2=%d L3=%d L4=%d L5=%d",
            last[0], last[1], last[2], last[3], last[4]);
    }
}

*int main(int argc, char **argv) {
    ...

    /* The task goofy is created, specifying that the Kernel
        should take care of the JET data */

    HARD_TASK_MODEL m;

    hard_task_default_model(m);

    /* ... other hard_task_XXX macros */
    hard_task_def_ctrl_jet(m); /* JET enabling */
    goofy_PID = task_create("Goofy", goofy, &m, NULL);

    /* ... creation of the JET control task, and so on */
}
\end{verbatim
}

%----------------------------------------------------------------------------
\begin{intest}
JET\_GETSTAT\index{jet\_getstat()}
\end{intest}

\begin{description}
\item [int jet\_getstat(PID p, TIME *sum, TIME *max, int *n, TIME
*curr);
]
\item [Description:]This primitive returns some JET informations about
the task p. The informations retrieved are stored into the following
parameters:

\begin{description}
\item [\texttt{sum}]is the task total execution time since it was created
or since the last call to the \texttt{jet\_delstat} function;
\item [\texttt{max}]is the maximum time used by a task instance since it
was created or since the last call to the \texttt{jet\_delstat} function;
\item [\texttt{n}]is the number of terminated instances which sum and max
refers to;
\item [\texttt{curr}]is the total execution time of the current instance.
\end{description}

If a parameter is passed as \texttt{NULL} the information is not returned.
The function returns 0 if the \texttt{PID} passed is correct, \texttt{-1}
if the PID passed does not correspond to a valid PID or the task does
not have the \texttt{JET\_ENABLE} bit set.

\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
JET\_DELSTAT\index{jet\_delstat()}
\end{intest}

\begin{description}
\item [int jet\_delstat(PID p);]
\item [Description:]The primitive voids the actual task execution time
data mantained by the Generic Kernel. The function returns 0 if the
PID passed is correct, \texttt{-1} if the PID passed does not correspond
to a valid PID or the task does not have the \texttt{JET\_ENABLE}
bit set.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
JET\_GETTABLE\index{jet\_gettable()}
\end{intest}

\begin{description}
\item [int jet\_gettable(PID p, TIME *table, int n);]
\item [Description:]The primitive returns the last \texttt{n} execution
times of the task \texttt{p}. If the parameter n is less than 0, it
returns only the last values stored since the last call to \texttt{jet\_gettable}
(up to a maximum of \texttt{JET\_TABLE\_DIM} values). If the value
is greater than 0, the function returns the last \texttt{min(n,~JET\_TABLE\_DIM)}
values registered. The return value is \texttt{-1} if the task passed
as parameter does not exist or the task does not have the \texttt{JET\_ENABLE}
bit set, otherwise the number of values stored into the array is returned.
The table passed as parameter should store at least \texttt{JET\_TABLE\_DIM}
elements.
\end{description
}