9.4 Protected Units and Protected Objects
1
{protected object}
{protected operation}
{protected subprogram}
{protected entry}
A
protected object provides coordinated access
to shared data, through calls on its visible
protected operations,
which can be
protected subprograms or
protected entries.
{protected declaration} {protected
unit} {protected
declaration} A
protected unit is
declared by a
protected declaration, which has a corresponding
protected_body. A protected declaration may
be a
protected_type_declaration, in which
case it declares a named protected type; alternatively, it may be a
single_protected_declaration,
in which case it defines an anonymous protected type, as well as declaring
a named protected object of that type.
{broadcast
signal: See protected object}
Syntax
2/2
{
AI95-00345-01}
protected_type_declaration ::=
protected type defining_identifier [
known_discriminant_part]
is
[new interface_list with]
protected_definition;
3/2
{
AI95-00399-01}
single_protected_declaration ::=
protected defining_identifier is
[new interface_list with]
protected_definition;
4
protected_definition ::=
{
protected_operation_declaration }
[
private
{
protected_element_declaration } ]
end [
protected_identifier]
5/1
{
8652/0009}
{
AI95-00137-01}
protected_operation_declaration ::= subprogram_declaration
|
entry_declaration
|
aspect_clause representation_clause
6
protected_element_declaration ::= protected_operation_declaration
|
component_declaration
6.a
Reason: We allow the operations and components
to be mixed because that's how other things work (for example, package
declarations). We have relaxed the ordering rules for the items inside
declarative_parts and task_definitions
as well.
7
protected_body ::=
protected body defining_identifier is
{
protected_operation_item }
end [
protected_identifier];
8/1
{
8652/0009}
{
AI95-00137-01}
protected_operation_item ::= subprogram_declaration
|
subprogram_body
|
entry_body
|
aspect_clause representation_clause
9
If a protected_identifier
appears at the end of a protected_definition
or protected_body, it shall repeat the defining_identifier.
Legality Rules
10/2
This paragraph was
deleted.{
AI95-00345-01}
{requires a completion
(protected_declaration}) [partial]} A
protected declaration requires a completion[, which shall be a protected_body,]
and every protected_body shall be the completion
of some protected declaration.
10.a/2
This paragraph
was deleted.To be honest: The
completion can be a pragma Import, if the
implementation supports it.
Static Semantics
11/2
{
AI95-00345-01}
{
AI95-00401-01}
A
protected_definition defines a protected
type and its first subtype.
{visible
part (of a protected unit) [partial]} The
list of
protected_operation_declarations of
a
protected_definition, together with the
known_discriminant_part, if any, is called
the visible part of the protected unit. [
{private
part (of a protected unit) [partial]} The
optional list of
protected_element_declarations
after the reserved word
private is called the private part of
the protected unit.]
11.a
Proof: Private part is defined in Section
8.
11.1/2
{
AI95-00345-01}
{
AI95-00397-01}
{
AI95-00399-01}
{
AI95-00419-01}
For a protected declaration with an interface_list,
the protected type inherits user-defined primitive subprograms from each
progenitor type (see 3.9.4), in the same
way that a derived type inherits user-defined primitive subprograms from
its progenitor types (see 3.4). If the first
parameter of a primitive inherited subprogram is of the protected type
or an access parameter designating the protected type, and there is a
protected_operation_declaration for a protected
subprogram or single entry with the same identifier within the protected
declaration, whose profile is type conformant with the prefixed view
profile of the inherited subprogram, the inherited subprogram is said
to be implemented by the conforming protected subprogram or entry.{implemented
(by a protected subprogram) [partial]} {implemented
(by a protected entry) [partial]} {type
conformance (required)}
11.b/2
Ramification: The
inherited subprograms can only come from an interface given as part of
the protected declaration.
Legality Rules
11.2/2
{
AI95-00345-01}
{requires a completion
(protected_declaration}) [partial]} A
protected declaration requires a completion[, which shall be a protected_body,]
and every protected_body shall be the completion
of some protected declaration.
11.c/2
To be honest: The
completion can be a pragma Import, if the
implementation supports it.
11.3/2
{
AI95-00345-01}
{
AI95-00399-01}
[Each interface_subtype_mark
of an interface_list appearing within a protected
declaration shall denote a limited interface type that is not a task
interface.]
11.d/2
Proof: 3.9.4
requires that an interface_list only name
interface types, and limits the descendants of the various kinds of interface
types. Only a limited, protected, or synchronized interface can have
a protected type descendant. Nonlimited or task interfaces are not allowed,
as they offer operations that a protected type does not have.
11.4/2
{
AI95-00397-01}
The prefixed view profile of an explicitly declared
primitive subprogram of a tagged protected type shall not be type conformant
with any protected operation of the protected type, if the first parameter
of the subprogram is of the protected type or is an access parameter
designating the protected type.{type
conformance (required)}
11.e/2
Reason: This prevents
the existence of two operations with the same name and profile which
could be called with a prefixed view. If the operation was inherited,
this would be illegal by the following rules; this rule puts inherited
and non-inherited routines on the same footing. Note that this only applies
to tagged protected types (that is, those with an interface in their
declaration); we do that as there is no problem with prefixed view calls
of primitive operations for “normal” protected types, and
having this rule apply to all protected types would be incompatible with
Ada 95.
11.5/2
{
AI95-00345-01}
{
AI95-00399-01}
For each primitive subprogram inherited by the
type declared by a protected declaration, at most one of the following
shall apply:
11.6/2
- {AI95-00345-01}
the inherited subprogram is overridden with a primitive
subprogram of the protected type, in which case the overriding subprogram
shall be subtype conformant with the inherited subprogram and not abstract;
or{subtype conformance (required)}
11.7/2
- {AI95-00345-01}
{AI95-00397-01}
the inherited subprogram is implemented by a protected
subprogram or single entry of the protected type, in which case its prefixed
view profile shall be subtype conformant with that of the protected subprogram
or entry. {subtype conformance (required)}
11.8/2
If neither applies, the
inherited subprogram shall be a null procedure. {generic
contract issue [partial]} In addition
to the places where Legality Rules normally apply (see 12.3),
these rules also apply in the private part of an instance of a generic
unit.
11.f/2
Reason: Each inherited
subprogram can only have a single implementation (either from overriding
a subprogram, implementing a subprogram, or implementing an entry), and
must have an implementation unless the subprogram is a null procedure.
11.9/2
{
AI95-00345-01}
If an inherited subprogram is implemented by a
protected procedure or an entry, then the first parameter of the inherited
subprogram shall be of mode out or in out, or an access-to-variable
parameter.
11.g/2
Reason: For a protected
procedure or entry, the protected object can be read or written (see
9.5.1}. A subprogram that is implemented
by a protected procedure or entry must have a profile which reflects
that in order to avoid confusion.
11.10/2
{
AI95-00397-01}
If a protected subprogram declaration has an overriding_indicator,
then at the point of the declaration:
11.11/2
- if the overriding_indicator
is overriding, then the subprogram shall implement an inherited
subprogram;
11.12/2
- if the overriding_indicator
is not overriding, then the subprogram shall not implement any
inherited subprogram.
11.13/2
{generic
contract issue [partial]} In addition
to the places where Legality Rules normally apply (see 12.3),
these rules also apply in the private part of an instance of a generic
unit.
11.h/2
Discussion: These
rules are subtly different than those for subprograms (see 8.3.1)
because there cannot be “late” inheritance of primitives
from interfaces. Hidden (that is, private) interfaces are prohibited
explicitly (see 7.3), as are hidden primitive
operations (as private operations of public abstract types are prohibited
— see 3.9.3).
Dynamic Semantics
12
[
{elaboration (protected
declaration) [partial]} The elaboration
of a protected declaration elaborates the
protected_definition.
{elaboration (single_protected_declaration)
[partial]} The elaboration of a
single_protected_declaration
also creates an object of an (anonymous) protected type.]
12.a
Proof: This is redundant with the general
rules for the elaboration of a full_type_declaration
and an object_declaration.
13
{elaboration (protected_definition)
[partial]} [The elaboration of a
protected_definition
creates the protected type and its first subtype;] it also includes the
elaboration of the
component_declarations
and
protected_operation_declarations in the
given order.
14
[
{initialization
(of a protected object) [partial]} As
part of the initialization of a protected object, any per-object constraints
(see
3.8) are elaborated.]
14.a
Discussion: We do not mention pragmas
since each pragma has its own elaboration rules.
15
{elaboration (protected_body)
[partial]} The elaboration of a
protected_body
has no other effect than to establish that protected operations of the
type can from then on be called without failing the Elaboration_Check.
16
The content of an object
of a given protected type includes:
17
- The values of the components of the
protected object, including (implicitly) an entry queue for each entry
declared for the protected object;
17.a
Ramification: "For each entry"
implies one queue for each single entry, plus one for each entry of each
entry family.
18
- {execution
resource (associated with a protected object) [partial]} A
representation of the state of the execution resource associated
with the protected object (one such resource is associated with each
protected object).
19
[The execution resource associated with a protected
object has to be acquired to read or update any components of the protected
object; it can be acquired (as part of a protected action — see
9.5.1) either for concurrent read-only access,
or for exclusive read-write access.]
20
{finalization (of
a protected object) [partial]} {Program_Error
(raised by failure of run-time check)} As
the first step of the
finalization of a protected object, each
call remaining on any entry queue of the object is removed from its queue
and Program_Error is raised at the place of the corresponding
entry_call_statement.
20.a
Reason: This
is analogous to the raising of Tasking_Error in callers of a task that
completes before accepting the calls. This situation can only occur due
to a requeue (ignoring premature unchecked_deallocation), since any task
that has accessibility to a protected object is awaited before finalizing
the protected object. For example:
20.b
procedure Main is
task T is
entry E;
end T;
20.c
task body T is
protected PO is
entry Ee;
end PO;
20.d
protected body PO is
entry Ee when False is
begin
null;
end Ee;
end PO;
begin
accept E do
requeue PO.Ee;
end E;
end T;
begin
T.E;
end Main;
20.e
The environment task is queued on PO.EE when
PO is finalized.
20.f
In a real example, a server task might park
callers on a local protected object for some useful purpose, so we didn't
want to disallow this case.
Bounded (Run-Time) Errors
20.1/2
{
AI95-00280-01}
{bounded error
(cause) [partial]} It is a bounded error
to call an entry or subprogram of a protected object after that object
is finalized. If the error is detected, Program_Error is raised. Otherwise,
the call proceeds normally, which may leave a task queued forever.
20.g/2
Reason: This is
very similar to the finalization rule. It is a bounded error so that
an implementation can avoid the overhead of the check if it can ensure
that the call still will operate properly. Such an implementation cannot
need to return resources (such as locks) to an executive that it needs
to execute calls.
20.h/2
This
case can happen (and has happened in production code) when a protected
object is accessed from the Finalize routine of a type. For example:
20.i/2
with Ada.Finalization.Controlled;
package Window_Manager is
...
type Root_Window is new Ada.Finalization.Controlled with private;
type Any_Window is access all Root_Window;
...
private
...
procedure Finalize (Object : in out Root_Window);
...
end Window_Manager;
20.j/2
package body Window_Manager is
protected type Lock is
entry Get_Lock;
procedure Free_Lock;
...
end Lock;
20.k/2
Window_Lock : Lock;
20.l/2
procedure Finalize (Object : in out Root_Window) is
begin
Window_Lock.Get_Lock;
...
Window_Lock.Free_Lock;
end Finalize;
...
A_Window : Any_Window := new Root_Window;
end Window_Manager;
20.m/2
The environment task will
call Window_Lock for the object allocated for A_Window when the collection
for Any_Window is finalized, which will happen after the finalization
of Window_Lock (because finalization of the package body will occur before
that of the package specification).
21/2
13 {
AI95-00382-01}
Within the declaration or body of a protected unit
other than in an access_definition,
the name of the protected unit denotes the current instance of the unit
(see
8.6), rather than the first subtype of
the corresponding protected type (and thus the name cannot be used as
a
subtype_mark).
21.a/2
Discussion: {
AI95-00382-01}
It can be used as a subtype_mark
in an anonymous access type. In addition However,
it is possible to refer to some other subtype of the protected type within
its body, presuming such a subtype has been declared between the
protected_type_declaration
and the
protected_body.
22
14 A
selected_component
can be used to denote a discriminant of a protected object (see
4.1.3).
Within a protected unit, the name of a discriminant of the protected
type denotes the corresponding discriminant of the current instance of
the unit.
23/2
15 {
AI95-00287-01}
A protected type is a limited type (see
7.5),
and hence
precludes use of assignment_statements
and has neither an assignment operation
nor predefined equality operators.
24
16 The bodies of the protected operations
given in the protected_body define the actions
that take place upon calls to the protected operations.
25
17 The declarations in the private part
are only visible within the private part and the body of the protected
unit.
25.a
Reason: Component_declarations
are disallowed in a protected_body because,
for efficiency, we wish to allow the compiler to determine the size of
protected objects (when not dynamic); the compiler cannot necessarily
see the body. Furthermore, the semantics of initialization of such objects
would be problematic — we do not wish to give protected objects
complex initialization semantics similar to task activation.
25.b
The same applies to entry_declarations,
since an entry involves an implicit component — the entry queue.
Examples
26
Example of declaration
of protected type and corresponding body:
27
protected type Resource is
entry Seize;
procedure Release;
private
Busy : Boolean := False;
end Resource;
28
protected body Resource is
entry Seize when not Busy is
begin
Busy := True;
end Seize;
29
procedure Release is
begin
Busy := False;
end Release;
end Resource;
30
Example of a single
protected declaration and corresponding body:
31
protected Shared_Array is
-- Index, Item, and Item_Array are global types
function Component (N : in Index) return Item;
procedure Set_Component(N : in Index; E : in Item);
private
Table : Item_Array(Index) := (others => Null_Item);
end Shared_Array;
32
protected body Shared_Array is
function Component(N : in Index) return Item is
begin
return Table(N);
end Component;
33
procedure Set_Component(N : in Index; E : in Item) is
begin
Table(N) := E;
end Set_Component;
end Shared_Array;
34
Examples of protected
objects:
35
Control : Resource;
Flags : array(1 .. 100) of Resource;
Extensions to Ada 83
35.a
{
extensions to Ada 83}
This
entire clause is new; protected units do not exist in Ada 83.
Extensions to Ada 95
35.b/2
{
AI95-00345-01}
{
AI95-00397-01}
{
AI95-00399-01}
{
AI95-00401-01}
{
AI95-00419-01}
{extensions to Ada 95} Protected
types and single protected objects can be derived from one or more interfaces.
Operations declared in the protected type can implement the primitive
operations of an interface. Overriding_indicators
can be used to specify whether or not a protected operation implements
a primitive operation.
Wording Changes from Ada 95
35.c/2
{
8652/0009}
{
AI95-00137-01}
Corrigendum: Changed representation clauses
to aspect clauses to reflect that they are used for more than just representation.
35.d/2
{
AI95-00280-01}
Described what happens when an operation of a finalized
protected object is called.
35.e/2
{
AI95-00287-01}
Revised the note on operations of protected types
to reflect that limited types do have an assignment operation, but not
copying (assignment_statements).
35.f/2
{
AI95-00382-01}
Revised the note on use of the name of a protected
type within itself to reflect the exception for anonymous access types.