From: Nick Roberts <nickroberts@blueyonder.co.uk>
Subject: Re: Elegant 'abort' of sleeping task
Date: Tue, 01 Apr 2003 17:26:46 +0100
Date: 2003-04-01T17:26:46+01:00 [thread overview]
Message-ID: <oprmylawncbqmqul@news.cis.dfn.de> (raw)
In-Reply-To: <310b040f.0303310518.76dc9bf7@posting.google.com>
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
prev parent reply other threads:[~2003-04-01 16:26 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
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
2003-04-01 16:20 ` David C. Hoos
2003-04-01 16:26 ` Nick Roberts [this message]
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox