comp.lang.ada
 help / color / mirror / Atom feed
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



      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