From: Reto Buerki on
John B. Matthews wrote:
> In article <h9sgj7$1qr$1(a)news.eternal-september.org>,
> Reto Buerki <reet(a)codelabs.ch> wrote:
>
>> Jeffrey R. Carter wrote:
>>> An entry call is always potentially blocking, regardless of its barrier;
>>> see ARM 9.5.1. Technically, this code contains a bounded error. Since
>>> the potentially blocking operation was detected, your program should
>>> raise Program_Error; GNAT pretends that it hasn't detected that the
>>> operation is potentially blocking and lets you get away with only a
>>> warning. Other compilers (and even other versions of GNAT) may actually
>>> raise Program_Error.
>> Thanks for the clarification and the pointer to the ARM. This basically
>> means that our workaround is not legal Ada code ...
>
> Well, it's a bounded error. Using Pragma Detect_Blocking, as suggested
> by Bob Duff, will tell you if the compiler's warning is actualized at
> run-time. As this is a work-around and your original code seems correct,
> you might arrange things so that the original can be restored easily
> upon upgrade or port.

The code runs correctly even though Program_Error is raised when
compiled with the Detect_Blocking pragma.

I think it's good enough as a workaround, we will upgrade to GNAT 4.4.x
as soon as possible.

Thanks for all your help!
From: John B. Matthews on
In article <h9vp0g$1kn$1(a)news.eternal-september.org>,
Reto Buerki <reet(a)codelabs.ch> wrote:

> John B. Matthews wrote:
> > Using Pragma Detect_Blocking, as suggested by Bob Duff, will tell
> > you if the compiler's warning is actualized at run-time. As this is
> > a work-around and your original code seems correct, you might
> > arrange things so that the original can be restored easily upon
> > upgrade or port.
>
> The code runs correctly even though Program_Error is raised when
> compiled with the Detect_Blocking pragma.
>
> I think it's good enough as a workaround, we will upgrade to GNAT
> 4.4.x as soon as possible.

Excellent, although upgrading has it's own perils. :-)

Another alternative, suggested by Dmitry A. Kazakov, is to replace entry
Wait with procedure Wait, which obviates the need for Signal:

with Ada.Text_IO;
with Alarm_Clock;

procedure Main is
My_Alarm : Alarm_Clock.Alarm_Type;
begin
Ada.Text_IO.Put_Line ("Setting alarm ...");
My_Alarm.Set;

Ada.Text_IO.Put_Line ("Waiting for alarm ...");
My_Alarm.Wait;

Ada.Text_IO.Put_Line ("ALARM!!");
end Main;

with Ada.Real_Time.Timing_Events;

package Alarm_Clock is

protected type Alarm_Type is
procedure Set;
procedure Wait;
end Alarm_Type;

end Alarm_Clock;

package body Alarm_Clock is

use Ada.Real_Time;
Timer : Timing_Events.Timing_Event;
Fired : Boolean := False;

protected body Alarm_Type is

procedure Wakeup(Event : in out Timing_Events.Timing_Event) is
begin
Fired := True;
end Wakeup;

procedure Set is
begin
Timer.Set_Handler(In_Time => Seconds (2),
Handler => Wakeup'Access);
end Set;

procedure Wait is
begin
loop
exit when Fired;
end loop;
end Wait;

end Alarm_Type;

end Alarm_Clock;

> Thanks for all your help!

I enjoyed the opportunity to examine this interesting question.

--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
From: John B. Matthews on
In article <nospam-12AAE0.11590930092009(a)news.aioe.org>,
"John B. Matthews" <nospam(a)nospam.invalid> wrote:

[...]
> Another alternative, suggested by Dmitry A. Kazakov, is to replace
> entry Wait with procedure Wait, which obviates the need for Signal:

Sorry, I carelessly posted code with a busy-wait. Please consider this
alternative, which polls the timer's state:

with Ada.Text_IO;
with Alarm_Clock;

procedure Main is
My_Alarm : Alarm_Clock.Alarm_Type;
begin
Ada.Text_IO.Put_Line ("Setting alarm ...");
My_Alarm.Set;

Ada.Text_IO.Put_Line ("Waiting for alarm ...");
My_Alarm.Wait;

Ada.Text_IO.Put_Line ("ALARM!!");
end Main;

package Alarm_Clock is

type Alarm_Type is tagged null record;
procedure Set(Alarm : Alarm_Type);
procedure Wait(Alarm : Alarm_Type);

end Alarm_Clock;

with Ada.Real_Time.Timing_Events;

package body Alarm_Clock is

use Ada.Real_Time;
Timer : Timing_Events.Timing_Event;
Fired : Boolean := False;

protected Clock is
procedure Wakeup(Event : in out Timing_Events.Timing_Event);
end Clock;

protected body Clock is
procedure Wakeup(Event : in out Timing_Events.Timing_Event) is
begin
Fired := True;
end Wakeup;
end Clock;

procedure Set(Alarm : Alarm_Type) is
begin
Timer.Set_Handler(In_Time => Seconds(3),
Handler => Clock.Wakeup'Access);
end Set;

procedure Wait(Alarm : Alarm_Type) is
begin
loop
delay 1.0;
exit when Fired;
end loop;
end Wait;

end Alarm_Clock;


--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
From: Anh Vo on
On Oct 1, 9:12 am, "John B. Matthews" <nos...(a)nospam.invalid> wrote:
> In article <nospam-12AAE0.11590930092...(a)news.aioe.org>,
>  "John B. Matthews" <nos...(a)nospam.invalid> wrote:
>
> [...]
>
> > Another alternative, suggested by Dmitry A. Kazakov, is to replace
> > entry Wait with procedure Wait, which obviates the need for Signal:
>
> Sorry, I carelessly posted code with a busy-wait. Please consider this
> alternative, which polls the timer's state:
>
> with Ada.Text_IO;
> with Alarm_Clock;
>
> procedure Main is
>    My_Alarm : Alarm_Clock.Alarm_Type;
> begin
>    Ada.Text_IO.Put_Line ("Setting alarm ...");
>    My_Alarm.Set;
>
>    Ada.Text_IO.Put_Line ("Waiting for alarm ...");
>    My_Alarm.Wait;
>
>    Ada.Text_IO.Put_Line ("ALARM!!");
> end Main;
>
> package Alarm_Clock is
>
>    type Alarm_Type is tagged null record;
>    procedure Set(Alarm : Alarm_Type);
>    procedure Wait(Alarm : Alarm_Type);
>
> end Alarm_Clock;
>
> with Ada.Real_Time.Timing_Events;
>
> package body Alarm_Clock is
>
>    use Ada.Real_Time;
>    Timer : Timing_Events.Timing_Event;
>    Fired : Boolean := False;
>
>    protected Clock is
>       procedure Wakeup(Event : in out Timing_Events.Timing_Event);
>    end Clock;
>
>    protected body Clock is
>       procedure Wakeup(Event : in out Timing_Events.Timing_Event) is
>       begin
>          Fired := True;
>       end Wakeup;
>    end Clock;
>
>    procedure Set(Alarm : Alarm_Type) is
>    begin
>       Timer.Set_Handler(In_Time => Seconds(3),
>                         Handler => Clock.Wakeup'Access);
>    end Set;
>
>    procedure Wait(Alarm : Alarm_Type) is
>    begin
>       loop
>          delay 1.0;
>          exit when Fired;
>       end loop;
>    end Wait;
>
> end Alarm_Clock;

I am curious why Alarm_Type is even needed in this case. In addition,
Alarm_Clock works the first time. However, it won't work right on the
second time and on because Fired was not reset after the Alarm goes
off.

Anh Vo
From: John B. Matthews on
In article
<3c32a0db-9878-4057-b3b8-44d03f02571d(a)z3g2000prd.googlegroups.com>,
Anh Vo <anhvofrcaus(a)gmail.com> wrote:

> On Oct 1, 9:12 am, "John B. Matthews" <nos...(a)nospam.invalid> wrote:
> > In article <nospam-12AAE0.11590930092...(a)news.aioe.org>,
> >  "John B. Matthews" <nos...(a)nospam.invalid> wrote:
> > [...]
> > > Another alternative, suggested by Dmitry A. Kazakov, is to
> > > replace entry Wait with procedure Wait, which obviates the need
> > > for Signal:
> >
> > Sorry, I carelessly posted code with a busy-wait. Please consider
> > this alternative, which polls the timer's state:
[...]
> >    type Alarm_Type is tagged null record;
[...]
> I am curious why Alarm_Type is even needed in this case.

Good question. It's not. I was tinkering with several implementation
that matched a variant of the OP's main. As this is a work-around, I
wanted to preserve the calling style, which used the prefixed notation.
That style was available to protected types in Ada 95 and was extended
to tagged types in Ada 05. I didn't realize how much I liked the
notation until I stared using Ada.Containers.

> In addition, Alarm_Clock works the first time. However, it won't work
> right on the second time and on because Fired was not reset after the
> Alarm goes off.

Good catch; thank you. Set should clear Fired.

Another work-around implementation is shown below. Entering Signal from
the protected procedure Wakeup is a bounded error (LRM 9.5.1(11)), and
the compiler (FSF GNAT 4.3.4) issues a corresponding compile time
warning. Interestingly, using pragma Detect_Blocking does not raise
Program_Error. I'm curious to know whether that's just a reasonable
limitation in how the pragma is implemented, or is the entry call
reasonable in this context.

with Ada.Real_Time.Timing_Events;

package Alarm_Clock is

use Ada.Real_Time;

protected type Alarm_Type is
procedure Set;
entry Wait;
entry Signal;
end Alarm_Type;

end Alarm_Clock;

Pragma Detect_Blocking;
package body Alarm_Clock is

Timer : Timing_Events.Timing_Event;

protected body Alarm_Type is

procedure Wakeup(Event : in out Timing_Events.Timing_Event) is
begin
Signal;
end Wakeup;

procedure Set is
begin
Timer.Set_Handler(In_Time => Seconds (1),
Handler => Wakeup'Access);
end Set;

entry Wait when Signal'Count > 0 is
begin
null;
end Wait;

entry Signal when Wait'Count = 0 is
begin
null;
end Signal;

end Alarm_Type;

end Alarm_Clock;

--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>