comp.lang.ada
 help / color / mirror / Atom feed
* Elegant 'abort' of sleeping task
@ 2003-03-31 13:18 Simon Apperley
  2003-03-31 13:28 ` Lutz Donnerhacke
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Simon Apperley @ 2003-03-31 13:18 UTC (permalink / raw)


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



^ 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
                   ` (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 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 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
                   ` (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

end of thread, other threads:[~2003-04-01 16:26 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox