9.5.1 Protected Subprograms and Protected Actions
A
protected subprogram is a subprogram declared immediately within
a
protected_definition.
Protected procedures provide exclusive read-write access to the data
of a protected object; protected functions provide concurrent read-only
access to the data.
Static Semantics
[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).
Exclusive_Functions
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).
Aspect Description for Exclusive_Functions:
Specifies mutual exclusion behavior of protected functions in a protected
type.
{
AI12-0129-1}
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.
Dynamic Semantics
For the execution of a call on
a protected subprogram, the evaluation of the
name
or
prefix
and of the parameter associations, and any assigning back of
in out
or
out parameters, proceeds as for a normal subprogram call (see
6.4). If the call is an internal call (see
9.5), 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
action 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).]
{
AI12-0129-1}
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:
{
AI12-0129-1}
Starting a protected action
on a protected object corresponds to
acquiring the execution resource
associated with the protected object, either for exclusive read-write
access if the protected action is for a call on an exclusive protected
operation, or for concurrent read-only access otherwise;
Completing
the protected action corresponds to
releasing the associated execution
resource.
{
AI12-0129-1}
[After performing an exclusive protected operation on a protected object,
but prior to completing the associated protected action, the entry queues
(if any) of the protected object are serviced (see
9.5.3).]
{
AI12-0119-1}
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
{
AI12-0064-2}
During a protected action, it is a bounded error
to invoke an operation that is potentially blocking (see
9.5).
Paragraphs 9 through
16 were moved to 9.5.
{
AI12-0439-1}
If the bounded error is detected, Program_Error is
raised. If not detected, the bounded error can result in deadlock or
a (nested) protected action on the same target object.
Discussion: {
AI95-00305-01}
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).
{
AI12-0064-2}
{
AI12-0247-1}
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.
Reason: {
AI12-0247-1}
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.
NOTE 1 {
AI12-0276-1}
{
AI12-0440-1}
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 can be consuming processing resources
while it awaits its turn. Unless there is an admission policy (see
D.4.1)
in effect, there is no language-defined ordering or queuing presumed
for tasks competing to start a protected action — on a multiprocessor
such tasks can use busy-waiting; for further monoprocessor and multiprocessor
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.
NOTE 2 {
AI12-0440-1}
The body of a protected unit can contain declarations and bodies for
local subprograms. These are not visible outside the protected unit.
NOTE 3 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.
NOTE 4 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 operation.
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.
NOTE 5 {
AI95-00305-01}
{
AI12-0064-2}
{
AI12-0440-1}
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). The
pragma
Detect_Blocking can be used to ensure that any remaining executions of
potentially blocking operations during a protected action raise Program_Error.
See
H.5.
Discussion: {
AI12-0064-2}
The deadlock case cannot be detected at compile-time, so pragma Detect_Blocking
is needed to give it consistent behavior.
Examples
Examples of protected
subprogram calls (see 9.4):
Shared_Array.Set_Component(N, E);
E := Shared_Array.Component(M);
Control.Release;
Wording Changes from Ada 95
{
AI95-00305-01}
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
{
AI12-0129-1}
Corrigendum: Aspect Exclusive_Functions is
new. The term “exclusive protected operations” is new.
Wording Changes from Ada 2012
{
AI12-0064-2}
Moved the definition of potentially blocking operations to
9.5,
so it could be integrated into the definition of the Nonblocking aspect.
{
AI12-0247-1}
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