9.5.3 Entry Calls
[An
entry_call_statement
(an
entry call) can appear in various contexts.]
A
simple entry call is a stand-alone statement that represents an
unconditional call on an entry of a target task or a protected object.
[Entry calls can also appear as part of
select_statements
(see
9.7).]
Syntax
Name Resolution Rules
The
entry_name
given in an
entry_call_statement
shall resolve to denote an entry. The rules for parameter associations
are the same as for subprogram calls (see
6.4
and
6.4.1).
Static Semantics
[The
entry_name
of an
entry_call_statement
specifies (explicitly or implicitly) the target object of the call, the
entry or entry family, and the entry index, if any (see
9.5).]
Dynamic Semantics
Under
certain circumstances (detailed below), an entry of a task or protected
object is checked to see whether it is
open or
closed:
{
AI05-0264-1}
An entry of a protected object
is open if the
condition
of the
entry_barrier
of the corresponding
entry_body
evaluates to True; otherwise, it is closed.
If the
evaluation of the
condition
propagates an exception, the exception Program_Error is propagated to
all current callers of all entries of the protected object.
Reason: An exception during barrier evaluation
is considered essentially a fatal error. All current entry callers are
notified with a Program_Error. In a fault-tolerant system, a protected
object might provide a Reset protected procedure, or equivalent, to support
attempts to restore such a "broken" protected object to a reasonable
state.
Discussion: Note that the definition
of when a task entry is open is based on the state of the (accepting)
task, whereas the "openness" of a protected entry is defined
only when it is explicitly checked, since the barrier expression needs
to be evaluated. Implementation permissions are given (below) to allow
implementations to evaluate the barrier expression more or less often
than it is checked, but the basic semantic model presumes it is evaluated
at the times when it is checked.
For
the execution of an
entry_call_statement,
evaluation of the
name
and of the parameter associations is as for a subprogram call (see
6.4).
The entry call is then
issued: For a call
on an entry of a protected object, a new protected action is started
on the object (see
9.5.1). The named entry
is checked to see if it is open;
if open, the entry
call is said to be
selected immediately, and the execution of
the call proceeds as follows:
For a call on an open entry of a task, the accepting
task becomes ready and continues the execution of the corresponding
accept_statement
(see
9.5.2).
For a call on an open entry of a protected object,
the corresponding
entry_body
is executed (see
9.5.2) as part of the protected
action.
If the
accept_statement
or
entry_body
completes other than by a requeue (see
9.5.4),
return is made to the caller (after servicing the entry queues —
see below); any necessary assigning back of formal to actual parameters
occurs, as for a subprogram call (see
6.4.1);
such assignments take place outside of any protected action.
Ramification: The return to the caller
will generally not occur until the protected action completes, unless
some other thread of control is given the job of completing the protected
action and releasing the associated execution resource.
If the named entry is closed, the entry call is added
to an
entry queue (as part of the protected action, for a call
on a protected entry), and the call remains queued until it is selected
or cancelled;
there is a separate (logical) entry
queue for each entry of a given task or protected object [(including
each entry of an entry family)].
When
a queued call is
selected, it is removed from its entry queue.
Selecting a queued call from a particular entry queue is called
servicing
the entry queue. An entry with queued calls can be serviced under the
following circumstances:
{
AI12-0129-1}
If after performing, as part of a protected action on the associated
protected object, an exclusive protected operation on the object, the
entry is checked and found to be open.
If there is at least one call
on a queue corresponding to an open entry, then one such call is selected
according to the
entry queuing policy in effect (see below), and
the corresponding
accept_statement
or
entry_body
is executed as above for an entry call that is selected immediately.
The entry queuing policy controls
selection among queued calls both for task and protected entry queues.
The default entry queuing policy
is to select calls on a given entry queue in order of arrival. If calls
from two or more queues are simultaneously eligible for selection, the
default entry queuing policy does not specify which queue is serviced
first. Other entry queuing policies can be specified by
pragmas
(see
D.4).
For a protected object, the above servicing of entry
queues continues until there are no open entries with queued calls, at
which point the protected action completes.
Discussion: While servicing the entry
queues of a protected object, no new calls can be added to any entry
queue of the object, except due to an internal requeue (see
9.5.4).
This is because the first step of a call on a protected entry is to start
a new protected action, which implies acquiring (for exclusive read-write
access) the execution resource associated with the protected object,
which cannot be done while another protected action is already in progress.
Ramification: For a call on a protected
entry, the caller is not blocked if the call is selected immediately,
unless a requeue causes the call to be queued.
An attempt can be made to cancel
an entry call upon an abort (see
9.8) and as
part of certain forms of
select_statement
(see
9.7.2,
9.7.3,
and
9.7.4). The cancellation does not take
place until a point (if any) when the call is on some entry queue, and
not protected from cancellation as part of a requeue (see
9.5.4);
at such a point, the call is removed from the entry queue and the call
completes due to the cancellation. The cancellation of a call on an entry
of a protected object is a protected action[, and as such cannot take
place while any other protected action is occurring on the protected
object. Like any protected action, it includes servicing of the entry
queues (in case some entry barrier depends on a Count attribute).]
Implementation Note: {
AI95-00114-01}
In the case of an attempted cancellation due to abort, this removal might
have to be performed by the calling task itself if the ceiling priority
of the protected object is lower than the priority of the task initiating
the abort.
A call on an entry of a task
that has already completed its execution raises the exception Tasking_Error
at the point of the call; similarly, this exception is raised at the
point of the call if the called task completes its execution or becomes
abnormal before accepting the call or completing the rendezvous (see
9.8). This applies equally to a simple entry
call and to an entry call as part of a
select_statement.
Implementation Permissions
An implementation may perform the sequence of steps
of a protected action using any thread of control; it need not be that
of the task that started the protected action. If an
entry_body
completes without requeuing, then the corresponding calling task may
be made ready without waiting for the entire protected action to complete.
Reason: These permissions are intended
to allow flexibility for implementations on multiprocessors. On a monoprocessor,
which thread of control executes the protected action is essentially
invisible, since the thread is not abortable in any case, and the "current_task"
function is not guaranteed to work during a protected action (see
C.7.1).
{
AI12-0129-1}
When the entry of a protected object is checked to see whether it is
open, the implementation need not reevaluate the
condition
of the corresponding
entry_barrier
if no variable or attribute referenced by the
condition
(directly or indirectly) has been altered by the execution (or cancellation)
of a call to an exclusive protected operation of the object since the
condition
was last evaluated.
Ramification: Changes to variables referenced
by an entry barrier that result from actions outside of a call to an
exclusive protected operation of the protected object need not be "noticed."
For example, if a global variable is referenced by an entry barrier,
it should not be altered (except as part of a protected action on the
object) any time after the barrier is first evaluated. In other words,
globals can be used to "parameterize" a protected object, but
they cannot reliably be used to control it after the first use of the
protected object.
Implementation Note: Note that even if
a global variable is volatile, the implementation need only reevaluate
a barrier if the global is updated during a protected action on the protected
object. This ensures that an entry-open bit-vector implementation approach
is possible, where the bit-vector is computed at the end of a protected
action, rather than upon each entry call.
An implementation may evaluate the
conditions
of all
entry_barriers
of a given protected object any time any entry of the object is checked
to see if it is open.
Ramification: In other words, any side
effects of evaluating an entry barrier should be innocuous, since an
entry barrier might be evaluated more or less often than is implied by
the "official" dynamic semantics.
Implementation Note: It is anticipated
that when the number of entries is known to be small, all barriers will
be evaluated any time one of them needs to be, to produce an "entry-open
bit-vector." The appropriate bit will be tested when the entry is
called, and only if the bit is false will a check be made to see whether
the bit-vector might need to be recomputed. This should allow an implementation
to maximize the performance of a call on an open entry, which seems like
the most important case.
In addition to the entry-open bit-vector, an
"is-valid" bit is needed per object, which indicates whether
the current bit-vector setting is valid. A "depends-on-Count-attribute"
bit is needed per type. The "is-valid" bit is set to false
(as are all the bits of the bit-vector) when the protected object is
first created, as well as any time an exception is propagated from computing
the bit-vector. Is-valid would also be set false any time the Count is
changed and "depends-on-Count-attribute" is true for the type,
or a protected procedure or entry returns indicating it might have updated
a variable referenced in some barrier.
A single procedure can be compiled to evaluate
all of the barriers, set the entry-open bit-vector accordingly, and set
the is-valid bit to true. It could have a "when others" handler
to set them all false, and call a routine to propagate Program_Error
to all queued callers.
For protected types where the number of entries
is not known to be small, it makes more sense to evaluate a barrier only
when the corresponding entry is checked to see if it is open. It isn't
worth saving the state of the entry between checks, because of the space
that would be required. Furthermore, the entry queues probably want to
take up space only when there is actually a caller on them, so rather
than an array of all entry queues, a linked list of nonempty entry queues
make the most sense in this case, with the first caller on each entry
queue acting as the queue header.
When an attempt is made to cancel an entry call,
the implementation need not make the attempt using the thread of control
of the task (or interrupt) that initiated the cancellation; in particular,
it may use the thread of control of the caller itself to attempt the
cancellation, even if this might allow the entry call to be selected
in the interim.
Reason: Because cancellation of a protected
entry call is a protected action (which helps make the Count attribute
of a protected entry meaningful), it might not be practical to attempt
the cancellation from the thread of control that initiated the cancellation.
For example, if the cancellation is due to the expiration of a delay,
it is unlikely that the handler of the timer interrupt could perform
the necessary protected action itself (due to being on the interrupt
level). Similarly, if the cancellation is due to an abort, it is possible
that the task initiating the abort has a priority higher than the ceiling
priority of the protected object (for implementations that support ceiling
priorities). Similar considerations could apply in a multiprocessor situation.
28 If an exception is raised during the
execution of an
entry_body,
it is propagated to the corresponding caller (see
11.4).
29 For a call on a protected entry, the
entry is checked to see if it is open prior to queuing the call, and
again thereafter if its Count attribute (see
9.9)
is referenced in some entry barrier.
Ramification: Given this, extra care
is required if a reference to the Count attribute of an entry appears
in the entry's own barrier.
Reason: An entry is checked to see if
it is open prior to queuing to maximize the performance of a call on
an open entry.
30 In addition to simple entry calls, the
language permits timed, conditional, and asynchronous entry calls (see
9.7.2,
9.7.3,
and see
9.7.4).
Ramification: A task can call its own
entries, but the task will deadlock if the call is a simple entry call.
31 The
condition
of an
entry_barrier
is allowed to be evaluated by an implementation more often than strictly
necessary, even if the evaluation might have side effects. On the other
hand, an implementation need not reevaluate the
condition
if nothing it references was updated by an intervening protected action
on the protected object, even if the
condition
references some global variable that might have been updated by an action
performed from outside of a protected action.
Examples
Examples of entry
calls:
Agent.Shut_Down; --
see 9.1
Parser.Next_Lexeme(E); --
see 9.1
Pool(5).Read(Next_Char); --
see 9.1
Controller.Request(Low)(Some_Item); --
see 9.1
Flags(3).Seize; --
see 9.4
Wording Changes from Ada 2012
{
AI12-0129-1}
Corrigendum: Revised wording to talk about “exclusive protected
operations” (see
9.5.1).
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe