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;
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
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;
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;
On Sun, 28 Dec 2008 07:53:44 -0800 (PST), christoph.gr...@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.
>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.)
"Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> writes:
> On Sun, 28 Dec 2008 07:53:44 -0800 (PST), christoph.gr...@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.
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.
> 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").
> 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").
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).
> On Sun, 28 Dec 2008 07:53:44 -0800 (PST), christoph.gr...@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
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)?
> 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.
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?
> 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.
-- --------------------------------------------------------- J-P. Rosen (ro...@adalog.fr) Visit Adalog's web site at http://www.adalog.fr
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.).
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.
> 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. -- --------------------------------------------------------- J-P. Rosen (ro...@adalog.fr) Visit Adalog's web site at http://www.adalog.fr
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.
> > 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.
sjw <simon.j.wri...@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.
> 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. -- --------------------------------------------------------- J-P. Rosen (ro...@adalog.fr) Visit Adalog's web site at http://www.adalog.fr
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).
> 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?
Yes, this is a nice summary
-- --------------------------------------------------------- J-P. Rosen (ro...@adalog.fr) Visit Adalog's web site at http://www.adalog.fr