9.4 Protected Units and Protected Objects
1
A
protected object provides coordinated access to shared data, through
calls on its visible
protected operations, which can be
protected
subprograms or
protected entries.
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.
Syntax
2/3
3/3
4
5/1
6
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
8/1
9
Legality Rules
10/2
10.a/2
This paragraph
was deleted.To be honest: The
completion can be a pragma
Import, if the implementation supports it.
Paragraph
10 was deleted.
Static Semantics
11/2
11.a
Proof: Private part is defined in Section
8.
11.1/3
{
AI95-00345-01}
{
AI95-00397-01}
{
AI95-00399-01}
{
AI95-00419-01}
{
AI05-0042-1}
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 using an implicitly declared non-abstract
subprogram which has the same profile as the inherited subprogram and
which overrides it.
11.b/2
Ramification: The
inherited subprograms can only come from an interface given as part of
the protected declaration.
11.b.1/3
Reason: {
AI05-0042-1}
The part about the implicitly declared subprogram
is needed so that a subprogram implemented by an entry or subprogram
is considered to be overridden for the purpose of the other rules of
the language. Without it, it would for instance be illegal for an abstract
subprogram to be implemented by an entry, because the abstract subprogram
would not be overridden. The Legality Rules below ensure that there is
no conflict between the implicit overriding subprogram and a user-defined
overriding subprogram.
Legality Rules
11.2/2
11.c/3
To be honest: {
AI05-0229-1}
If The
completion can be a pragma
Import, if the implementation supports
it, the protected body can be imported (using
aspect Import, see B.1), in which case no explicit
protected_body
is allowed.
11.3/2
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/3
{
AI95-00397-01}
{
AI05-0042-1}
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 subprogram has the same defining name as the protected operation and
the first parameter of the subprogram is
of the protected type or is an access parameter designating the protected
type.
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
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.
11.8/2
If neither applies, the
inherited subprogram shall be a null procedure. 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
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
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
12.a
13
14
[
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
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
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
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/3
{
AI05-0005-1}
The environment task is queued on PO.
Ee 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}
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
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
This entire clause is new;
protected units do not exist in Ada 83.
Extensions to Ada 95
35.b/2
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.
Extensions to Ada 2005
35.g/3
Wording Changes from Ada 2005
35.h/3
{
AI05-0042-1}
Correction: Clarified that an inherited
subprogram of a progenitor is overridden when it is implemented by an
entry or subprogram.
35.i/3
{
AI05-0090-1}
Correction: Added the missing defining name
in the no conflicting primitive operation rule.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe