From: John Wilkinson on
select
PID.Call; --an entry call to task PID
Status := P;
then abort
delay 10.0;
Status := Q;
end select;

Is the above valid Ada code? It's been handed to me by someone, I
don't really know much about Ada, and I'm supposed to understand
what it does. I can't find any information on "abort" or even
"then".

If it's valid, what does it do?
From: Dmitry A. Kazakov on
On Wed, 28 Apr 2010 18:16:44 +0000 (UTC), John Wilkinson wrote:

> select
> PID.Call; --an entry call to task PID
> Status := P;
> then abort
> delay 10.0;
> Status := Q;
> end select;
>
> Is the above valid Ada code? It's been handed to me by someone, I
> don't really know much about Ada, and I'm supposed to understand
> what it does. I can't find any information on "abort" or even
> "then".

See ARM 9.7.4 Asynchronous Transfer of Control

> If it's valid, what does it do?

It waits 10s and then assigns Q to Status. But if within these 10s + the
time needed to assign Status, the task PID accepts Call, then the sequence
introduced by "then abort" is aborted, Call is performed, and finally, P is
assigned to Status.

Note that the recommended way to do this is the timed entry call ARM 9.7.2:

select
PID.Call; -- call to task PID
Status := P;
or delay 10.0; -- wait to longer than 10s
Status := Q;
end select;

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Jeffrey R. Carter on
Dmitry A. Kazakov wrote:
>
> It waits 10s and then assigns Q to Status. But if within these 10s + the
> time needed to assign Status, the task PID accepts Call, then the sequence
> introduced by "then abort" is aborted, Call is performed, and finally, P is
> assigned to Status.

That's a good explanation for the OP.

Technically, though, it isn't quite true: if the entry call *completes* then the
abortable part (after "then abort") is aborted: "If the triggering_statement
completes other than due to cancellation, the abortable_part is aborted". The
abortable part only begins execution if the entry call is queued, either
immediately or by requeue-with-abort.

If the abortable part completes, then it attempts to cancel the entry call, even
if it has been "selected" (is no longer queued but has not completed).

For a timed entry call, no attempt is made to cancel the entry call if the entry
has been "selected". This difference in semantics may be why this code uses
asynchronous transfer of control rather than a timed entry call.

But I doubt it.

--
Jeff Carter
"From this day on, the official language of San Marcos will be Swedish."
Bananas
28
From: Dmitry A. Kazakov on
On Wed, 28 Apr 2010 14:00:50 -0700, Jeffrey R. Carter wrote:

> Technically, though, it isn't quite true: if the entry call *completes* then the
> abortable part (after "then abort") is aborted: "If the triggering_statement
> completes other than due to cancellation, the abortable_part is aborted".

This is a very interesting question. I don't know if this wording indeed
requires the rendezvous and the abortable part to execute in parallel.
Otherwise, in effect, fetching the call from the queue must abort. I would
expect rather the latter, because the former would be quite difficult to
implement. The following example illustrates the case:

protected Event is
procedure Signal;
entry Wait;
private
Signaled : Boolean := False;
end Event;

protected body Event is
procedure Signal is
begin
Signaled := True;
end Signal;
entry Wait when Signaled is
begin
Signaled := False;
end;
end Event;

task PID is
entry Call;
end PID;

task body PID is
begin
accept Call do Event.Wait; end;
end PID;

begin
select
PID.Call; -- Blocked in the rendezvous
then abort
delay 2.0;
Event.Signal; -- Releases the rendezvous after 2s
end select;

If abort to happen after *completion* of the rendezvous then the above
shall not deadlock. (I checked this under GNAT/Windows, it deadlocks
there.)

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Jeffrey R. Carter on
Dmitry A. Kazakov wrote:
>
> protected Event is
> procedure Signal;
> entry Wait;
> private
> Signaled : Boolean := False;
> end Event;
>
> protected body Event is
> procedure Signal is
> begin
> Signaled := True;
> end Signal;
> entry Wait when Signaled is
> begin
> Signaled := False;
> end;
> end Event;
>
> task PID is
> entry Call;
> end PID;
>
> task body PID is
> begin
> accept Call do Event.Wait; end;
> end PID;
>
> begin
> select
> PID.Call; -- Blocked in the rendezvous
> then abort
> delay 2.0;
> Event.Signal; -- Releases the rendezvous after 2s
> end select;
>
> If abort to happen after *completion* of the rendezvous then the above
> shall not deadlock. (I checked this under GNAT/Windows, it deadlocks
> there.)

Here the call to PID.Call is not queued, but is accepted immediately, and does
not encounter an explicit requeue-with-abort. I guess that queuing on Event.Wait
does not count as equivalent to requeue-with-abort (unless this is a compiler
error; any language lawyers care to comment?). So the abortable part is never
executed, leading to deadlock.

It will be interesting to add "delay 1.0;" to the task before the accept, and
see if that changes the behavior. Then the call to PID.Call will queue, and the
abortable part should execute.

--
Jeff Carter
"From this day on, the official language of San Marcos will be Swedish."
Bananas
28