10.2 Program Execution
{program}
{program execution}
{running a program:
See program execution} An Ada
program
consists of a set of
partitions[, which can execute in parallel
with one another, possibly in a separate address space, and possibly
on a separate computer.]
Post-Compilation Rules
{partition [distributed]}
{partition building}
A partition is a program or part of a program that
can be invoked from outside the Ada implementation. [For example, on
many systems, a partition might be an executable file generated by the
system linker.]
{explicitly assign}
The user can
explicitly assign library units
to a partition. The assignment is done in an implementation-defined manner.
The compilation units included in a partition are those of the explicitly
assigned library units, as well as other compilation units
needed
by those library units. The compilation units needed by a given compilation
unit are determined as follows (unless specified otherwise via an implementation-defined
pragma, or
by some other implementation-defined means):
{linking:
See partition building} {compilation
units needed (by a compilation unit) [distributed]} {needed
(of a compilation unit by another) [distributed]}
Discussion: From a run-time point of
view, an Ada 95 partition is identical to an Ada 83 program — implementations
were always allowed to provide inter-program communication mechanisms.
The additional semantics of partitions is that interfaces between them
can be defined to obey normal language rules (as is done in
Annex
E, “
Distributed Systems”), whereas
interfaces between separate programs had no particular semantics.
Implementation defined: The manner of
explicitly assigning library units to a partition.
Implementation defined: The implementation-defined
means, if any, of specifying which compilation units are needed by a
given compilation unit.
Discussion: There are no pragmas that
“specify otherwise” defined by the core language. However,
an implementation is allowed to provide such pragmas, and in fact
Annex
E, “
Distributed Systems” defines
some pragmas whose semantics includes reducing the set of compilation
units described here.
A compilation unit needs itself;
If a compilation unit is needed, then so are any
compilation units upon which it depends semantically;
{
AI95-00217-06}
If a compilation unit with stubs is needed, then so are any corresponding
subunits;
Discussion: Note that in the environment,
the stubs are replaced with the corresponding proper_bodies.
{
AI95-00217-06}
If the (implicit) declaration of the limited view of a library package
is needed, then so is the explicit declaration of the library package.
Discussion: Note that a child unit is
not included just because its parent is included — to include a
child, mention it in a
with_clause.
{
AI95-00217-06}
A package is included in a partition even if the only reference to it
is in a
limited_with_clause.
While this isn't strictly necessary (no objects of types imported from
such a unit can be created), it ensures that all incomplete types are
eventually completed, and is the least surprising option.
{main subprogram
(for a partition)} The user can optionally
designate (in an implementation-defined manner) one subprogram as the
main subprogram for the partition. A main subprogram, if specified,
shall be a subprogram.
Discussion: This may seem superfluous,
since it follows from the definition. But we would like to have every
error message that might be generated (before run time) by an implementation
correspond to some explicitly stated “shall” rule.
Of course, this does not mean that the “shall”
rules correspond one-to-one with an implementation's error messages.
For example, the rule that says overload resolution “shall”
succeed in producing a single interpretation would correspond to many
error messages in a good implementation — the implementation would
want to explain to the user exactly why overload resolution failed. This
is especially true for the syntax rules — they are considered part
of overload resolution, but in most cases, one would expect an error
message based on the particular syntax rule that was violated.
Implementation defined: The manner of
designating the main subprogram of a partition.
Ramification: An implementation cannot
require the user to specify, say, all of the library units to be included.
It has to support, for example, perhaps the most typical case, where
the user specifies just one library unit, the main program. The implementation
has to do the work of tracking down all the other ones.
{environment task}
Each partition has an anonymous
environment task[,
which is an implicit outermost task whose execution elaborates the
library_items
of the environment
declarative_part,
and then calls the main subprogram, if there is one. A partition's execution
is that of its tasks.]
Ramification: An environment task has
no master; all nonenvironment tasks have masters.
An implementation is allowed to support multiple
concurrent executions of the same partition.
[The order of elaboration of library units is determined
primarily by the
elaboration dependences.]
{elaboration
dependence (library_item on another)} {dependence
(elaboration)} There is an elaboration
dependence of a given
library_item
upon another if the given
library_item
or any of its subunits depends semantically on the other
library_item.
In addition, if a given
library_item
or any of its subunits has a
pragma
Elaborate or Elaborate_All that names another library unit, then there
is an elaboration dependence of the given
library_item
upon the body of the other library unit, and, for Elaborate_All only,
upon each
library_item
needed by the declaration of the other library unit.
Discussion: {
8652/0107}
{
AI95-00180-01}
{
AI95-00256-01}
“Mentions” was used informally in the above rule; it was
not intended to refer to the definition of
mentions in
10.1.2.
It was changed to “names” to make this clear.
See above for a definition of which
library_items
are “needed by” a given declaration.
Note that elaboration dependences are among
library_items,
whereas the other two forms of dependence are among compilation units.
Note that elaboration dependence includes semantic dependence. It's a
little bit sad that pragma Elaborate_Body can't be folded into this mechanism.
It follows from the definition that the elaboration dependence relationship
is transitive. Note that the wording of the rule does not need to take
into account a semantic dependence of a
library_item
or one of its subunits upon a subunit of a different library unit, because
that can never happen.
The environment task
for a partition has the following structure:
task Environment_Task;
task body Environment_Task is
... (1) --
The environment declarative_part
--
(that is, the sequence of library_items) goes here.
begin
... (2) --
Call the main subprogram, if there is one.
end Environment_Task;
Ramification: The name of the environment
task is written in italics here to indicate that this task is anonymous.
Discussion: The model is different for
a “passive partition” (see
E.1).
Either there is no environment task, or its
sequence_of_statements
is an infinite loop rather than a call on a main subprogram.
The order of all included
library_items
is such that there are no forward elaboration dependences.
Discussion: This implies that the body
of such a library unit shall not “with” any of its own children,
or anything else that depends semantically upon the declaration of the
library unit.
All
library_items
declared pure occur before any that are not declared pure.
All preelaborated
library_items
occur before any that are not preelaborated.
Discussion: Normally, if two partitions
contain the same compilation unit, they each contain a separate
copy
of that compilation unit. See
Annex E, “
Distributed
Systems” for cases where two partitions share the same copy
of something.
There is no requirement that the main subprogram
be elaborated last. In fact, it is possible to write a partition in which
the main subprogram cannot be elaborated last.
Ramification: This
declarative_part
has the properties required of all environments (see
10.1.4).
However, the environment
declarative_part
of a partition will typically contain fewer compilation units than the
environment
declarative_part
used at compile time — only the “needed” ones are included
in the partition.
There shall be a total order of the
library_items
that obeys the above rules. The order is otherwise implementation defined.
Discussion: The only way to violate this
rule is to have Elaborate, Elaborate_All, or Elaborate_Body
pragmas
that cause circular ordering requirements, thus preventing an order that
has no forward elaboration dependences.
Implementation defined: The order of
elaboration of
library_items.
To be honest: {
requires a completion
(library_unit_declaration) [partial]}
{
notwithstanding}
Notwithstanding what the RM95 says elsewhere, each
rule that requires a declaration to have a corresponding completion is
considered to be a Post-Compilation Rule when the declaration is that
of a library unit.
Discussion: Such rules may be checked
at “link time,” for example. Rules requiring the completion
to have certain properties, on the other hand, are checked at compile
time of the completion.
The full expanded names of the library units and
subunits included in a given partition shall be distinct.
Reason: This is a Post-Compilation Rule
because making it a Legality Rule would violate the Language Design Principle
labeled “legality determinable via semantic dependences.”
A call to the main subprogram, if the partition
has one. If the main subprogram has parameters, they are passed; where
the actuals come from is implementation defined. What happens to the
result of a main function is also implementation defined.
Implementation defined: Parameter passing
and function return for the main subprogram.
or:
Discussion: For a passive partition,
either there is no environment task, or its
sequence_of_statements
is an infinite loop. See
E.1.
The mechanisms for building and running partitions
are implementation defined. [These might be combined into one operation,
as, for example, in dynamic linking, or “load-and-go” systems.]
Implementation defined: The mechanisms
for building and running partitions.
Dynamic Semantics
{execution (program)
[partial]} The execution of a program consists
of the execution of a set of partitions. Further details are implementation
defined.
{execution (partition) [partial]}
The execution of a partition starts with the execution
of its environment task, ends when the environment task terminates, and
includes the executions of all tasks of the partition. [The execution
of the (implicit)
task_body
of the environment task acts as a master for all other tasks created
as part of the execution of the partition. When the environment task
completes (normally or abnormally), it waits for the termination of all
such tasks, and then finalizes any remaining objects of the partition.]
Ramification: The “further details”
mentioned above include, for example, program termination — it
is implementation defined. There is no need to define it here; it's entirely
up to the implementation whether it wants to consider the program as
a whole to exist beyond the existence of individual partitions.
Implementation defined: The details of
program execution, including program termination.
To be honest: {
termination (of a partition)
[partial]}
{
normal termination (of a partition)
[partial]}
{
termination (normal) [partial]}
{
abnormal termination (of a partition) [partial]}
{
termination (abnormal) [partial]}
The
execution of the partition terminates (normally or abnormally) when the
environment task terminates (normally or abnormally, respectively).
Bounded (Run-Time) Errors
{bounded error (cause)
[partial]} {Program_Error
(raised by failure of run-time check)} Once
the environment task has awaited the termination of all other tasks of
the partition, any further attempt to create a task (during finalization)
is a bounded error, and may result in the raising of Program_Error either
upon creation or activation of the task.
{unspecified
[partial]} If such a task is activated, it
is not specified whether the task is awaited prior to termination of
the environment task.
Implementation Requirements
The implementation
shall ensure that all compilation units included in a partition are consistent
with one another, and are legal according to the rules of the language.
Discussion: The consistency requirement
implies that a partition cannot contain two versions of the same compilation
unit. That is, a partition cannot contain two different library units
with the same full expanded name, nor two different bodies for the same
program unit. For example, suppose we compile the following:
package A is -- Version 1.
...
end A;
with A;
package B is
end B;
package A is -- Version 2.
...
end A;
with A;
package C is
end C;
It would be wrong for a partition containing
B and C to contain both versions of A. Typically, the implementation
would require the use of Version 2 of A, which might require the recompilation
of B. Alternatively, the implementation might automatically recompile
B when the partition is built. A third alternative would be an incremental
compiler that, when Version 2 of A is compiled, automatically patches
the object code for B to reflect the changes to A (if there are any relevant
changes — there might not be any).
An implementation that supported fancy version
management might allow the use of Version 1 in some circumstances. In
no case can the implementation allow the use of both versions in the
same partition (unless, of course, it can prove that the two versions
are semantically identical).
Implementation Permissions
{active partition}
The kind of partition described in this clause is
known as an
active partition. An implementation is allowed to
support other kinds of partitions, with implementation-defined semantics.
Implementation defined: The semantics
of any nonactive partitions supported by the implementation.
Discussion: Annex
E, “
Distributed Systems” defines
the concept of passive partitions; they may be thought of as a partition
without an environment task, or as one with a particularly simple form
of environment task, having an infinite loop rather than a call on a
main subprogram as its
sequence_of_statements.
An implementation may restrict the kinds of subprograms
it supports as main subprograms. However, an implementation is required
to support all main subprograms that are public parameterless library
procedures.
Ramification: The implementation is required
to support main subprograms that are procedures declared by
generic_instantiations,
as well as those that are children of library units other than Standard.
Generic units are, of course, not allowed to be main subprograms, since
they are not subprograms.
Note that renamings are irrelevant to this rule.
This rules says which subprograms (not views) have to be supported. The
implementation can choose any way it wants for the user to indicate which
subprogram should be the main subprogram. An implementation might allow
any name of any view, including those declared by renamings. Another
implementation might require it to be the original name. Another implementation
still might use the name of the source file or some such thing.
If the environment task completes abnormally, the
implementation may abort any dependent tasks.
Reason: If the implementation does not
take advantage of this permission, the normal action takes place —
the environment task awaits those tasks.
The possibility of aborting them is not shown
in the
Environment_Task code above, because there is nowhere to
put an
exception_handler
that can handle exceptions raised in both the environment
declarative_part
and the main subprogram, such that the dependent tasks can be aborted.
If we put an
exception_handler
in the body of the environment task, then it won't handle exceptions
that occur during elaboration of the environment
declarative_part.
If we were to move those things into a nested
block_statement,
with the
exception_handler
outside that, then the
block_statement
would await the library tasks we are trying to abort.
Furthermore, this is merely a permission, and
is not fundamental to the model, so it is probably better to state it
separately anyway.
Note that implementations (and tools like debuggers)
can have modes that provide other behaviors in addition.
8 An implementation may provide inter-partition
communication mechanism(s) via special packages and pragmas. Standard
pragmas for distribution and methods for specifying inter-partition communication
are defined in
Annex E, “
Distributed
Systems”. If no such mechanisms are provided, then each partition
is isolated from all others, and behaves as a program in and of itself.
Ramification: Not providing such mechanisms
is equivalent to disallowing multi-partition programs.
An implementation may provide mechanisms to
facilitate checking the consistency of library units elaborated in different
partitions;
Annex E, “
Distributed
Systems” does so.
9 Partitions are not required to run in
separate address spaces. For example, an implementation might support
dynamic linking via the partition concept.
10 An order of elaboration of
library_items
that is consistent with the partial ordering defined above does not always
ensure that each
library_unit_body
is elaborated before any other compilation unit whose elaboration necessitates
that the
library_unit_body
be already elaborated. (In particular, there is no requirement that the
body of a library unit be elaborated as soon as possible after the
library_unit_declaration
is elaborated, unless the pragmas in subclause
10.2.1
are used.)
11 A partition (active or otherwise) need
not have a main subprogram. In such a case, all the work done by the
partition would be done by elaboration of various
library_items,
and by tasks created by that elaboration. Passive partitions, which cannot
have main subprograms, are defined in
Annex E,
“
Distributed Systems”.
Ramification: The environment task is
the outermost semantic level defined by the language.
Standard has no private part. This prevents
strange implementation-dependences involving private children of Standard
having visibility upon Standard's private part. It doesn't matter where
the body of Standard appears in the environment, since it doesn't do
anything. See
Annex A, “
Predefined
Language Environment”.
Note that elaboration dependence is carefully
defined in such a way that if (say) the body of something doesn't exist
yet, then there is no elaboration dependence upon the nonexistent body.
(This follows from the fact that “needed by” is defined that
way, and the elaboration dependences caused by a
pragma
Elaborate or Elaborate_All are defined in terms of “needed by”.)
This property allows us to use the environment concept both at compile
time and at partition-construction time/run time.
Extensions to Ada 83
{
extensions to Ada 83}
The
concept of partitions is new to Ada 95.
A main subprogram is now optional. The language-defined
restrictions on main subprograms are relaxed.
Wording Changes from Ada 83
Ada 95 uses the term “main subprogram”
instead of Ada 83's “main program” (which was inherited from
Pascal). This is done to avoid confusion — a main subprogram is
a subprogram, not a program. The program as a whole is an entirely different
thing.
Wording Changes from Ada 95
{
AI95-00256-01}
The mistaken use of “mentions” in the elaboration dependence
rule was fixed.
{
AI95-00217-06}
The
needs relationship was extended to include limited views.