9.5.1 Protected Subprograms and Protected Actions
is a subprogram declared immediately within
Protected procedures provide exclusive read-write access to the data
of a protected object; protected functions provide concurrent read-only
access to the data.
[Within the body of a protected function (or a function
declared immediately within a protected_body
the current instance of the enclosing protected unit is defined to be
a constant (that is, its subcomponents may be read but not updated).
Within the body of a protected procedure (or a procedure declared immediately
within a protected_body
and within an entry_body
the current instance is defined to be a variable (updating is permitted).]
Ramification: The current instance is
like an implicit parameter, of mode in for a protected function,
and of mode in out for a protected procedure (or protected entry).
The type of aspect Exclusive_Functions is Boolean.
If not specified (including by inheritance), the aspect is False.
A value of True for this aspect indicates that
protected functions behave in the same way as protected procedures with
respect to mutual exclusion and queue servicing (see below).
for Exclusive_Functions: Specifies
mutual exclusion behavior of protected functions in a protected type.
A protected procedure or entry is an exclusive
protected operation. A protected function of a protected
type P is an exclusive protected operation if the Exclusive_Functions
aspect of P is True.
For the execution of a call on
a protected subprogram, the evaluation of the name
and of the parameter associations, and any assigning back of in out
parameters, proceeds as for a normal subprogram call (see
). If the call is an internal call (see
), the body of the subprogram is executed
as for a normal subprogram call. If the call is an external call, then
the body of the subprogram is executed as part of a new protected
on the target protected object; the protected action completes
after the body of the subprogram is executed. [A protected action can
also be started by an entry call (see 9.5.3
A new protected action is not started on a protected
object while another protected action on the same protected object is
underway, unless both actions are the result of a call on a nonexclusive
protected function. This rule is expressible in terms of the execution
resource associated with the protected object:
a protected action
on a protected object corresponds to acquiring
the execution resource
associated with the protected object, either for exclusive
read-write concurrent read-only
if the protected action is for a call on an exclusive
protected operation a protected function
or for concurrent read-only exclusive
the protected action corresponds to releasing
the associated execution
[After performing an exclusive protected
on a protected object other than a call on a protected
, but prior to completing the associated protected action,
the entry queues (if any) of the protected object are serviced (see 9.5.3
If a parallel construct occurs within a protected
action, no new logical threads of control are created. Instead, each
element of the parallel construct that would have become a separate logical
thread of control executes on the logical thread of control that is performing
the protected action. If there are multiple such elements initiated at
the same point, they execute in an arbitrary order.
Reason: It would
be feasible to allow multiple logical threads of control within a protected
action, but it would significantly complicate the definition of “sequential”
and “concurrent” actions, since we generally presume that
everything occuring within protected actions of a given protected object
is sequential. We could simply disallow any use of parallel constructs,
but that seems unnecessary, particularly as a parallel construct might
be buried within a subprogram that is declared Nonblocking.
Bounded (Run-Time) Errors
During a protected action,
it is a bounded error to invoke an operation that is potentially
blocking (see 9.5). potentially
blocking. The following are
defined to be potentially blocking operations:
9 through 16 were moved to 9.5.
Some of these operations are not directly blocking.
However, they are still treated as bounded errors during a protected
action, because allowing them might impose an undesirable implementation
task creation or activation;
an external call on a protected
subprogram (or an external requeue) with the same target object as that
of the protected action;
Reason: This is
really a deadlocking call, rather than a blocking call, but we include
it in this list for simplicity.
a call on a subprogram whose
body contains a potentially blocking operation.
Reason: This allows
an implementation to check and raise Program_Error as soon as a subprogram
is called, rather than waiting to find out whether it actually reaches
the potentially blocking operation. This in turn allows the potentially
blocking operation check to be performed prior to run time in some environments.
If the bounded error is detected,
Program_Error is raised. If not detected, the bounded error might result
in deadlock or a (nested) protected action on the same target object.
By “nested protected action”, we mean that an additional
protected action can be started by another task on the same protected
object. This means that mutual exclusion may be broken in this bounded
error case. A way to ensure that this does not happen is to use pragma
Detect_Blocking (see H.5
During a protected action, a call on a subprogram
whose body contains a potentially blocking operation is a bounded error.
If the bounded error is detected,
Program_Error is raised; otherwise, the call proceeds normally. Certain
language-defined subprograms are potentially blocking. In particular,
the subprograms of the language-defined input-output packages that manipulate
files (implicitly or explicitly) are potentially blocking. Other potentially
blocking subprograms are identified where they are defined. When not
specified as potentially blocking, a language-defined subprogram is nonblocking.
Any subprogram in a language-defined input-output
package that has a file parameter or result or operates on a default
file is considered to manipulate a file. An instance of a language-defined
input-output generic package provides subprograms that are covered by
this rule. The only subprograms in language-defined input-output packages
not covered by this rule (and thus not potentially blocking) are the
Get and Put routines that take string parameters defined in the packages
nested in Text_IO.
This allows an implementation to check and raise
Program_Error as soon as a subprogram is called, rather than waiting
to find out whether it actually reaches the potentially blocking operation.
If the call proceeds normally, reaching the potentially blocking operation
is a separate bounded error, covered by the previous rules.
If two tasks both try to start a protected action on a protected object,
and at most one is calling a protected nonexclusive
function, then only one of the tasks can proceed. Although the
other task cannot proceed, it is not considered blocked, and it might
be consuming processing resources while it awaits its turn. Unless
there is an admission policy (see D.4.1)
in effect, there There
is no language-defined
ordering or queuing presumed for tasks competing to start a protected
action — on a multiprocessor such tasks might use busy-waiting;
considerations, see D.3
“Priority Ceiling Locking
Discussion: The intended implementation
on a multi-processor is in terms of “spin locks” —
the waiting task will spin.
20 The body of a protected unit may contain
declarations and bodies for local subprograms. These are not visible
outside the protected unit.
21 The body of a protected function can
contain internal calls on other protected functions, but not protected
procedures, because the current instance is a constant. On the other
hand, the body of a protected procedure can contain internal calls on
both protected functions and procedures.
22 From within a protected action, an internal
call on a protected subprogram, or an external call on a protected subprogram
with a different target object is not considered a potentially blocking
Reason: This is because a task is not
considered blocked while attempting to acquire the execution resource
associated with a protected object. The acquisition of such a resource
is rather considered part of the normal competition for execution resources
between the various tasks that are ready. External calls that turn out
to be on the same target object are considered potentially blocking,
since they can deadlock the task indefinitely.
The aspect Nonblocking can be specified True on
the definition of a protected unit in order to reject most attempts to
use potentially blocking operations within the protected unit (see 9.5).
Detect_Blocking may be used to ensure that any
executions of potentially
blocking operations during a protected action raise Program_Error. See
The deadlock case cannot be detected at compile-time,
so pragma Detect_Blocking is needed to give it consistent behavior.
Examples of protected
subprogram calls (see 9.4):
E := Shared_Array.Component(M);
Wording Changes from Ada 95
Added a note pointing out the existence of pragma
Detect_Blocking. This pragma can be used to ensure portable (somewhat
pessimistic) behavior of protected actions by converting the Bounded
Error into a required check.
Extensions to Ada 2012
Exclusive_Functions is new. The term “exclusive protected operations”
Wording Changes from Ada 2012
Moved the definition of potentially blocking operations
to 9.5, so it could be integrated into the
definition of the Nonblocking aspect.
Correction: Added a separate bounded error
for a subprogram containing a blocking operation, to keep compatibility
with Ada 95 rules without requiring a correct implementation of pragma
Detect_Blocking to do full program analysis.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe