![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Interrupt Handlers This chapter provides an overview of handling UNIX interrupts in Ada and describes how the Ada runtime system processes interrupts received by an Ada program. It also provides guidelines for writing Ada interrupt-entry tasks, Ada procedural signal handlers, and non-Ada signal handlers to handle the interrupts.
Topics covered in this chapter include:
- Overview of UNIX Interrupt (Signal) Handling
- Apex Ada Signal Handling
- Interrupt-Entry Tasks
- Ada Procedural Signal Handlers
- Non-Ada Signal Handlers
This chapter applies to all native UNIX based products and to Apex Embedded for LynxOS. Other Embedded products provide more direct access to hardware interrupts. See "Interrupts" in the Rational Apex Embedded Programming Guide for Rational Exec or the Rational Apex Embedded Programming Guide for Tornado.
Overview of UNIX Interrupt (Signal) HandlingThis overview provides background for the following explanation of how the Rational Ada runtime system processes interrupts in UNIX. You can find platform-specific and more detailed information in the platform vendor's documentation.
To allow processes to communicate asynchronously, an operating system must provide a method for the processes to notify each other when a significant event occurs. Interrupts provide this functionality. In UNIX, interrupts are called signals.
Signals can be generated deliberately by a program or they can arrive unexpectedly. For example:
- The program might set a timer that causes a signal to be generated when a particular duration has expired.
- A signal might be generated unexpectedly by another process, by a program or bus error, by a user requesting an interrupt or halt at a terminal, or by other abnormal events.
When a program receives a signal:
- It can allow UNIX to take default action.
- It can prevent the signal from having an effect by blocking it.
- It can catch the signal to respond to it and direct the action that is to take place.
To be able to catch and respond to a signal, the program must install a signal handler, which is software that defines the action to take place when a specified signal occurs. UNIX allows a program to install no more than one signal handler for each signal.
Installing a signal handler notifies UNIX that, whenever the program receives a specific signal, the signal handler must be called instead of using default signal handling. The arrival of a signal causes UNIX to switch context to signal context and then invoke the signal handler, usually at an elevated priority.
An Ada program can handle UNIX signals as described in the next section.
Apex Ada Signal HandlingYou can define signal handling for your Ada program by doing one of the following for each signal that you need to process:
- Attach a protected procedure handler to the signal as described in Ada 95 LRM C.3.
- Bind an Ada interrupt-entry task to the signal as described in Interrupt-Entry Tasks, and in the POSIX.5 standard.
- Write a procedural signal handler in Ada, if the runtime system does not restrict the signal's use, as described in Ada Procedural Signal Handlers.
Note: You can use both of the above methods for a single signal only if the Posix_Compliant argument on pragma Main is False.
- Write a signal handler in a non-Ada language; this is the least favored option and must follow restrictions as discussed in Non-Ada Signal Handlers.
Warning: Do not call UNIX signal-handling facilities directly from an Ada program. This can cause the runtime system to block or otherwise affect the correct operation of your program.
When you link your Ada program in Apex, the Rational Ada runtime system is automatically linked with it. When the program executes, the runtime system installs signal handlers for all UNIX signals that either the runtime system or your program uses. All other signals are handled by the default UNIX signal handlers. See Restrictions on UNIX Signals in the Ada Compiler Reference.
The signal handlers installed by the runtime system perform various operations required to process signals from the Ada language. This intervention must happen even for program-specified signal handling because:
- UNIX signal context does not maintain sufficient information about the Ada program context.
- UNIX signal handling does not provide the stack-size monitoring capabilities required by the LRM.
- UNIX cannot manage Ada tasking rendezvous in the manner required by the LRM.
When a signal occurs while your program is executing, the signal handler installed by the runtime system is invoked by UNIX. The signal handler, in order:
- 1 . Performs various setup and initialization tasks.
- 2 . Calls a signal-handling procedure when one exists either because the runtime system includes one to handle signals that it uses or because your program includes one. This procedure executes in signal context.
- 3 . Queues an interrupt entry at interrupt priority if your program declares an interrupt-entry task.
- 4 . Dismisses the signal.
The actual rendezvous occurs at the next context switch for nonpreemptive scheduling or immediately for preemptive scheduling. This guarantees that an Ada interrupt-entry task does not execute until the signal is dismissed, thereby ensuring that no Ada task executes in UNIX signal context.
Note: Steps 2 and 3 can both occur only when the Posix_Compliant argument on pragma Main is False; otherwise, the Program_Error exception is raised.
Interrupt-Entry TasksThe Rational Ada compiler allows an Ada program to handle UNIX signals with interrupt entries as described in Ada83 LRM 13.5.1, Ada95 LRM J.7.1.
Declaring an Interrupt-Entry Task
An interrupt entry is a simple, parameterless task entry to which an address clause has been applied. Your program can use interrupt entries as shown in this code fragment:
with Posix_Signals; task Interrupt_Handler is entry Handle_Sigusr1; entry Handle_Sigusr2; entry Done; for Handle_Sigusr1 use at Posix_Signals.Signal_User_1_Ref; for Handle_Sigusr2 use at Posix_Signals.Signal_User_2_Ref; end Interrupt_Handler; -- At this point entry calls will start being queued for -- Handle_Sigusr1 and Handle_Sigusr2 whenever the -- signals Signal_User_1_Ref and Signal_User_2_Ref -- arrive. Once the task is activated, it will start -- accepting these entry calls. task body Interrupt_Handler is begin loop select accept Handle_Sigusr1 do ... or accept Handle_Sigusr2 do ... or accept Done; exit; end select; end loop; end Interrupt_Handler;
The address clause must give the UNIX signal number for the signal to be handled by the interrupt entry; this binds the signal to the interrupt entry. The address clause can be a nonstatic expression.
In addition to the standard Apex UNIX restrictions, an interrupt-entry task must:
- Be of an anonymous type.
- Be declared in a static scope.
- Observe restrictions on binding interrupt entries to the UNIX signals discussed in Restrictions on UNIX Signals in the Ada Compiler Reference.
- Observe the restrictions discussed in "Restrictions on Preemptive Scheduling", if you want to use preemptive tasking to ensure a quicker response to interrupts.
- Not bind a single UNIX signal to more than one interrupt entry (Ada83 LRM 13.5(8), Ada95 LRM J.7). This will raise Program_Error.
- Not bind a single interrupt entry to more than one signal. Because it can have only one address clause, Ada83 LRM 13.1(4) Ada95 LRM 13.1, this cannot be done.
- Not use entry families for interrupt entries (Ada83 LRM 13.5(6), Ada95 LRM J.7).
Installing the Interrupt Entry
The interrupt entry is said to be installed when its task specification is elaborated successfully. During elaboration of the task, the following events will cause the installation to fail:
- An entry address is specified that is not in the range of valid signal numbers for the target. The Constraint_Error exception is raised.
- An attempt is made to install an interrupt entry for a signal already bound to another interrupt entry. The Program_Error exception is raised.
Note: If an interrupt entry fails to install, any occurrences of the specified signal will be processed as if the interrupt-entry task never existed.
Invoking the Interrupt Entry
An installed interrupt entry can be invoked in two ways:
- A UNIX signal results in an entry call to the task, in which case the task is scheduled as described below.
- The program calls the task normally, in which case no special processing or priorities are applied to the task and its rendezvous.
Interrupt-Entry Signal Handling
After an interrupt entry is installed for a specific signal, even if the task is not yet activated, the runtime system handles any occurrences of the signal as it is delivered to the program as described in Apex Ada Signal Handling.
In more detail, the runtime system queues the interrupt entry at interrupt priority, which is defined as being higher than System.Priority'Last, as specified in Ada83 LRM 13.5.1, Ada95 LRM J.7.1.
Entry calls are queued in the order of arrival of the corresponding signals (Ada83 LRM 9.5(15), Ada95 LRM 9.5.3), and the Count attribute for an interrupt entry can be used to determine the number of pending signals. Signals are not lost unless the heap becomes full, preventing the runtime system from allocating entry queue elements.
Once an interrupt entry is queued, the task is scheduled according to normal Ada tasking rules; that is, the task's priority is compared with other tasks eligible for execution to determine which task runs next.
When the task accepts an interrupt-entry call, it is elevated to interrupt priority for the duration of the rendezvous.
Note: Upon termination of a task containing interrupt entries, any queued calls for its interrupt entries are discarded and no further interrupt-entry calls are queued.
Advantages and Disadvantages of Interrupt Entries
Interrupt entries provide signal handlers that are more platform-independent than signal handlers written in other languages. Each platform vendor has its own mechanism for generating and catching signals. With the interrupt entries defined by the LRM, a software designer can write signal handlers in Ada at a high level of abstraction, hiding most of the architectural and operating-system detail.
The disadvantage is that, because of the controls and limits implicit in rendezvous, interrupt entries provide a potentially delayed response to signals. To provide more immediate response, you can:
- Use preemptive scheduling
- Use procedural signal handlers
Ada Procedural Signal HandlersYou can write Ada procedures to handle UNIX signals. This is a Rational implementation-specific feature.
Declaring a Procedural Signal Handler
A procedural signal handler is an Ada procedure that is assigned to execute when a UNIX signal occurs. Pragma Signal_Handler, described in the Ada Compiler Reference, identifies which signal(s) you want to handle with which Ada procedure.
For example, for a single procedure that will handle two different UNIX signals:
procedure Handle_Signals is begin -- code goes here end Handle_Signals; pragma Signal_Handler (Name => Handle_Signals, Signal => 30); pragma Signal_Handler (Name => Handle_Signals, Signal => 31);
The procedure must be parameterless
The procedure can perform dynamic storage allocation on the stack, if needed. In addition to the standard Apex UNIX restrictions, the procedure must:
- Be declared in a static scope.
- Observe restrictions on the UNIX signals discussed in Restrictions on UNIX Signals in the Ada Compiler Reference. This includes directly or indirectly invoking UNIX system calls that Apex forbids in signal context. This implies that Text_Io and tasking cannot be used.
- Handle any exceptions it raises and not propagate any exceptions.
- Be for a UNIX signal that has not already been assigned to another procedural signal handler. (An attempt to install two signal handlers for a single signal will raise the Program_Error exception.)
- Never handle SIGIO.
- Not modify the signal context designated by the signal-context parameter.
- Not be an instantiation of a generic.
- Not invoke (directly or indirectly) any subprogram in a shared-code generic with a formal limited private type.
- Not instantiate any shared-code generic with a formal limited private type.
- Not invoke (directly or indirectly) any tasking constructs; specifically:
- Declaration or allocation of a task object (or an object containing tasks)
- Entry calls (normal, timed, or conditional)
- Aborts
- Delays
Note: Failure to observe these restrictions can result in a program that operates unpredictably or not at all.
Installing the Procedural Signal Handler
The procedural signal handler is said to be installed when specification is elaborated successfully. During elaboration of the procedure, the following events will cause the installation to fail:
- A signal number is specified that is not in the range of valid signal numbers for the target. The Constraint_Error exception is raised.
- An attempt is made to install a procedural handler for a signal already bound to another procedural handler. The Program_Error exception is raised.
Note: If a signal handler fails to install, any occurrences of the specified signal will be processed as if the signal handler procedure never existed.
After the procedure is installed, it remains in effect until your program terminates.
Invoking the Procedural Signal Handler
An installed procedural handler can be invoked in two ways:
- A UNIX signal causes the runtime-installed signal handler to execute, in which case the procedure operates as described below.
- The program calls the procedure normally, in which case no special processing or priorities are applied to the procedure and signals are not blocked during its execution.
Procedural Signal Handling
After the procedural handler is installed, the runtime system handles any occurrences of the signal as it is delivered to the program as described in Apex Ada Signal Handling.
In more detail, the runtime system invokes the procedure in UNIX signal context on a separate signal stack.
All asynchronous signals are blocked during the execution of the handler. Synchronized access to shared variables in the main program, however, is your program's responsibility.
Non-Ada Signal HandlersMixed-language programs, especially those using Ada tasking constructs, must comply with the following restrictions if they install a non-Ada signal handler:
- Do not invoke Ada code, directly or indirectly, from UNIX signal context. In other words, all non-Ada signal handlers must be written entirely in another language.
This is because a signal can be delivered while executing within a critical region in the Ada runtime system. To avoid corrupting runtime data structures, the runtime system would have to know which signals have handlers that invoke Ada code, in order to block these signals within these critical regions, and this does not occur.
- Do not change the signal stack.
- Observe the restrictions in the Ada Compiler Reference Guide.
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |