Skip to main content

9.4 Protected Units and Protected Objects

danger

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

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

protected_type_declaration ::=
protected type defining_identifier [known_discriminant_part]
[aspect_specification] is
[new interface_list with]
protected_definition;

3/3

single_protected_declaration ::=
protected defining_identifier
[aspect_specification] is
[new interface_list with]
protected_definition;

4

protected_definition ::=
{ protected_operation_declaration }
[ private
{ protected_element_declaration } ]
end [protected_identifier]

5/1

{8652/0009} protected_operation_declaration ::= subprogram_declaration
| entry_declaration
| aspect_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/3

protected_body ::=
protected body defining_identifier
[aspect_specification] is
{ protected_operation_item }
end [protected_identifier];

8/4

{8652/0009} protected_operation_item ::= subprogram_declaration
| subprogram_body
| null_procedure_declaration
| expression_function_declaration
| entry_body
| aspect_clause

9

If a protected_identifier appears at the end of a protected_definition or protected_body, it shall repeat the defining_identifier.

10.a/2
This paragraph was deleted.

Paragraph 10 was deleted.

Static Semantics

11/2

A protected_definition defines a protected type and its first subtype. 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. [ 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 Clause 8.

11.1/3

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 nonabstract 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

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

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/3

To be honest: 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

[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/3

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 noninherited 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

For each primitive subprogram inherited by the type declared by a protected declaration, at most one of the following shall apply:

11.6/2
  • 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
  • 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/3

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. If an inherited subprogram is implemented by a protected function, then the first parameter of the inherited subprogram shall be of mode in, but not an access-to-variable parameter.

11.g/3
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. Similarly, a protected function has a parameter that is a constant, and the inherited routine should reflect that.

11.10/2

If a protected subprogram declaration has an overriding_indicator, then at the point of the declaration:

11.11/2 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

[The elaboration of a protected declaration elaborates the protected_definition. 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

[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

[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

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

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

NOTE 1 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

It can be used as a subtype_mark in an anonymous access type. In addition, 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

NOTE 2 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

NOTE 3 A protected type is a limited type (see 7.5), and hence precludes use of assignment_statements and predefined equality operators.

24

NOTE 4 The bodies of the protected operations given in the protected_body define the actions that take place upon calls to the protected operations.

25

NOTE 5 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/5

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/3

This entire subclause is new; protected units do not exist in Ada 83.

Extensions to Ada 95

35.b/2

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} Corrigendum: Changed representation clauses to aspect clauses to reflect that they are used for more than just representation.

35.d/2

Described what happens when an operation of a finalized protected object is called.

35.e/2

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

Revised the note on use of the name of a protected type within itself to reflect the exception for anonymous access types.

Incompatibilities With Ada 2005

35.g/3
correction

When an inherited subprogram is implemented by a protected function, the first parameter has to be an in parameter, but not an access-to-variable type. Original Ada 2005 allowed access-to-variable parameters in this case; the parameter will need to be changed to access-to-constant with the addition of the constant keyword.

Extensions to Ada 2005

35.h/3

An optional aspect_specification can be used in a protected_type_declaration, a single_protected_declaration, and a protected_body. This is described in 13.1.1.

Wording Changes from Ada 2005

35.i/3
correction

Clarified that an inherited subprogram of a progenitor is overridden when it is implemented by an entry or subprogram.

35.j/3
correction

Added the missing defining name in the no conflicting primitive operation rule.

Extensions to Ada 2012

35.k/5

Corrigendum: Null procedures and expression functions are allowed in protected bodies. We consider this an omission, as there is no reason why the convenient shorthand notations should not be allowed in this context.