Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

requeue with abort and timed call

21 views
Skip to first unread message

ishi...@arielworks.com

unread,
Dec 28, 2008, 8:24:25 AM12/28/08
to
Hi,

I found a strange behavior of GNAT about requeue-with-abort.

In the RM, I found the following definition
>16 * if the original entry call was timed (or conditional), then the original expiration time is the expiration time for the requeued call.
(http://www.adaic.org/standards/1zrm/html/RM-9-5-4.html)

But In the following code, its timed entry call had never be aborted.
When "Original_Call" does not have "delay 3.0", it got aborted.

It seems that "Original_Call" takes more than 1.0 second(over the
expiration time of the timed call), "with abort" does not work as
intended.
I think the requeued call should be aborted in this case.

Does anyone understand whether this behavior is a bug or not?
I could not find any definition supporting this behavior on RM.

------
with Ada.Text_IO; use Ada.Text_IO;
procedure Req is
task T is
entry Original_Call;
entry Requeued_Call;
end T;

task body T is
begin
loop
accept Original_Call do
Put_Line ("Original Call...");
-- takes three seconds
delay 3.0;
Put_Line ("Original Call Done");
requeue Requeued_Call with abort;
end Original_Call;

-- inifinity loop
loop
delay 10.0;
end loop;

-- will be never accepted
accept Requeued_Call do
Put_Line ("Requeued_Call");
end Requeued_Call;
end loop;
end T;

begin
select
T.Original_Call;
or
-- just wait 1 second
delay 1.0;
Put_Line ("Aborting");
end select;
Put_Line ("Parent Done");
end Req;

Dmitry A. Kazakov

unread,
Dec 28, 2008, 9:30:16 AM12/28/08
to
On Sun, 28 Dec 2008 05:24:25 -0800 (PST), ishi...@arielworks.com wrote:

> I found a strange behavior of GNAT about requeue-with-abort.

[...]

This looks like a bug to me. The entry call must be aborted after 3 seconds
waiting according to 9.5.4 (15).

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

sjw

unread,
Dec 28, 2008, 10:53:25 AM12/28/08
to
9.7.2(5) says "If the call is queued (including due to a requeue-with-
abort), and not selected before the expiration time is reached, an
attempt to cancel the call is made." But in your code, the call has
been selected, just not completed (English usage, not RM language).

If you say

accept Original_Call do
Put_Line ("Original Call...");

requeue Requeued_Call with abort;
end Original_Call;
-- takes three seconds
delay 3.0;
Put_Line ("Original Call Done");

it behaves more reasonably (I think; it depends what you were trying
to do):

./req
Original Call...
Aborting
Parent Done
Original Call Done
^C

christo...@eurocopter.com

unread,
Dec 28, 2008, 10:53:44 AM12/28/08
to
I do not see a problem with aborting. Why should the entry call be
aborted?

After elaboration of the declaration part of Req, at the begin
keyword, the task T starts and is blocked at entry Original_Call.
Concurrently the main task proceeds to the select statement, where it
waits at most 1 s for the rendezvous to take place. Since T is already
waiting there, the rendezvous takes place, so the "or delay" is never
executed. The rendezvous terminates after 3 s, and T goes into the
infinte loop.

The main task, the master of T, now waits for T to terminate (which
will never happen), so the process never ends.

This is not an asynchronous select:

select
delay ...;
then abort
Original_Call;
end select;

christo...@eurocopter.com

unread,
Dec 28, 2008, 11:09:01 AM12/28/08
to
With 9.7.2(5) in mind, I think the delay in the select statement must
be longer than it takes T to arrive at the requeue.nIn your case, the
delay expires before T reaches the requeue. If you increase the delay
to 4 s

select
T.Original_Call;
or
-- just wait longer than it takes to reach the requeue
delay 4.0;


Put_Line ("Aborting");
end select;

the program will behave as expected.

Dmitry A. Kazakov

unread,
Dec 28, 2008, 12:16:34 PM12/28/08
to
On Sun, 28 Dec 2008 07:53:44 -0800 (PST), christo...@eurocopter.com
wrote:

> I do not see a problem with aborting. Why should the entry call be
> aborted?

Because the request remains *queued* after expiration of the timed entry
call.

-----------------
If GNAT's implementation is conform to RM, then it is a language bug. The
behavior of a timed entry call shall not depend on whether the entry body
is occasionally split into several pieces glued by requeue-with-abort
statements. I am not a language lawyer, but I doubt that such a huge issue
was overlooked.

ishi...@arielworks.com

unread,
Dec 28, 2008, 12:46:31 PM12/28/08
to
Thank you for your replyes.

>But in your code, the call has
>been selected, just not completed (English usage, not RM language).

>Concurrently the main task proceeds to the select statement, where it


>waits at most 1 s for the rendezvous to take place. Since T is already
>waiting there, the rendezvous takes place, so the "or delay" is never
>executed. The rendezvous terminates after 3 s, and T goes into the
>infinte loop.

As you said, it is a correct that Original_Call is not aborted.
But I think that Requeued_Call must be aborted when the original
expiration time is already over and the "requeued" entry call is on
the queue.
Of course, if the "requeue" is not followed by "with abort",
Requeued_Call will be not aborted.

Time line is like following;
0.0 The call of Original_Call
*1.0 The expiration time of original call(select or delay)
3.0 Complete Original_Call
*3.0 Queued on Requeued_Call

>With 9.7.2(5) in mind, I think the delay in the select statement must
>be longer than it takes T to arrive at the requeue.nIn your case, the
>delay expires before T reaches the requeue. If you increase the delay
>to 4 s

If the delay changed to 4;
0.0 The call of Original_Call
3.0 Complete Original_Call
*3.0 Queued on Requeued_Call
*4.0 The expiration time of original call(select or delay)
N/A Reueued_Call will be never called

In this case, the Requeued_Call will be aborted.

But, please think about following code.
Sometimes we cannot decide how long time procedures take.
For example, if "Procedure_Tkaes_0sec_to_60sec" takes for 0.3 or to
0.0999..., Requeued_Call will be aborted.
but, if it takes over 1 sec, Requeued_Class will be not aborted
forever.
This behavior is repugnant, I think.
---


accept Original_Call do
Put_Line ("Original Call...");

-- We have no idea about how long time following procedure
takes.
-- e.g.) 0 sec. in the best case, 60 sec. in the worth case.
Procedure_Takes_0sec_to_60sec


Put_Line ("Original Call Done");
requeue Requeued_Call with abort;
end Original_Call;

---

(In addition, we can give even negative duration to delay statements
in some cases.
"delay until" statements is available for fixed expiration time.)

Robert A Duff

unread,
Dec 28, 2008, 4:42:09 PM12/28/08
to

I suspect this is a bug in GNAT, not a bug in the RM.

But I admit I didn't study the example very carefully.

- Bob

christo...@eurocopter.com

unread,
Dec 29, 2008, 5:23:21 AM12/29/08
to
On 28 Dez., 18:46, ishik...@arielworks.com wrote:
> But, please think about following code.
> Sometimes we cannot decide how long time procedures take.
> For example, if "Procedure_Tkaes_0sec_to_60sec" takes for 0.3 or to
> 0.0999..., Requeued_Call will be aborted.
> but, if it takes over 1 sec, Requeued_Class will be not aborted
> forever.
> This behavior is repugnant, I think.

I think this is very idea behind a timed entry call - to be aborted, a
call must be queued when the delay expires; when it executes, it will
not be aborted.

Imagine you are waiting in a queue. If you are not serviced within a
certain time (there is still someone before you), you leave the queue
("or delay", including the case that you have been partly serviced and
are now waiting in another queue, i.e. "requeue with delay").

If you are serviced, but the service takes too long, you quit the
service - this is the asynchronous select (the one with "then abort").

Dmitry A. Kazakov

unread,
Dec 29, 2008, 5:55:09 AM12/29/08
to
On Mon, 29 Dec 2008 02:23:21 -0800 (PST), christo...@eurocopter.com
wrote:

No, apart from the race condition issue, the OP has already mentioned, the
call is not serviced. Servicing was initiated by accepting the entry call,
but then it was postponed using requeue.

My understanding is that for the observer (the caller) servicing is
semantically an atomic operation which ends with the last rendzevous or
protected action. It is not abortable during a rendezvous or a protected
action, but between them it well is (unless it was requeued without abort).

Jean-Pierre Rosen

unread,
Dec 29, 2008, 5:42:43 AM12/29/08
to
Dmitry A. Kazakov a écrit :

> On Sun, 28 Dec 2008 07:53:44 -0800 (PST), christo...@eurocopter.com
> wrote:
>
>> I do not see a problem with aborting. Why should the entry call be
>> aborted?
>
> Because the request remains *queued* after expiration of the timed entry
> call.
>
There seems to be some confusion about terms here.

For a timed entry call, the call is /cancelled/ if the it is not
accepted within the given time span. If the call has been requeued, it
is the beginning of the second accept that determines the deadline.

However, there is no /abort/ in this example. The "with abort" in the
requeue statement really means that the rendezvous will start over from
scratch, implying that the queued task could be aborted (or cancelled by
a timed entry call, or asynchronous transfer of control). Without the
"with abort", the second queue is considered as within the first
rendezvous, and a caller is abort-deferred during a rendezvous (and
since it is the acceptance time that matters for timed entry call, it
will be too late to cancel the call).

Anyway, it is clear that the second call should be cancelled in this
example.
--
---------------------------------------------------------
J-P. Rosen (ro...@adalog.fr)
Visit Adalog's web site at http://www.adalog.fr

christo...@eurocopter.com

unread,
Dec 29, 2008, 6:17:32 AM12/29/08
to
On 29 Dez., 11:55, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:

> My understanding is that for the observer (the caller) servicing is
> semantically an atomic operation which ends with the last rendzevous or
> protected action. It is not abortable during a rendezvous or a protected
> action, but between them it well is (unless it was requeued without abort).

But this is exactly what I meant to say.

Time line is like following;
0.0 The call of Original_Call

*1.0 The expiration time of original call, so no abort, because in
rendezvous
3.0 Complete Original_Call
*3.0 Queued on Requeued_Call, which is never serviced, but which is
also not abortable (no delay active).

So I do not see a problem with the behavious as shown in the OP's
example.

*3.0 Queued on Requeued_Call - hm, you think the delay is still
active? We need a language lawyer to enlighten us.

GNAT's behaviour follows my interpretation (I experimented a bit with
increasing the delays). Is this a bug and I am wrong (may well be)?

Dmitry A. Kazakov

unread,
Dec 29, 2008, 7:27:34 AM12/29/08
to
On Mon, 29 Dec 2008 03:17:32 -0800 (PST), christo...@eurocopter.com
wrote:

> On 29 Dez., 11:55, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
> wrote:
>> My understanding is that for the observer (the caller) servicing is
>> semantically an atomic operation which ends with the last rendzevous or
>> protected action. It is not abortable during a rendezvous or a protected
>> action, but between them it well is (unless it was requeued without abort).
>
> But this is exactly what I meant to say.
>
> Time line is like following;
> 0.0 The call of Original_Call
> *1.0 The expiration time of original call, so no abort, because in
> rendezvous
> 3.0 Complete Original_Call
> *3.0 Queued on Requeued_Call, which is never serviced, but which is
> also not abortable (no delay active).
>
> So I do not see a problem with the behavious as shown in the OP's
> example.
>
> *3.0 Queued on Requeued_Call - hm, you think the delay is still
> active? We need a language lawyer to enlighten us.

Yes, that is another way to say it. In my view the delay is always active,
because the caller cannot distinguish original and following calls. There
is only one call which can be logically aborted at any time before its
completion. Rendezvous and protected actions are considered semantically
instant for the caller. So servicing for the caller semantically consists
solely out of waiting for completion, there is no other visible components
in it.

A dual case is how internal protected actions are dealt with. When an
internal protected action is requeued to a closed entry, that ends the
action and will start a new one when the entry will open. The caller's
delay alternative can be selected before this new action start.

sjw

unread,
Dec 29, 2008, 4:05:58 PM12/29/08
to
On Dec 29, 10:42 am, Jean-Pierre Rosen <ro...@adalog.fr> wrote:

> Anyway, it is clear that the second call should be cancelled in this
> example.

The whole example is far from clear to me. If I were reviewing this
design I would have great difficulty understanding what the designer
was trying to do and whether the code achieved it.

The decision to requeue ought to be speedy. Requeue because you can't
deal with it now; don't ponder for 3 seconds and then decide to
requeue for a further arbitrary period.

If the code said

accept Original_Call do
Put_Line ("Original Call...");

delay 3.0;
Put_Line ("Original Call Done");

end Original_Call;

ie without the requeue, the timed entry call would not be cancelled,
because we're abort-deferred. So a caller design which relies on the
callee having/not having a requeue is broken.

Isn't it normally considered bad practice to do extensive processing
within the accept statement?

Jean-Pierre Rosen

unread,
Dec 30, 2008, 4:25:34 AM12/30/08
to
sjw a écrit :

> The whole example is far from clear to me. If I were reviewing this
> design I would have great difficulty understanding what the designer
> was trying to do and whether the code achieved it.
Sure, this is only an example to show an issue and demonstrate a problem.
In real life, you wouldn't have seconds, but micro-seconds. Would be
much more difficult to experiment with.

> The decision to requeue ought to be speedy. Requeue because you can't
> deal with it now; don't ponder for 3 seconds and then decide to
> requeue for a further arbitrary period.

Once again, the problem is not with the 3s. Could be much shorter.


>
> If the code said
>
> accept Original_Call do
> Put_Line ("Original Call...");
> delay 3.0;
> Put_Line ("Original Call Done");
> end Original_Call;
>
> ie without the requeue, the timed entry call would not be cancelled,
> because we're abort-deferred.

You are confusing things here, this has nothing to do with
abort-deferral. Abort-deferral means that the called task cannot be
aborted while engaged in a rendezvous. Timed entry call is about the
caller being removed from the queue if the delay expires before the
rendezvous has started.

> So a caller design which relies on the
> callee having/not having a requeue is broken.

No. A timed entry call means: "if the server does not arrive within XX
seconds, I give up". Requeue (with abort) allows the server to get the
request, inspect the parameters, and decide that this request cannot be
serviced now and put it back on wait. This is transparent as seen from
the client's side.

> Isn't it normally considered bad practice to do extensive processing
> within the accept statement?

Not within accept statements, but within protected subprograms or
entries. Accept statements are of a much higher level of abstraction
than POs.

christo...@eurocopter.com

unread,
Dec 30, 2008, 8:18:58 AM12/30/08
to
On 30 Dez., 10:25, Jean-Pierre Rosen <ro...@adalog.fr> wrote:
> No. A timed entry call means: "if the server does not arrive within XX
> seconds, I give up". Requeue (with abort) allows the server to get the
> request, inspect the parameters, and decide that this request cannot be
> serviced now and put it back on wait. This is transparent as seen from
> the client's side.

So what you say here is that the delay is still active in the time
line I gave in a previous post and Requeued_Call must be aborted? If
so, GNAT is definitively in error.

> Time line is like following;
> 0.0 The call of Original_Call
> *1.0 The expiration time of original call, so no abort, because in
> rendezvous
> 3.0 Complete Original_Call
> *3.0 Queued on Requeued_Call, which is never serviced, but which is
> also not abortable (no delay active).

> So I do not see a problem with the behavious as shown in the OP's
> example.

> *3.0 Queued on Requeued_Call - hm, you think the delay is still
> active? We need a language lawyer to enlighten us.

I guess this should be put to Ada-Comment for a definitive answer by
Ada mainainers (Randy, Tuck, etc.).

ishi...@arielworks.com

unread,
Dec 30, 2008, 10:54:57 AM12/30/08
to
Thank you very much for all responses.

On Dec 29, 7:23 pm, christoph.gr...@eurocopter.com wrote:
> I think this is very idea behind a timed entry call - to be aborted, a
> call must be queued when the delay expires; when it executes, it will
> not be aborted.

I thought the same way as you said once.
But I was not sure about that idea and it seemed repugnant (because
delay takes even negative delays).


On Dec 30, 10:18 pm, christoph.gr...@eurocopter.com wrote:
> So what you say here is that the delay is still active in the time
> line I gave in a previous post and Requeued_Call must be aborted? If
> so, GNAT is definitively in error.

I think Requeued_Call should be aborted.
Because RM-9.5.4(16) says;


>if the original entry call was timed (or conditional), then the original expiration time is the expiration time for the requeued call.

This sentence means that delay is still active on requeueing, I
believe.
(Of course I'm not a language lawyer, so I could be wrong.)


On Dec 30, 6:05 am, sjw <simon.j.wri...@mac.com> wrote:
> The whole example is far from clear to me. If I were reviewing this
> design I would have great difficulty understanding what the designer
> was trying to do and whether the code achieved it.

I'm sorry. My example code and messages might be bad to understand.
I've never used "requeue" in my codes and I found it on RM this time.
Wrote some codes to understand how "requeue" works, I got the
question.

The problem is why Requeued_Call is not aborted.
Magic numbers in the codes (e.g. delay "3.0" or "1.0") does not have
any special meanings.
"delay 3.0" is there just to make Original_Call take enough time over
the expiration time.


On Dec 30, 10:18 pm, christoph.gr...@eurocopter.com wrote:
> I guess this should be put to Ada-Comment for a definitive answer by
> Ada mainainers (Randy, Tuck, etc.).

Ok, I try to do.
I'll report the response to this thread when get it.

Jean-Pierre Rosen

unread,
Dec 30, 2008, 10:39:25 AM12/30/08
to
christo...@eurocopter.com a écrit :

> On 30 Dez., 10:25, Jean-Pierre Rosen <ro...@adalog.fr> wrote:
>> No. A timed entry call means: "if the server does not arrive within XX
>> seconds, I give up". Requeue (with abort) allows the server to get the
>> request, inspect the parameters, and decide that this request cannot be
>> serviced now and put it back on wait. This is transparent as seen from
>> the client's side.
>
> So what you say here is that the delay is still active in the time
> line I gave in a previous post and Requeued_Call must be aborted? If
> so, GNAT is definitively in error.
Yes, except that nobody gets aborted. The call is simply cancelled.

christo...@eurocopter.com

unread,
Dec 30, 2008, 12:01:20 PM12/30/08
to
On 30 Dez., 16:39, Jean-Pierre Rosen <ro...@adalog.fr> wrote:

> Yes, except that nobody gets aborted. The call is simply cancelled.

Of course, it's in the queue, so it's taken out of the queue, i.e. the
call is cancelled. Sorry for my wrong wording.

ishi...@arielworks.com

unread,
Dec 30, 2008, 12:16:44 PM12/30/08
to
I'm sorry. I should use not "abort" but "cancell".
My wording made confusion.


sjw

unread,
Dec 30, 2008, 4:33:51 PM12/30/08
to
On Dec 30, 9:25 am, Jean-Pierre Rosen <ro...@adalog.fr> wrote:
> sjw a écrit :
> > The whole example is far from clear to me. If I were reviewing this
> > design I would have great difficulty understanding what the designer
> > was trying to do and whether the code achieved it.
>
> Sure, this is only an example to show an issue and demonstrate a problem.
> In real life, you wouldn't have seconds, but micro-seconds. Would be
> much more difficult to experiment with.

My problem is rather more with the idea of doing work in the accept of
size comparable with the caller's permissible waiting period *and
then* requeueing.

> > The decision to requeue ought to be speedy. Requeue because you can't
> > deal with it now; don't ponder for 3 seconds and then decide to
> > requeue for a further arbitrary period.
>
> Once again, the problem is not with the 3s. Could be much shorter.

See above.

>
> > If the code said
>
> >          accept Original_Call do
> >             Put_Line ("Original Call...");
> >             delay 3.0;
> >             Put_Line ("Original Call Done");
> >          end Original_Call;
>
> > ie without the requeue, the timed entry call would not be cancelled,
> > because we're abort-deferred.
>
> You are confusing things here, this has nothing to do with
> abort-deferral. Abort-deferral means that the called task cannot be
> aborted while engaged in a rendezvous. Timed entry call is about the
> caller being removed from the queue if the delay expires before the
> rendezvous has started.
>
> > So a caller design which relies on the
> > callee having/not having a requeue is broken.
>
> No. A timed entry call means: "if the server does not arrive within XX
> seconds, I give up". Requeue (with abort) allows the server to get the
> request, inspect the parameters, and decide that this request cannot be
> serviced now and put it back on wait. This is transparent as seen from
> the client's side.

OK, I was confused about abort-deferral. Not the first time. But if it
is to be "transparent from the client's side", then it would be
reasonable for it to make no difference to the behaviour as seen by
the caller whether or not there is a requeue in the internals of the
callee. It's legitimate (well, no one has said it's wrong) for the
caller to be delayed well past its timed entry limit if there is a lot
of processing in the accept and no requeue.

What about
1 timed entry starts
2 original entry accepted
3 requeued
4 requeued entry started
5 timed entry expires but has no effect
6 requeued work continues for a significant period

From the outside, you would be hard put to distinguish this from the
case where 4 & 5 were reversed, I think.

Robert A Duff

unread,
Dec 30, 2008, 5:52:13 PM12/30/08
to
sjw <simon.j...@mac.com> writes:

> On Dec 30, 9:25 am, Jean-Pierre Rosen <ro...@adalog.fr> wrote:
>> sjw a écrit :
>> > The whole example is far from clear to me. If I were reviewing this
>> > design I would have great difficulty understanding what the designer
>> > was trying to do and whether the code achieved it.
>>
>> Sure, this is only an example to show an issue and demonstrate a problem.
>> In real life, you wouldn't have seconds, but micro-seconds. Would be
>> much more difficult to experiment with.
>
> My problem is rather more with the idea of doing work in the accept of
> size comparable with the caller's permissible waiting period *and
> then* requeueing.

I don't understand your "problem". The server doesn't know how long
clients might want to wait. In fact, the client might say "delay 0.0"
(or "else", which is equivalent).

The server needs to decide whether it's OK to have the whole operation
canceled in the middle -- i.e. decide whether the requeue should be with
or without "abort".

> OK, I was confused about abort-deferral. Not the first time. But if it
> is to be "transparent from the client's side", then it would be
> reasonable for it to make no difference to the behaviour as seen by
> the caller whether or not there is a requeue in the internals of the
> callee. It's legitimate (well, no one has said it's wrong) for the
> caller to be delayed well past its timed entry limit if there is a lot
> of processing in the accept and no requeue.

Yes, that can happen. So don't put too much code in the accept (or in
the requeued accept, if without "abort").

> What about
> 1 timed entry starts
> 2 original entry accepted
> 3 requeued
> 4 requeued entry started
> 5 timed entry expires but has no effect
> 6 requeued work continues for a significant period
>
> From the outside, you would be hard put to distinguish this from the
> case where 4 & 5 were reversed, I think.

Seems to me that's in the nature of timing. I mean, if you have
timeouts in your code, there's always a potential for race conditions --
no matter what you are timing out, the timeout could occur just a little
bit earlier or a little bit later. And it's impossible for the language
definition to define precisely how long things take.

- Bob

Jean-Pierre Rosen

unread,
Dec 31, 2008, 4:21:45 AM12/31/08
to
sjw a écrit :

> OK, I was confused about abort-deferral. Not the first time. But if it
> is to be "transparent from the client's side", then it would be
> reasonable for it to make no difference to the behaviour as seen by
> the caller whether or not there is a requeue in the internals of the
> callee. It's legitimate (well, no one has said it's wrong) for the
> caller to be delayed well past its timed entry limit if there is a lot
> of processing in the accept and no requeue.
>
> What about
> 1 timed entry starts
> 2 original entry accepted
> 3 requeued
> 4 requeued entry started
> 5 timed entry expires but has no effect
> 6 requeued work continues for a significant period
>
> From the outside, you would be hard put to distinguish this from the
> case where 4 & 5 were reversed, I think.

The basic idea is that, for a requeue-with-abort, the first accept is
deemed not to have happened at all. It really means: "oops, I took you
by mistake, forget it". So all time-out continue to run, as though
nothing had happened.

And yes, from an implementation point of view, it means that you are not
allowed to cancel any timer at the time a rendezvous is accepted, you
have to keep it running until the (good) termination of the rendezvous,
just in case.

christo...@eurocopter.com

unread,
Dec 31, 2008, 10:39:51 AM12/31/08
to
So if I understand correctly, the behaviour can be summarized as
follows in times Ti:

T0: timed entry call E1 with delay Delta
T1: rendezvous E1 starts
T2=T0+Delta: delay expires, but since E1 is executing, rendezvous
continues
T3: requeued with abort to E2 and rendezvous E1 ends

We now have to distinguish two cases:

1. E2 is ready for rendezvous:

T3 cont'ed: Entry call E2 is not queued, instead rendezvous E2 starts
immediately, so entry call of T0 is not cancelled.
From the client's point of view, the entry call terminates
normally (because the client is unaware of the requeue).

2. E2 is not ready for rendezvous:

T3 cont'ed: Entry call E2 is queued. Since the delay has already
expired at T2, the entry call E2 is immediately cancelled,
i.e. taken out of the queue again.
This is the cancellation of the timed entry call at T0
because the call did not start before the expiration
(in fact, it's the call of E2 that did not start before
T2, but the client is unaware of the requeue).

Is this correct?

Jean-Pierre Rosen

unread,
Dec 31, 2008, 11:14:59 AM12/31/08
to
christo...@eurocopter.com a écrit :
Yes, this is a nice summary

ishi...@arielworks.com

unread,
Jan 3, 2009, 11:49:18 AM1/3/09
to
Timed entry call (select else/ select or delay) for protected objects
seems broken too...

with Ada.Text_IO; use Ada.Text_IO;
procedure Timed_Protected is
protected P is
entry E1;
entry E2;
end P;

protected body P is
entry E1 when True is
begin
Put_Line ("E1");
delay 5.0; -- takes over Parent's delay
Put_Line ("E1 done");
end E1;

entry E2 when True is
begin
Put_Line ("E2");
end E2;
end P;

task T1 is
end T1;

task body T1 is
begin
P.E1;
end T1;

begin
delay 0.2; -- to ensure start after T1
Put_Line ("Parent selecting");
select
P.E2;
else
Put_Line ("Parent canceling");
end select;
end Timed_Protected;

This code outputs;
E1
Parent selecting
E1 done
E2

I think the code should output:
E1
Parent selecting
Parent canceling
E1 done

The both problems might have the same cause.

Jeffrey R. Carter

unread,
Jan 3, 2009, 4:09:20 PM1/3/09
to
ishi...@arielworks.com wrote:
>
> protected body P is
> entry E1 when True is
> begin
> Put_Line ("E1");
> delay 5.0; -- takes over Parent's delay
> Put_Line ("E1 done");
> end E1;
>
> entry E2 when True is
> begin
> Put_Line ("E2");
> end E2;
> end P;

Delay statements and calls to Ada.Text_IO.Put_Line are potentially blocking
operations. It is an error to call potentially blocking operations from a
protected action. See ARM 9.5.1.

--
Jeff Carter
"Pray that there's intelligent life somewhere up in
space, 'cause there's bugger all down here on earth."
Monty Python's Meaning of Life
61

christo...@eurocopter.com

unread,
Jan 4, 2009, 10:40:30 AM1/4/09
to
On 3 Jan., 22:09, "Jeffrey R. Carter" <spam.jrcarter....@spam.acm.org>
wrote:

> It is an error to call potentially blocking operations from a
> protected action. See ARM 9.5.1.

That's correct, but you'll get the same (wrong) result when you store
output in an intermediate container and output everything at the end.

Jeffrey R. Carter

unread,
Jan 4, 2009, 12:09:34 PM1/4/09
to
christo...@eurocopter.com wrote:
>
> That's correct, but you'll get the same (wrong) result when you store
> output in an intermediate container and output everything at the end.

If it retains the delay in the protected action, the program is still in error.
The output cannot be "wrong", since the language does not define the semantics
of the error.

The point I was making is that the language does not and need not define what
happens in such a case, unlike for a task, where the delay is perfectly legal.

--
Jeff Carter
"Hello! Smelly English K...niggets."
Monty Python & the Holy Grail
08

ishi...@arielworks.com

unread,
Jan 4, 2009, 1:03:44 PM1/4/09
to
On Jan 4, 6:09 am, "Jeffrey R. Carter"

<spam.jrcarter....@spam.acm.org> wrote:
> Delay statements and calls to Ada.Text_IO.Put_Line are potentially blocking
> operations. It is an error to call potentially blocking operations from a
> protected action. See ARM 9.5.1.
I'm sorry, I overlooked it.

How about this one;

with Ada.Text_IO; use Ada.Text_IO;
procedure Timed_Protected is

Z : boolean := True;

protected P is
entry E1;
entry E2;
end P;

protected body P is


entry E1 when True is
begin

-- this entry takes 5 seconds
while Z loop -- Z will be changed in 5 sec. by T2
null;
end loop;
end E1;

entry E2 when True is
begin

null;
end E2;
end P;

task T1 is
end T1;

task body T1 is


begin
Put_Line ("E1");

P.E1;
end T1;


task T2 is
end T2;

task body T2 is
begin
-- to complete E1, change the flag
delay 5.0;
Z := False;


Put_Line ("E1 done");

end T2;

begin
delay 0.2; -- to ensure start after T1
Put_Line ("Parent selecting");
select
P.E2;

Put_Line ("E2");
else
Put_Line ("Parent cancelling");
end select;
end Timed_Protected;


I got the same result from this code.

It seems that the cancellation of entry calls occurs only at the end/
start of protected actions.
If the onging protected action takes long time, timed calls wait for
the end of the onging protected action ignoring its expiration time.

Dmitry A. Kazakov

unread,
Jan 4, 2009, 1:56:36 PM1/4/09
to
On Sun, 4 Jan 2009 10:03:44 -0800 (PST), ishi...@arielworks.com wrote:

> On Jan 4, 6:09 am, "Jeffrey R. Carter"
> <spam.jrcarter....@spam.acm.org> wrote:

>> Delay statements and calls to Ada.Text_IO.Put_Line are potentially blocking
>> operations. It is an error to call potentially blocking operations from a
>> protected action. See ARM 9.5.1.

> I'm sorry, I overlooked it.

No, it is perfectly legal to use Put_Line in protected actions *with* GNAT.

GNAT implementation explicitly permits this use. So Jeffrey's comment was
irrelevant here.

You should report this bug to AdaCore.

> How about this one;
>
> with Ada.Text_IO; use Ada.Text_IO;
> procedure Timed_Protected is
> Z : boolean := True;

Pedantically, you should have added pragma Atomic (Z), because you access
it from different tasks. And probably Volatile (Z) to prevent loop
optimization.

But much simpler would be a primitive busy waiting:

entry E1 when True is

T : Time := Clock + 5.0;
begin
for I in Unsigned_64'Range loop
exit when Clock >= T;
end loop;
end E1;

belteshazzar

unread,
Jan 4, 2009, 6:17:39 PM1/4/09
to
On Jan 4, 1:56 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:

it was my understanding that pragma atomic does nothing for tasks, it
just tells the compiler to ensure that the architecture implements
atomic memory access.

also if pragma atomic is used this implies volatile.

Jeffrey R. Carter

unread,
Jan 4, 2009, 9:48:05 PM1/4/09
to
Dmitry A. Kazakov wrote:
>
> No, it is perfectly legal to use Put_Line in protected actions *with* GNAT.
>
> GNAT implementation explicitly permits this use. So Jeffrey's comment was
> irrelevant here.

No, the use of Ada.Text_IO.Put_Line in a protected action is a bounded error
with *all* compilers, GNAT included. A bounded error is an error, therefore this
use is an error for all compilers.

What happens in the case of a bounded error is compiler dependent. GNAT 6.1.1
gives me a warning if I use a delay statement in a protected operation, but
otherwise allows it. Another compiler might choose to raise Program_Error at run
time.

> Pedantically, you should have added pragma Atomic (Z), because you access
> it from different tasks. And probably Volatile (Z) to prevent loop
> optimization.

It's more than pedantic if you have multiple processors.

> for I in Unsigned_64'Range loop
> exit when Clock >= T;
> end loop;

loop
exit when ...
end loop;

Dmitry A. Kazakov

unread,
Jan 5, 2009, 4:12:26 AM1/5/09
to
On Sun, 4 Jan 2009 15:17:39 -0800 (PST), belteshazzar wrote:

> it was my understanding that pragma atomic does nothing for tasks, it
> just tells the compiler to ensure that the architecture implements
> atomic memory access.

and ensures that reads and updates are sequential, which in turn allows
sharing an atomic object by concurrent tasks, provided they only do
assigning and reading values. Without the pragma concurrent reading and
updating is erroneous RM 9.10 (11). [Granted, it is difficult to imagine a
situation when things might go wrong with a Boolean object.]

> also if pragma atomic is used this implies volatile.

Right, I was unsure about it, because logically one could have atomic
non-volatile, but I was too lazy to check.

Dmitry A. Kazakov

unread,
Jan 5, 2009, 4:30:27 AM1/5/09
to
On Mon, 05 Jan 2009 02:48:05 GMT, Jeffrey R. Carter wrote:

> Dmitry A. Kazakov wrote:
>>
>> No, it is perfectly legal to use Put_Line in protected actions *with* GNAT.
>>
>> GNAT implementation explicitly permits this use. So Jeffrey's comment was
>> irrelevant here.
>
> No, the use of Ada.Text_IO.Put_Line in a protected action is a bounded error
> with *all* compilers, GNAT included. A bounded error is an error, therefore this
> use is an error for all compilers.

There is an easy way out. They could say that GNAT's Ada.Text_IO.Put_Line
is not potentially blocking.

> What happens in the case of a bounded error is compiler dependent. GNAT 6.1.1
> gives me a warning if I use a delay statement in a protected operation, but
> otherwise allows it. Another compiler might choose to raise Program_Error at run
> time.

Here they are in violation. Since they detected a delay statement within a
protected action, they should have raised Protected_Error.

Jean-Pierre Rosen

unread,
Jan 5, 2009, 5:17:04 AM1/5/09
to
ishi...@arielworks.com a écrit :

> On Jan 4, 6:09 am, "Jeffrey R. Carter"
> <spam.jrcarter....@spam.acm.org> wrote:
>> Delay statements and calls to Ada.Text_IO.Put_Line are potentially blocking
>> operations. It is an error to call potentially blocking operations from a
>> protected action. See ARM 9.5.1.
> I'm sorry, I overlooked it.
>
> How about this one;
>
[...]
Be careful: you didn't specify any priority. Therefore, your busy loop
may prevent the main program from advancing to the select statement.

It would be interesting to retry this with a high priority for the main
program, and lower priority for other tasks.

Jeffrey R. Carter

unread,
Jan 5, 2009, 3:28:14 PM1/5/09
to
Dmitry A. Kazakov wrote:
>
> There is an easy way out. They could say that GNAT's Ada.Text_IO.Put_Line
> is not potentially blocking.

No, the ARM specifies that all the operations of the language-defined I/O pkgs
are potentially blocking.

> Here they are in violation. Since they detected a delay statement within a
> protected action, they should have raised Protected_Error.

I think the ARM gives the implementation the right to decide whether or not to
raise an exception if it detects the problem.

--
Jeff Carter
"It's all right, Taggart. Just a man and a horse being hung out there."
Blazing Saddles
34

Dmitry A. Kazakov

unread,
Jan 5, 2009, 3:47:37 PM1/5/09
to
On Mon, 05 Jan 2009 20:28:14 GMT, Jeffrey R. Carter wrote:

> Dmitry A. Kazakov wrote:
>>
>> There is an easy way out. They could say that GNAT's Ada.Text_IO.Put_Line
>> is not potentially blocking.
>
> No, the ARM specifies that all the operations of the language-defined I/O pkgs
> are potentially blocking.

RM only says "In particular, the subprograms of the language-defined
input-output packages that manipulate files (implicitly or explicitly) are
potentially blocking." 9.5.1 (18). If Put_Line does not manipulate files,
then it is OK. "file" is not defined anyway.

>> Here they are in violation. Since they detected a delay statement within a
>> protected action, they should have raised Protected_Error.
>
> I think the ARM gives the implementation the right to decide whether or not to
> raise an exception if it detects the problem.

But RM 9.5.1 (17) is specific "If the bounded error is detected,
Program_Error is raised." No permission given to ignore it.

Jeffrey R. Carter

unread,
Jan 5, 2009, 9:35:30 PM1/5/09
to
Dmitry A. Kazakov wrote:
>
> RM only says "In particular, the subprograms of the language-defined
> input-output packages that manipulate files (implicitly or explicitly) are
> potentially blocking." 9.5.1 (18). If Put_Line does not manipulate files,
> then it is OK. "file" is not defined anyway.

That Put_Line manipulates files is explicitly stated in the ARM.

> But RM 9.5.1 (17) is specific "If the bounded error is detected,
> Program_Error is raised." No permission given to ignore it.

Looks as if you're right about this one. But I prefer the way GNAT handles it;
the restrictions on protected actions are unnecessary.

0 new messages