* Re: Elegant 'abort' of sleeping task
2003-03-31 13:18 Elegant 'abort' of sleeping task Simon Apperley
@ 2003-03-31 13:28 ` Lutz Donnerhacke
2003-03-31 16:04 ` Jeffrey Carter
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Lutz Donnerhacke @ 2003-03-31 13:28 UTC (permalink / raw)
* Simon Apperley wrote:
> I'm looking at the design of a piece of server code which has to handle
> calls that also pass a timeout value. The target system is aerospace
> related, and dynamically creating tasks 'on the fly' just is not an
> option.
If you are not hiding your own process details, you can pass the timeout
value down to the lowest level routines. Running on an approbriate OS, all
syscalls has a separate timeout parameter, therefore you are done. ;-)
> I want to be able to set up a single task to handle the timeout from
> the head of a delta-queue of timeouts, but have found a problem. If I
> have the timeout implemented as a task stuck in a 'delay' call, and a
> more immediate timeout comes in, I want to wake up the sleeping task,
> re-calculate the delta-queue and then sleep on the new, shorter,
> delay. So far the only way I can see to do this is to use abort, and
> set up the task again, which seems a bit of a brute force approach.
Do not use delay: Use the syscall to sleep a specified amount of time or
shorter if an event occur. In Posix enviroments, select(2) or poll(2) comes
to mind. But if possible look for calls like epoll(2).
> Has anyone got any suggestions on how I can interrupt the sleep call,
> without using a polling approach that would just consume CPU time at
> the expense of the other code in the system.
Depends on your system. Most can do this easily.
> I could use direct calls to the underlying RTOS, but I'd rather keep
> as much as possible within the Ada language.
Write a wrapper "procedure Pause_Max (seconds : in out Delay_T; events : out
Event_Array; last_event : out Natural)" or "function Pause_Max (seconds :
Delay_T) return event_or_remaining_time_t".
> I did wonder about delay until TIME, and having another task change
> TIME, but that seems rather un-safe to me as it starts making
> assumptions about the underlying run-time implementation.
This will not work, because it will not stop in the case of events.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Elegant 'abort' of sleeping task
2003-03-31 13:18 Elegant 'abort' of sleeping task Simon Apperley
2003-03-31 13:28 ` Lutz Donnerhacke
@ 2003-03-31 16:04 ` Jeffrey Carter
2003-04-01 12:02 ` Dmitry A. Kazakov
2003-03-31 16:49 ` David C. Hoos
` (2 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Jeffrey Carter @ 2003-03-31 16:04 UTC (permalink / raw)
Simon Apperley wrote:
>
> I'm looking at the design of a piece of server code which has to
> handle calls that also pass a timeout value. The target system is
> aerospace related, and dynamically creating tasks 'on the fly' just is
> not an option.
>
> I want to be able to set up a single task to handle the timeout from
> the head of a delta-queue of timeouts, but have found a problem. If I
> have the timeout implemented as a task stuck in a 'delay' call, and a
> more immediate timeout comes in, I want to wake up the sleeping task,
> re-calculate the delta-queue and then sleep on the new, shorter,
> delay. So far the only way I can see to do this is to use abort, and
> set up the task again, which seems a bit of a brute force approach.
It sounds as if you have something like
loop
-- get Timeout
delay Timeout;
-- post timeout processing
end loop;
in which case you might be able to do something like
loop
-- get Timeout
loop
select
accept New_Timeout;
-- get Timeout;
or
delay Timeout;
-- post timeout processing
exit;
end select;
end loop;
end loop;
This is a timed select, but it has the effect you desire. If nothing
happens during the timeout period, the task is delayed for that period
and then the post timeout processing occurs. Then it gets the next
timeout period. If a new timeout value becomes available during a
timeout, you make a call to the New_Timeout entry, which immediately
results in the task obtaining a new value for Timeout and performing the
timed select again with the new value.
> I did wonder about delay until TIME, and having another task change
> TIME, but that seems rather un-safe to me as it starts making
> assumptions about the underlying run-time implementation.
All delays are defined in terms of delay until, so you can certainly use
delay until in this construct, and it will probably simplify your code.
But what you're talking about won't work. Changing the variable after
the delay until has commenced doesn't change the time at which the delay
expires. One way to consider this is that the delay until effectively
stores the current value of your variable. Ada does a lot of this kind
of thing; for example
I : Integer := 5;
begin
for J in 1 .. I loop
I := 10;
loops 5 times. Changing the value of I doesn't change the loop bounds.
--
Jeff Carter
"If a sperm is wasted, God gets quite irate."
Monty Python's the Meaning of Life
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Elegant 'abort' of sleeping task
2003-03-31 16:04 ` Jeffrey Carter
@ 2003-04-01 12:02 ` Dmitry A. Kazakov
0 siblings, 0 replies; 7+ messages in thread
From: Dmitry A. Kazakov @ 2003-04-01 12:02 UTC (permalink / raw)
On Mon, 31 Mar 2003 16:04:58 GMT, Jeffrey Carter <spam@spam.com>
wrote:
>Simon Apperley wrote:
>>
>> I'm looking at the design of a piece of server code which has to
>> handle calls that also pass a timeout value. The target system is
>> aerospace related, and dynamically creating tasks 'on the fly' just is
>> not an option.
>>
>> I want to be able to set up a single task to handle the timeout from
>> the head of a delta-queue of timeouts, but have found a problem. If I
>> have the timeout implemented as a task stuck in a 'delay' call, and a
>> more immediate timeout comes in, I want to wake up the sleeping task,
>> re-calculate the delta-queue and then sleep on the new, shorter,
>> delay. So far the only way I can see to do this is to use abort, and
>> set up the task again, which seems a bit of a brute force approach.
>
>It sounds as if you have something like
>
>loop
> -- get Timeout
> delay Timeout;
> -- post timeout processing
>end loop;
>
>in which case you might be able to do something like
>
>loop
> -- get Timeout
> loop
> select
> accept New_Timeout;
> -- get Timeout;
> or
> delay Timeout;
> -- post timeout processing
>
> exit;
> end select;
> end loop;
>end loop;
Just to complete the image ...
Ada 95 offers also protected objects which entries can be used for
timed entry calls. So a solution based on a protected object might
look like:
protected type Timeout_Queue is
entry Get (Timeout : out Duration);
-- blocks until a new timeout comes
procedure Put (Timeout : Duration);
-- puts a new timeout
...
end Timeout_Queue;
...
Queue : Timeout_Queue;
...
loop
select
Queue.Get (Shorter_Timeout);
-- get new timeout;
or
delay Last_Timeout;
-- post timeout processing
end select;
end loop;
BTW. For any of two alternatives, it might be better to use a schedule
time (Time) instead of a timeout (Duration). If you do so, replace
delay with delay until.
---
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Elegant 'abort' of sleeping task
2003-03-31 13:18 Elegant 'abort' of sleeping task Simon Apperley
2003-03-31 13:28 ` Lutz Donnerhacke
2003-03-31 16:04 ` Jeffrey Carter
@ 2003-03-31 16:49 ` David C. Hoos
2003-04-01 16:20 ` David C. Hoos
2003-04-01 16:26 ` Nick Roberts
4 siblings, 0 replies; 7+ messages in thread
From: David C. Hoos @ 2003-03-31 16:49 UTC (permalink / raw)
To: comp.lang.ada mail to news gateway; +Cc: simon_the_softy
Hi Simon,
I have a timer package that does what I believe you need. I do not have
access to the code at the moment, but later today I will get it and post it
on my FTP site.
Basically, the package declares an abstract data type Timers, which
has a discriminant specifying the maximum number of timeouts that
can be simultaneously managed.
Operations are provided for Setting and Clearing of timers.
Individual timer members of the object are identified by a positive
integer.
Timers can be set either at a specified time from now, or at an absolute
time.
Internally, the object contains a time-ordered array of the timers' absolute
expiration time, an access to the callback procedure to be called on
expiration, and identification.
When one of the operations is called, a persistent signal is sent to
the timer manager task which aborts the current delay until statement,
using asynchronous transfer of control.
This the permits the ordered array to be manipulated as required by the
operation, followed by the initiation of a new delay until statement for
the earliest expiration time in the list.
Upon timer expiration, a "callback" procedure is called, with the timer
identification as its only parameter.
The Set operation has a parameter to specify which Action procedure
(callback) is to be called upon timer expiration.
This package has been in heavy use since 1997.
----- Original Message -----
From: "Simon Apperley" <simon_the_softy@hotmail.com>
Newsgroups: comp.lang.ada
To: <comp.lang.ada@ada.eu.org>
Sent: Monday, March 31, 2003 7:18 AM
Subject: Elegant 'abort' of sleeping task
> Hi,
>
> I'm looking at the design of a piece of server code which has to
> handle calls that also pass a timeout value. The target system is
> aerospace related, and dynamically creating tasks 'on the fly' just is
> not an option.
>
> I want to be able to set up a single task to handle the timeout from
> the head of a delta-queue of timeouts, but have found a problem. If I
> have the timeout implemented as a task stuck in a 'delay' call, and a
> more immediate timeout comes in, I want to wake up the sleeping task,
> re-calculate the delta-queue and then sleep on the new, shorter,
> delay. So far the only way I can see to do this is to use abort, and
> set up the task again, which seems a bit of a brute force approach.
>
> Has anyone got any suggestions on how I can interrupt the sleep call,
> without using a polling approach that would just consume CPU time at
> the expense of the other code in the system.
>
> I could use direct calls to the underlying RTOS, but I'd rather keep
> as much as possible within the Ada language.
>
> I did wonder about delay until TIME, and having another task change
> TIME, but that seems rather un-safe to me as it starts making
> assumptions about the underlying run-time implementation.
>
> Thanks
>
> Simon Apperley @ General Dynamics UK COM
> _______________________________________________
> comp.lang.ada mailing list
> comp.lang.ada@ada.eu.org
> http://ada.eu.org/mailman/listinfo/comp.lang.ada
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Elegant 'abort' of sleeping task
2003-03-31 13:18 Elegant 'abort' of sleeping task Simon Apperley
` (2 preceding siblings ...)
2003-03-31 16:49 ` David C. Hoos
@ 2003-04-01 16:20 ` David C. Hoos
2003-04-01 16:26 ` Nick Roberts
4 siblings, 0 replies; 7+ messages in thread
From: David C. Hoos @ 2003-04-01 16:20 UTC (permalink / raw)
To: comp.lang.ada mail to news gateway; +Cc: simon_the_softy
The timer package of which I wrote yesterday is now at
ftp.ada95.com/pub/timers.tgz or
ftp.ada95.com/pub/timers.tar.gz
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Elegant 'abort' of sleeping task
2003-03-31 13:18 Elegant 'abort' of sleeping task Simon Apperley
` (3 preceding siblings ...)
2003-04-01 16:20 ` David C. Hoos
@ 2003-04-01 16:26 ` Nick Roberts
4 siblings, 0 replies; 7+ messages in thread
From: Nick Roberts @ 2003-04-01 16:26 UTC (permalink / raw)
On 31 Mar 2003 05:18:20 -0800, Simon Apperley <simon_the_softy@hotmail.com>
wrote:
> I'm looking at the design of a piece of server code which has to
> handle calls that also pass a timeout value. The target system is
> aerospace related, and dynamically creating tasks 'on the fly' just is
> not an option.
>
> I want to be able to set up a single task to handle the timeout from
> the head of a delta-queue of timeouts, but have found a problem. If I
> have the timeout implemented as a task stuck in a 'delay' call, and a
> more immediate timeout comes in, I want to wake up the sleeping task,
> re-calculate the delta-queue and then sleep on the new, shorter,
> delay. So far the only way I can see to do this is to use abort, and
> set up the task again, which seems a bit of a brute force approach.
>
> Has anyone got any suggestions on how I can interrupt the sleep call,
> without using a polling approach that would just consume CPU time at
> the expense of the other code in the system.
>
> I could use direct calls to the underlying RTOS, but I'd rather keep
> as much as possible within the Ada language.
>
> I did wonder about delay until TIME, and having another task change
> TIME, but that seems rather un-safe to me as it starts making
> assumptions about the underlying run-time implementation.
Changing the parameter of a delay statement won't work! (Unless the Ada
implementation is faulty.)
I don't think there is likely to be any way of avoiding some sort of a
polling solution to your problem, in practice, since presumably you want
the server task to know if a timeout has occured, and the server must, in
general, be doing 'something else' at most stages of its execution.
The best solution I can think of is one where the server is given the
opportunity to check for timeout expiry at convenient stages during its
execution.
In essence, we have the familiar 'service loop', with a delay statement
whose tail performs the timeout checks and then does some work. Provided
the delay is reasonably small, and each quantum of work is also reasonably
quick, we get roughly the necessary behaviour. (Maybe too rough for some
hard RT situations.) The server ticks round and round the loop, doing a bit
of work and a bit of administration each iteration.
My suggested (bare bones) solution makes use of a bank of buffers to
contain the results of the server's work. These are protected objects, so
each client can wait on a separate buffer, against its own timeout period,
to get the result of its enquiry. The server task puts the results of its
work into the buffers; it checks to see whether a client has given up and
gone home, in which it (sensibly!) stops doing any more work for the client
(but is able to continue undisturbed doing work for other clients).
Depending on the exact details of your application, your mileage may
significantly vary.
===
package Sampling_Management is
type Sampling_Bottle is ...;
subtype Sampling_Timeout is Duration range 0.0 .. 60.0;
Sampling_Failure: exception; -- raised upon timeout expiry
procedure Obtain_Sample (Bottle: in out Sampling_Bottle;
Timeout: in Sampling_Timeout);
...
end;
...
package body Sampling_Management is
Server_Heartbeat: constant Duration := 0.001; -- one millisecond
type Buffer_Status is (Available, Pending_Sampling,
Awaiting_Checkout);
protected type Bottle_Buffer is
entry Check_Out (Bottle: out Sampling_Bottle);
procedure Cancel;
private
Result: Sampling_Bottle;
Status: Buffer_Status := Available;
end;
type Buffer_Number is range 1..100;
Buffers: array (Buffer_Number) of Bottle_Buffer;
...
task Sampler_Task is
entry Initiate_Sampling (Buffer: out Buffer_Number);
...
end;
...
procedure Obtain_Sample (Bottle: out Sampling_Bottle;
Timeout: in Sampling_Timeout) is
B: Buffer_Number;
begin
Sampler_Task.Initiate_Sampling(B);
select
Buffers(B).Check_Out(Bottle);
or
delay Timeout;
Buffers(B).Cancel;
raise Sampling_Failure;
end select;
end Obtain_Sample;
...
protected body Bottle_Buffer is
entry Check_Out (Bottle: out Sampling_Bottle)
when Status /= Pending_Sampling is
begin
if Status /= Awaiting_Checkout then raise ...; end if;
Bottle := Result;
Status := Available;
end;
procedure Cancel is
begin
if Status = Available then raise ...; end if;
Status := Available;
end;
end;
...
task body Sampler_Task is
...
begin
loop
select
accept Initiate_Sampling (Buffer: out Buffer_Number) do
... -- allocate new buffer and assign number to Buffer
end;
or
...
or
delay Server_Heartbeat;
-- Test for buffers whose client has timed out
-- (its status will have changed to Available),
-- and stop doing work for them:
... -- Do one quantum of work (unless there are no
-- clients currently requiring work):
...
end select;
end loop;
...
end Sampler_Task;
...
end Sampling_Management;
===
Not elegant, of course, but practicable. I think there are quite a lot of
design issues tangled up in this problem, some of them rather complex.
--
Nick Roberts
^ permalink raw reply [flat|nested] 7+ messages in thread