9.5.1 Protected Subprograms and Protected Actions
{protected subprogram}
{protected procedure}
{protected function}
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).
Dynamic Semantics
{execution (protected
subprogram call) [partial]} 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).]
{protected
action} 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 protected function. This rule is expressible in terms of the
execution resource associated with the protected object:
{protected action
(start)} {acquire
(execution resource associated with protected object)} Starting
a protected action on a protected object corresponds to
acquiring
the execution resource associated with the protected object, either for
concurrent read-only access if the protected action is for a call on
a protected function, or for exclusive read-write access otherwise;
{protected action
(complete)} {release
(execution resource associated with protected object)} Completing
the protected action corresponds to
releasing the associated execution
resource.
[After performing an operation on a protected object
other than a call on a protected function, but prior to completing the
associated protected action, the entry queues (if any) of the protected
object are serviced (see
9.5.3).]
Bounded (Run-Time) Errors
{bounded
error (cause) [partial]} During a protected
action, it is a bounded error to invoke an operation that is
potentially
blocking.
{potentially blocking operation}
{blocking, potentially}
The following are defined to be potentially blocking
operations:
Reason: 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 burden.
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.
{Program_Error (raised
by failure of run-time check)} 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.
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).
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.
Discussion: {
AI95-00178-01}
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.
18 If two tasks both try to start a protected
action on a protected object, and at most one is calling a protected
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. 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;
for monoprocessor 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.
19 The body of a protected unit may contain
declarations and bodies for local subprograms. These are not visible
outside the protected unit.
20 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.
21 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.
22 {
AI95-00305-01}
The
pragma
Detect_Blocking may be used to ensure that all executions of potentially
blocking operations during a protected action raise Program_Error. See
H.5.
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.