comp.lang.ada
 help / color / mirror / Atom feed
* Abortable Timed Action
@ 2015-12-31  4:15 T.G.
  2015-12-31  6:40 ` Anh Vo
  0 siblings, 1 reply; 11+ messages in thread
From: T.G. @ 2015-12-31  4:15 UTC (permalink / raw)


Hello everyone. Is there a better way to implement a timed action?
I've tried the following :

   task type Timed_Action_Task is
     entry Exec_After (T : Duration);
     entry Cancel;
     entry Finish;
   end Timed_Action_Task;

   task body Timed_Action_Task is
      Timeout : Duration;
   begin
      loop
         select
            accept Exec_After (T : Duration) do
               Timeout := T;
            end Exec_After;
            select
               accept Cancel;
            or
               delay Timeout;
               Do_Something;
            end select;
         or
            accept Finish;
            exit;
         end select;
      end loop;
   end Timed_Action_Task;

and then later something like

  Timed_Action.Exec_After (5.0);
  ...
  if Something_Changed then
     Timed_Action.Cancel;
  end if;

it works, I've also tried,

   task body Timed_Action_Task is
      Timeout  : Duration := Duration'Last;
   begin
      loop
         select
            accept Exec_After (T : Duration) do
               Timeout := T;
            end Exec_After;
         or
            accept Cancel;
            Timeout := Duration'Last;
         or
            accept Finish;
            exit;
         or
            delay Timeout;
            Do_Something;
            Timeout := Duration'Last;
         end select;
      end loop;
   end Timed_Action_Task;

any other options?

Regards,


--- news://freenews.netfront.net/ - complaints: news@netfront.net ---

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2015-12-31  4:15 Abortable Timed Action T.G.
@ 2015-12-31  6:40 ` Anh Vo
  2015-12-31  7:32   ` T.G.
  0 siblings, 1 reply; 11+ messages in thread
From: Anh Vo @ 2015-12-31  6:40 UTC (permalink / raw)


On Wednesday, December 30, 2015 at 8:15:15 PM UTC-8, T.G. wrote:
> Hello everyone. Is there a better way to implement a timed action?
> I've tried the following :
> 
>    task type Timed_Action_Task is
>      entry Exec_After (T : Duration);
>      entry Cancel;
>      entry Finish;
>    end Timed_Action_Task;
> 
>    task body Timed_Action_Task is
>       Timeout : Duration;
>    begin
>       loop
>          select
>             accept Exec_After (T : Duration) do
>                Timeout := T;
>             end Exec_After;
>             select
>                accept Cancel;
>             or
>                delay Timeout;
>                Do_Something;
>             end select;
>          or
>             accept Finish;
>             exit;
>          end select;
>       end loop;
>    end Timed_Action_Task;
> 
> and then later something like
> 
>   Timed_Action.Exec_After (5.0);
>   ...
>   if Something_Changed then
>      Timed_Action.Cancel;
>   end if;
> 
> it works, I've also tried,
> 
>    task body Timed_Action_Task is
>       Timeout  : Duration := Duration'Last;
>    begin
>       loop
>          select
>             accept Exec_After (T : Duration) do
>                Timeout := T;
>             end Exec_After;
>          or
>             accept Cancel;
>             Timeout := Duration'Last;
>          or
>             accept Finish;
>             exit;
>          or
>             delay Timeout;
>             Do_Something;
>             Timeout := Duration'Last;
>          end select;
>       end loop;
>    end Timed_Action_Task;
> 
> any other options?

Look like your code worked like a timer. Was it your intention to create a timer?

Anh Vo


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2015-12-31  6:40 ` Anh Vo
@ 2015-12-31  7:32   ` T.G.
  2015-12-31 16:21     ` Anh Vo
  0 siblings, 1 reply; 11+ messages in thread
From: T.G. @ 2015-12-31  7:32 UTC (permalink / raw)


On 2015-12-31, Anh Vo <anhvofrcaus@gmail.com> wrote:
> Look like your code worked like a timer. Was it your intention to
> create a timer?
>
> Anh Vo

More or less, Yes. The code I posted works as expected but I'm
wondering if there is a better/simpler way to do it.

--- news://freenews.netfront.net/ - complaints: news@netfront.net ---

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2015-12-31  7:32   ` T.G.
@ 2015-12-31 16:21     ` Anh Vo
  2015-12-31 18:09       ` T.G.
  0 siblings, 1 reply; 11+ messages in thread
From: Anh Vo @ 2015-12-31 16:21 UTC (permalink / raw)


On Wednesday, December 30, 2015 at 11:32:02 PM UTC-8, T.G. wrote:
> On 2015-12-31, Anh Vo <anhvofrcaus@gmail.com> wrote:
> > Look like your code worked like a timer. Was it your intention to
> > create a timer?
 
> More or less, Yes. The code I posted works as expected but I'm
> wondering if there is a better/simpler way to do it.

I think accuracy more important than better/simpler way. There is a drifting issue in your codes in term of time since delay statement is involved. In addition, timer has no problem of this kind. If you decide to use timer, take look at http://www.adacore.com/adaanswers/gems/ada-gem-15/. Let me know if have any question.

Anh Vo


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2015-12-31 16:21     ` Anh Vo
@ 2015-12-31 18:09       ` T.G.
  2016-01-06 21:14         ` Anh Vo
  0 siblings, 1 reply; 11+ messages in thread
From: T.G. @ 2015-12-31 18:09 UTC (permalink / raw)


On 2015-12-31, Anh Vo <anhvofrcaus@gmail.com> wrote:
> I think accuracy more important than better/simpler way. There is a
> drifting issue in your codes in term of time since delay statement
> is involved. In addition, timer has no problem of this kind. If you
> decide to use timer, take look at
> http://www.adacore.com/adaanswers/gems/ada-gem-15/. Let me know if
> have any question.
>
> Anh Vo

Very nice example. I didn't know about Ada.Real_Time.Timing_Events,
I'll probably go with that. Thanks for the pointer.

--- news://freenews.netfront.net/ - complaints: news@netfront.net ---

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2015-12-31 18:09       ` T.G.
@ 2016-01-06 21:14         ` Anh Vo
  2016-01-08 20:24           ` T.G.
  0 siblings, 1 reply; 11+ messages in thread
From: Anh Vo @ 2016-01-06 21:14 UTC (permalink / raw)


On Thursday, December 31, 2015 at 10:09:30 AM UTC-8, T.G. wrote:
> On 2015-12-31, Anh Vo <anhvofrcaus@gmail.com> wrote:
> > I think accuracy more important than better/simpler way. There is a
> > drifting issue in your codes in term of time since delay statement
> > is involved. In addition, timer has no problem of this kind. If you
> > decide to use timer, take look at
> > http://www.adacore.com/adaanswers/gems/ada-gem-15/. Let me know if
> > have any question.
> >
> > Anh Vo
> 
> Very nice example. I didn't know about Ada.Real_Time.Timing_Events,
> I'll probably go with that. Thanks for the pointer.

After looking at your original post again, I believe your code should work after replacing delay statement by delay until statement. The delay until statement does not have time drifting issue. In addition, then Entry Finish can be replaced by the terminate alternative. The modified version is shown below.

   with Ada.Calendar; use type Ada.Calendar.Time;
   with Ada.Text_Io;  use Ada.Text_Io;
   -- ...

   -- ...
   task type Timed_Action_Task is 
     entry Exec_After (T : Duration); 
     entry Cancel; 
   end Timed_Action_Task; 

   task body Timed_Action_Task is 
      Timeout : Duration; 
   begin 
      loop 
         select 
            accept Exec_After (T : Duration) do 
               Timeout := T; 
            end Exec_After; 
            select 
               accept Cancel; 
            or 
               delay until (Ada.Calendar.Clock + Timeout); 
               Put_Line ("Do Something"); 
            end select; 
         or 
            terminate;
         end select; 
      end loop; 
   end Timed_Action_Task;

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2016-01-06 21:14         ` Anh Vo
@ 2016-01-08 20:24           ` T.G.
  2016-01-09  8:45             ` Simon Wright
  0 siblings, 1 reply; 11+ messages in thread
From: T.G. @ 2016-01-08 20:24 UTC (permalink / raw)


On 2016-01-06, Anh Vo <anhvofrcaus@gmail.com> wrote:
> After looking at your original post again, I believe your code
> should work after replacing delay statement by delay until
> statement. The delay until statement does not have time drifting
> issue. In addition, then Entry Finish can be replaced by the
> terminate alternative. The modified version is shown below.

>             select 
>                accept Cancel; 
>             or 
>                delay until (Ada.Calendar.Clock + Timeout); 
>                Put_Line ("Do Something"); 
>             end select; 

The reason why I had an explicit Finish instead of using terminate was
that I was thinking of creating the timer dynamically and then freeing
it with Ada.Unchecked_Deallocation. So I wanted to Finish the task
before freeing it. I'm not sure if calling Free on an access actually
terminates the task Normally.

delay until is an interesting idea. I'm assuming that time drift would
be an issue in periodic actions that repeat at a certain interval, but
in that case delay until could also have issues if it loses some
accuracy on each iteration. For example, modifying the code to run
periodically, with:

      Start_Time : Ada.Calendar.Time;
   begin
      ...
	    accept Exec_After (T : Duration) do
   	       Start_Time := Ada.Calendar.Clock;
	       Timeout    := T;
	    end Exec_After; 
	    Inner : loop
	       select 
		  accept Cancel; 
		  exit Inner;
	       or 
		  delay until (Ada.Calendar.Clock + Timeout); 
		  Put_Line (Duration'Image (Ada.Calendar.Clock - Start_Time));
	       end select; 
	    end loop Inner;

it will drift, however, we can still use delay until but with a
counter, for example:

   task body Timed_Action_Task is 
      Timeout        : Duration;
      Start_Time     : Ada.Calendar.Time;
      Counter        : Positive := 1;
   begin 
      loop 
	 select 
	    accept Exec_After (T : Duration) do 
	       Start_Time := Ada.Calendar.Clock;
	       Timeout    := T;
	       Counter    := 1;
	    end Exec_After; 
	    Inner : loop
	       select 
		  exit Inner;
		  accept Cancel; 
	       or 
		  delay until (Start_Time + (Timeout * Counter));
		  Counter := Counter + 1;
		  Put_Line (Duration'Image (Ada.Calendar.Clock - Start_Time));
	       end select; 
	    end loop Inner;
	 or 
	    terminate;
	 end select; 
      end loop; 
   end Timed_Action_Task;

This avoids the drift from the previous example.

In my actual code, I used Ada.Real_Time.Timing_Events. It ended up
being somewhat complicated, but only because I wanted to pass both
Action and User_Data to the Timer and make a more flexible/reusable
timer. Now after testing the code above, I think I want to recheck my
code for a possible time drift with periodic timed events.


--- news://freenews.netfront.net/ - complaints: news@netfront.net ---

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2016-01-08 20:24           ` T.G.
@ 2016-01-09  8:45             ` Simon Wright
  2016-01-09  9:10               ` Dmitry A. Kazakov
                                 ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Simon Wright @ 2016-01-09  8:45 UTC (permalink / raw)


"T.G." <anon@anon.org> writes:

> The reason why I had an explicit Finish instead of using terminate was
> that I was thinking of creating the timer dynamically and then freeing
> it with Ada.Unchecked_Deallocation. So I wanted to Finish the task
> before freeing it. I'm not sure if calling Free on an access actually
> terminates the task Normally.

GNAT certainly used to have issues in this area. Indeed, you could abort
the task and then deallocate it, and end with a memory leak (the task
control block wasn't actually freed); the cure was to wait until
'Terminated.

I believe that this is no longer a problem.

> delay until is an interesting idea. I'm assuming that time drift would
> be an issue in periodic actions that repeat at a certain interval, but
> in that case delay until could also have issues if it loses some
> accuracy on each iteration.

The common solution is something like

   with Ada.Calendar;
   with Ada.Text_IO; use Ada.Text_IO;
   procedure Delay_Until is
      task T is
         entry Exec_After (T : Duration);
      end T;
      task body T is
         Start : Ada.Calendar.Time;
         Next : Ada.Calendar.Time;
         Interval : Duration;
         use type Ada.Calendar.Time;
      begin
         accept Exec_After (T : Duration) do
            Start := Ada.Calendar.Clock;
            Next := Start + T;
            Interval := T;
         end Exec_After;
         loop
            delay until Next;
            Next := Next + Interval;  -- *not* Ada.Calendar.Clock + Interval
            Put_Line (Duration'Image (Ada.Calendar.Clock - Start));
         end loop;
      end T;
   begin
      T.Exec_After (0.5);
   end Delay_Until;

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2016-01-09  8:45             ` Simon Wright
@ 2016-01-09  9:10               ` Dmitry A. Kazakov
  2016-01-09 14:41               ` Bob Duff
  2016-01-09 15:59               ` T.G.
  2 siblings, 0 replies; 11+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-09  9:10 UTC (permalink / raw)


On 2016-01-09 09:45, Simon Wright wrote:
> "T.G." <anon@anon.org> writes:
>
>> delay until is an interesting idea. I'm assuming that time drift would
>> be an issue in periodic actions that repeat at a certain interval, but
>> in that case delay until could also have issues if it loses some
>> accuracy on each iteration.

Yes, if the period is not a multiple of the clock and system timer 
interrupt source. Though there is no way to eliminate this type of 
jitter, so why worry?

The resolution of Time (not accuracy) can be a problem, e.g. in the 
suggested schema below, which will accumulate error. The error can be 
compensated by computing Next as

    Count := Count + 1;
    Next :=
       Start + Duration (Long_Float (Count) * Long_Float (Interval));

Though, it is difficult to imagine a scenario where that would be really 
needed.

> The common solution is something like
>
>     with Ada.Calendar;
>     with Ada.Text_IO; use Ada.Text_IO;
>     procedure Delay_Until is
>        task T is
>           entry Exec_After (T : Duration);
>        end T;
>        task body T is
>           Start : Ada.Calendar.Time;
>           Next : Ada.Calendar.Time;
>           Interval : Duration;
>           use type Ada.Calendar.Time;
>        begin
>           accept Exec_After (T : Duration) do
>              Start := Ada.Calendar.Clock;
>              Next := Start + T;
>              Interval := T;
>           end Exec_After;
>           loop
>              delay until Next;
>              Next := Next + Interval;  -- *not* Ada.Calendar.Clock + Interval

An missing ticks detection and compensation:

    if Next < Clock then
       Next := Clock;
       Put_Line ("Ticks missed!");
    end if;


-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2016-01-09  8:45             ` Simon Wright
  2016-01-09  9:10               ` Dmitry A. Kazakov
@ 2016-01-09 14:41               ` Bob Duff
  2016-01-09 15:59               ` T.G.
  2 siblings, 0 replies; 11+ messages in thread
From: Bob Duff @ 2016-01-09 14:41 UTC (permalink / raw)


Simon Wright <simon@pushface.org> writes:

> GNAT certainly used to have issues in this area. Indeed, you could abort
> the task and then deallocate it, and end with a memory leak (the task
> control block wasn't actually freed); the cure was to wait until
> 'Terminated.
>
> I believe that this is no longer a problem.

When you call Unchecked_Deallocation on an object containing tasks that
are not yet terminated, they keep running.  GNAT used to have a memory
leak as you say.  I think it was fixed perhaps 2 years ago.  So with
the latest GNAT, when you call Unchecked_Deallocation, the memory is
freed for tasks that are already terminated.  Not-yet-terminated tasks
keep running, and the memory gets freed when they terminate.

It is a language design flaw that you can't block a task until some
other task(s) are terminated -- other than at scope exit, which is
usually way too late.  You can busy wait:

    abort T;
    -- Now T is aborting itself, running finalization actions and whatnot.
    while not T'Terminated loop
        delay X;
    end loop;
    -- Here, we know that T is terminated.

But X is guaranteed to be too long or too short -- most likely both.  ;-)

- Bob

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Abortable Timed Action
  2016-01-09  8:45             ` Simon Wright
  2016-01-09  9:10               ` Dmitry A. Kazakov
  2016-01-09 14:41               ` Bob Duff
@ 2016-01-09 15:59               ` T.G.
  2 siblings, 0 replies; 11+ messages in thread
From: T.G. @ 2016-01-09 15:59 UTC (permalink / raw)


On 2016-01-09, Simon Wright <simon@pushface.org> wrote:
> The common solution is something like

> ...
>         delay until Next;
>         Next := Next + Interval;  -- *not* Ada.Calendar.Clock + Interval

Yes, using Next is much better than my :

          delay until (Start + (Interval * Counter));
          Counter := Counter + 1;

--- news://freenews.netfront.net/ - complaints: news@netfront.net ---


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-01-09 15:59 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-31  4:15 Abortable Timed Action T.G.
2015-12-31  6:40 ` Anh Vo
2015-12-31  7:32   ` T.G.
2015-12-31 16:21     ` Anh Vo
2015-12-31 18:09       ` T.G.
2016-01-06 21:14         ` Anh Vo
2016-01-08 20:24           ` T.G.
2016-01-09  8:45             ` Simon Wright
2016-01-09  9:10               ` Dmitry A. Kazakov
2016-01-09 14:41               ` Bob Duff
2016-01-09 15:59               ` T.G.

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox