Discussion:
Interrupt handler and Ada.Real_Time.Timing_Events
(too old to reply)
Reto Buerki
2009-05-15 16:26:12 UTC
Permalink
Hi,

I hit a rather strange issue today mixing signal/interrupt handling with
Ada.Real_Time.Timing_Events. We have a real life application where we
use timing events but we also need a signal handler to catch signals
from the environment (SIGTERM etc.).

I wrote a small reproducer to illustrate the problem. The following
protected object is used as an interrupt handler, which can be attached
to a specific interrupt/signal:

with Ada.Interrupts;

package Handlers is

protected type Signal_Handler (Signal : Ada.Interrupts.Interrupt_ID)
is
pragma Interrupt_Priority;

entry Wait;
private
procedure Handle_Signal;
pragma Attach_Handler (Handle_Signal, Signal);

Occured : Boolean := False;
end Signal_Handler;

end Handlers;

package body Handlers is

protected body Signal_Handler is
procedure Handle_Signal is
begin
Occured := True;
end Handle_Signal;

entry Wait when Occured is
begin
if Wait'Count = 0 then
Occured := False;
end if;
end Wait;
end Signal_Handler;

end Handlers;

The handler is used like this:

with Ada.Text_IO;
with Ada.Interrupts.Names;

-- Uncommenting the next line breaks interrupt handler
-- with Ada.Real_Time.Timing_Events;

with Handlers;

procedure Interrupt_Problem is
use Ada.Interrupts;

Handler : Handlers.Signal_Handler (Signal => Names.SIGTERM);
begin

if Is_Attached (Interrupt => Names.SIGTERM) then
Ada.Text_IO.Put_Line ("Attached handler to SIGTERM");
else
Ada.Text_IO.Put_Line ("Could not attach to SIGTERM!");
return;
end if;

Handler.Wait;
Ada.Text_IO.Put_Line ("Interrupt received ...");

end Interrupt_Problem;

As expected, when sending SIGTERM to the running 'Interrupt_Problem'
process "Interrupt received ..." is displayed. So far so good.

As commented in the source code, as soon as the
Ada.Real_Time.Timing_Events package is with'ed, this mechanism breaks.

The signal handler is not invoked any more when I send a SIGTERM signal
to a running 'Interrupt_Problem' process, it just terminates without
triggering the Handler.Wait.

What could be the cause for this behavior? Is there a problem with this
code?

Thanks in advance!
- reto
Adam Beneschan
2009-05-15 16:54:17 UTC
Permalink
Post by Reto Buerki
Hi,
I hit a rather strange issue today mixing signal/interrupt handling with
Ada.Real_Time.Timing_Events. We have a real life application where we
use timing events but we also need a signal handler to catch signals
from the environment (SIGTERM etc.).
I wrote a small reproducer to illustrate the problem. The following
protected object is used as an interrupt handler, which can be attached
with Ada.Interrupts;
package Handlers is
protected type Signal_Handler (Signal : Ada.Interrupts.Interrupt_ID)
is
pragma Interrupt_Priority;
entry Wait;
private
procedure Handle_Signal;
pragma Attach_Handler (Handle_Signal, Signal);
Occured : Boolean := False;
end Signal_Handler;
end Handlers;
package body Handlers is
protected body Signal_Handler is
procedure Handle_Signal is
begin
Occured := True;
end Handle_Signal;
entry Wait when Occured is
begin
if Wait'Count = 0 then
Occured := False;
end if;
end Wait;
end Signal_Handler;
end Handlers;
with Ada.Text_IO;
with Ada.Interrupts.Names;
-- Uncommenting the next line breaks interrupt handler
-- with Ada.Real_Time.Timing_Events;
with Handlers;
procedure Interrupt_Problem is
use Ada.Interrupts;
Handler : Handlers.Signal_Handler (Signal => Names.SIGTERM);
begin
if Is_Attached (Interrupt => Names.SIGTERM) then
Ada.Text_IO.Put_Line ("Attached handler to SIGTERM");
else
Ada.Text_IO.Put_Line ("Could not attach to SIGTERM!");
return;
end if;
Handler.Wait;
Ada.Text_IO.Put_Line ("Interrupt received ...");
end Interrupt_Problem;
As expected, when sending SIGTERM to the running 'Interrupt_Problem'
process "Interrupt received ..." is displayed. So far so good.
As commented in the source code, as soon as the
Ada.Real_Time.Timing_Events package is with'ed, this mechanism breaks.
The signal handler is not invoked any more when I send a SIGTERM signal
to a running 'Interrupt_Problem' process, it just terminates without
triggering the Handler.Wait.
What could be the cause for this behavior?
My guess would be that when Ada.Real_Time.Timing_Events is with'ed,
this causes elaboration code for the Timing_Events package to be
executed (before Interrupt_Problem), and there must be something that
this elaboration does that interferes with the Attach_Handler
mechanism. I can't find anything in the language definition of
Timing_Events that would cause this, so it must be a problem
particular to your Ada compiler implementation, and you should contact
the vendor, or at least let us know what compiler you're using so that
others who may have some knowledge of that particular compiler might
be able to help.
Post by Reto Buerki
Is there a problem with this
code?
Yes, definitely: "occurred" is misspelled. It has two R's. I happen
to know this one very well because I blew this word (or another form,
like "occurrence") in my 8th-grade spelling bee by only putting one R
in it. So you've brought up some memories here.......

-- Adam
Reto Buerki
2009-05-15 23:24:04 UTC
Permalink
Post by Adam Beneschan
My guess would be that when Ada.Real_Time.Timing_Events is with'ed,
this causes elaboration code for the Timing_Events package to be
executed (before Interrupt_Problem), and there must be something that
this elaboration does that interferes with the Attach_Handler
mechanism. I can't find anything in the language definition of
Timing_Events that would cause this, so it must be a problem
particular to your Ada compiler implementation, and you should contact
the vendor, or at least let us know what compiler you're using so that
others who may have some knowledge of that particular compiler might
be able to help.
I'm using FSF GNAT 4.3.2 on Debian Lenny.
Post by Adam Beneschan
Post by Reto Buerki
Is there a problem with this
code?
Yes, definitely: "occurred" is misspelled. It has two R's. I happen
to know this one very well because I blew this word (or another form,
like "occurrence") in my 8th-grade spelling bee by only putting one R
in it. So you've brought up some memories here.......
Sorry for digging out your long buried trauma from the 8th-grade
spelling bee! This was not my intention and I will definitely correct
the "Occured" in my example ;)
Ludovic Brenta
2009-05-15 16:56:54 UTC
Permalink
Post by Reto Buerki
Hi,
I hit a rather strange issue today mixing signal/interrupt handling with
Ada.Real_Time.Timing_Events. We have a real life application where we
use timing events but we also need a signal handler to catch signals
from the environment (SIGTERM etc.).
I wrote a small reproducer to illustrate the problem. The following
protected object is used as an interrupt handler, which can be attached
with Ada.Interrupts;
package Handlers is
   protected type Signal_Handler (Signal : Ada.Interrupts.Interrupt_ID)
   is
      pragma Interrupt_Priority;
      entry Wait;
   private
      procedure Handle_Signal;
      pragma Attach_Handler (Handle_Signal, Signal);
      Occured : Boolean := False;
   end Signal_Handler;
end Handlers;
package body Handlers is
   protected body Signal_Handler is
      procedure Handle_Signal is
      begin
         Occured := True;
      end Handle_Signal;
      entry Wait when Occured is
      begin
         if Wait'Count = 0 then
            Occured := False;
         end if;
      end Wait;
   end Signal_Handler;
end Handlers;
with Ada.Text_IO;
with Ada.Interrupts.Names;
--  Uncommenting the next line breaks interrupt handler
--  with Ada.Real_Time.Timing_Events;
with Handlers;
procedure Interrupt_Problem is
   use Ada.Interrupts;
   Handler : Handlers.Signal_Handler (Signal => Names.SIGTERM);
begin
   if Is_Attached (Interrupt => Names.SIGTERM) then
      Ada.Text_IO.Put_Line ("Attached handler to SIGTERM");
   else
      Ada.Text_IO.Put_Line ("Could not attach to SIGTERM!");
      return;
   end if;
   Handler.Wait;
   Ada.Text_IO.Put_Line ("Interrupt received ...");
end Interrupt_Problem;
As expected, when sending SIGTERM to the running 'Interrupt_Problem'
process "Interrupt received ..." is displayed. So far so good.
As commented in the source code, as soon as the
Ada.Real_Time.Timing_Events package is with'ed, this mechanism breaks.
The signal handler is not invoked any more when I send a SIGTERM signal
to a running 'Interrupt_Problem' process, it just terminates without
triggering the Handler.Wait.
What could be the cause for this behavior? Is there a problem with this
code?
Ada.Real_Time.Timing_Events's elaboration block creates a task and
promotes it to an outer level (i.e. it is no longer dependent on a
master). The only way to terminate this task is by sending it
SIGTERM, so the task attaches another signal handler to SIGTERM before
yours. That handler catches the signal and does not propagate it to
any other handler. See System.Task_Primitives.Operations.Initialize.

I'm afraid there is no way out :) maybe you can use another signal in
your task?

--
Ludovic Brenta.
Hibou57 (Yannick Duchêne)
2009-05-15 23:24:36 UTC
Permalink
Post by Ludovic Brenta
Ada.Real_Time.Timing_Events's elaboration block creates a task and
promotes it to an outer level (i.e. it is no longer dependent on a
master).  The only way to terminate this task is by sending it
SIGTERM, so the task attaches another signal handler to SIGTERM before
yours.  That handler catches the signal and does not propagate it to
any other handler.  See System.Task_Primitives.Operations.Initialize.
I'm afraid there is no way out :) maybe you can use another signal in
your task?
--
Ludovic Brenta.
Do you know why it is not propagated ?
SIGTERM is supposed to be intended to the whole of an application, not
only to a pat of it.
Reto Buerki
2009-05-16 00:20:54 UTC
Permalink
Post by Ludovic Brenta
Post by Reto Buerki
What could be the cause for this behavior? Is there a problem with this
code?
Ada.Real_Time.Timing_Events's elaboration block creates a task and
promotes it to an outer level (i.e. it is no longer dependent on a
master). The only way to terminate this task is by sending it
SIGTERM, so the task attaches another signal handler to SIGTERM before
yours. That handler catches the signal and does not propagate it to
any other handler. See System.Task_Primitives.Operations.Initialize.
I'm afraid there is no way out :) maybe you can use another signal in
your task?
I tried attaching the handler to various signals. As soon as the timer
task is started in the Ada.Real_Time.Timing_Events elaboration block, my
own handler is not triggered any more. This seems odd.

We are using Ada.Real_Time.Timing_Events to implement an event-driven
architecture in our application. The Timing_Event type seemed perfect
for this.

Nevertheless, the application should still be able to react to signals
it may receive from the operating system. Is it really Timing_Events XOR
interrupt handling?
Jeffrey R. Carter
2009-05-16 00:38:54 UTC
Permalink
Post by Reto Buerki
We are using Ada.Real_Time.Timing_Events to implement an event-driven
architecture in our application. The Timing_Event type seemed perfect
for this.
Timing_Events are certainly suited for this. However, they were added in the
most recent revision of the language. Event-driven systems were implemented in
Ada long before that revision. Should you be unable to get your application to
work with Timing_Events, you can use the old-fashioned way to achieve the same
thing. This is done by having tasks that execute delay statements and then call
the appropriate protected operations. Effectively, a Timing_Event object is
shorthand for such a task.
--
Jeff Carter
"From this day on, the official language of San Marcos will be Swedish."
Bananas
28
Reto Buerki
2009-05-29 15:59:46 UTC
Permalink
Post by Jeffrey R. Carter
Post by Reto Buerki
We are using Ada.Real_Time.Timing_Events to implement an event-driven
architecture in our application. The Timing_Event type seemed perfect
for this.
Timing_Events are certainly suited for this. However, they were added in
the most recent revision of the language. Event-driven systems were
implemented in Ada long before that revision. Should you be unable to
get your application to work with Timing_Events, you can use the
old-fashioned way to achieve the same thing. This is done by having
tasks that execute delay statements and then call the appropriate
protected operations. Effectively, a Timing_Event object is shorthand
for such a task.
Thanks for your answer. Re-implementing the Timing_Events functionality
seems to be the only possible solution for the moment.

I sent a bug report about this issue to ***@adacore.com and added it
to the GCC bug database (bug #40285).

sjw
2009-05-16 06:28:47 UTC
Permalink
On Mac OS X/GCC 4.3.3, the program as written outputs "raised
PROGRAM_ERROR : Interrupt 15 is reserved".

Changed to SIGUSR1: now runs as designed.

Sending SIGTERM is ignored (this seems odd).

Uncomment Timing_Events: doesn't report anything, but ps shows "User
defined signal 1 ./interrupt_problem" and when I run ps again the
process has gone.

This is all deep stuff (and apparently OS-dependent)! If I had this
sort of problem at work, I would raise a ticket with AdaCore straight
away.
anon
2009-05-16 11:05:11 UTC
Permalink
This is a Timeing example that uses "Ada.Real_Time.Timing_Events" package.

Now, adding an interrupt handler to this design you should wrap the interrupt
handler within a Task routine bacuse "Ada.Real_Time.Timing_Events" uses
tasking for its underlying algorithm, then call the Timers Shutdown routine
once the interrupt has occurred.

--
-- generic_timers.ads
--
with Ada.Real_Time.Timing_Events ;

generic
Multi_Events : Boolean := True ;
Timer_Name : String := "Generic_Timers" ;
Interval : in Ada.Real_Time.Time_Span ;
with procedure Action is <> ;

package Generic_Timers is

Timer_Error : exception ;

procedure Activate ;
procedure Shutdown ;

private

The_Event : Ada.Real_Time.Timing_Events.Timing_Event ;

end Generic_Timers ;

--
-- generic_timers.adb
--
with Ada.Real_Time ;
use Ada.Real_Time ;

package body Generic_Timers is


protected Events is
procedure Handler ( Event: in out Timing_Events.Timing_Event ) ;
end Events ;

protected body Events is
procedure Handler ( Event: in out Timing_Events.Timing_Event ) is
begin
Action ;
if Multi_Events then
Activate ; -- periodic timer continues
end if ;
end Handler ;
end Events ;

procedure Activate is
use type Timing_Events.Timing_Event_Handler ;
begin
if Timing_Events.Current_Handler ( The_Event ) = null then
Timing_Events.Set_Handler ( The_Event,
Interval,
Events.Handler'Access ) ;
else
raise Timer_Error with "Activation " & Timer_Name ;
end if ;
end Activate ;

procedure Shutdown is
Success : Boolean := False ;
use type Timing_Events.Timing_Event_Handler ;
begin
if Timing_Events.Current_Handler ( The_Event ) /= null then
Timing_Events.Cancel_Handler ( The_Event, Success ) ;
if not Success then
raise Timer_Error with "Shutdown: " & Timer_Name ;
end if ;
end if ;
end Shutdown ;

end Generic_Timers ;

--
-- timers.ads
--
package Timers is

procedure Activate ;
procedure Shutdown ;

end Timers ;

--
-- Timers.adb
--
with Ada.Real_Time ;
with Ada.Text_IO ;

with Generic_Timers ;

package body Timers is

use Ada ;
use Real_Time ;
use Text_IO ;

------------------------------------
-- Define Periodic Event Timers --
------------------------------------
Periodic_Interval : constant Time_Span := Milliseconds ( 2000 ) ;
Periodic_Timer_ID : constant String := "Periodic Timer" ;

procedure Periodic_Action is
begin
Put_Line ( "Timeout: Periodic Timer" ) ;
end Periodic_Action ;

Package Periodic_Timer is new Generic_Timers ( True,
Periodic_Timer_ID,
Periodic_Interval,
Periodic_Action ) ;

----------------------------------
-- Define Single Event Timers --
----------------------------------
Single_Interval : constant Time_Span := Milliseconds ( 1000 ) ;
Single_Timer_ID : constant String := "Single Timer" ;

procedure Single_Action is
begin
Put_Line ( "Timeout: Single Timer " ) ;
end Single_Action ;

Package Single_Timer is new Generic_Timers ( False,
Single_Timer_ID,
Single_Interval,
Single_Action ) ;

----------------------------
-- Controlling Routines --
----------------------------

procedure Activate is
begin
Put_Line ( "Timers: Activate" ) ;

Periodic_Timer.Activate ;

for Index in 0 .. 3 loop
Single_Timer.Activate ;
delay 7.0 ;
end loop;
end Activate ;

procedure Shutdown is
begin
Put_Line ( "Timers: Shutdown" ) ;
Periodic_Timer.Shutdown ;
Single_Timer.Shutdown ;
end Shutdown;

end Timers ;

--
-- testing.adb
--
with Ada.Exceptions ;
with Ada.Text_IO ;

with Timers ;

procedure Testing is

use Ada ;
use Text_IO ;

begin

Put_Line ( "Testing : Begin" ) ;

Timers.Activate ;
delay 5.0 ;
Timers.Shutdown ;

Put_Line ( "Testing : End" ) ;
exception
when Error : others =>
Put_Line ( "Testing fails for because of ==> "
& Exceptions.Exception_Information ( Error ) ) ;
end Testing ;
Post by Reto Buerki
Hi,
I hit a rather strange issue today mixing signal/interrupt handling with
Ada.Real_Time.Timing_Events. We have a real life application where we
use timing events but we also need a signal handler to catch signals
from the environment (SIGTERM etc.).
I wrote a small reproducer to illustrate the problem. The following
protected object is used as an interrupt handler, which can be attached
with Ada.Interrupts;
package Handlers is
protected type Signal_Handler (Signal : Ada.Interrupts.Interrupt_ID)
is
pragma Interrupt_Priority;
entry Wait;
private
procedure Handle_Signal;
pragma Attach_Handler (Handle_Signal, Signal);
Occured : Boolean := False;
end Signal_Handler;
end Handlers;
package body Handlers is
protected body Signal_Handler is
procedure Handle_Signal is
begin
Occured := True;
end Handle_Signal;
entry Wait when Occured is
begin
if Wait'Count = 0 then
Occured := False;
end if;
end Wait;
end Signal_Handler;
end Handlers;
with Ada.Text_IO;
with Ada.Interrupts.Names;
-- Uncommenting the next line breaks interrupt handler
-- with Ada.Real_Time.Timing_Events;
with Handlers;
procedure Interrupt_Problem is
use Ada.Interrupts;
Handler : Handlers.Signal_Handler (Signal => Names.SIGTERM);
begin
if Is_Attached (Interrupt => Names.SIGTERM) then
Ada.Text_IO.Put_Line ("Attached handler to SIGTERM");
else
Ada.Text_IO.Put_Line ("Could not attach to SIGTERM!");
return;
end if;
Handler.Wait;
Ada.Text_IO.Put_Line ("Interrupt received ...");
end Interrupt_Problem;
As expected, when sending SIGTERM to the running 'Interrupt_Problem'
process "Interrupt received ..." is displayed. So far so good.
As commented in the source code, as soon as the
Ada.Real_Time.Timing_Events package is with'ed, this mechanism breaks.
The signal handler is not invoked any more when I send a SIGTERM signal
to a running 'Interrupt_Problem' process, it just terminates without
triggering the Handler.Wait.
What could be the cause for this behavior? Is there a problem with this
code?
Thanks in advance!
- reto
Loading...