1 ------------------------------------------------------------------------------
3 -- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
5 -- S Y S T E M . I N T E R R U P T S --
11 -- Copyright (C) 1991-2000 Free Software Foundation, Inc. --
13 -- GNARL is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNARL; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
31 -- GNARL was developed by the GNARL team at Florida State University. It is --
32 -- now maintained by Ada Core Technologies Inc. in cooperation with Florida --
33 -- State University (http://www.gnat.com). --
35 ------------------------------------------------------------------------------
37 -- This is an OpenVMS/Alpha version of this package.
41 -- Once we associate a Server_Task with an interrupt, the task never
42 -- goes away, and we never remove the association.
44 -- There is no more than one interrupt per Server_Task and no more than
45 -- one Server_Task per interrupt.
47 -- Within this package, the lock L is used to protect the various status
48 -- tables. If there is a Server_Task associated with an interrupt, we use
49 -- the per-task lock of the Server_Task instead so that we protect the
50 -- status between Interrupt_Manager and Server_Task. Protection among
51 -- service requests are done using User Request to Interrupt_Manager
54 with Ada.Task_Identification;
55 -- used for Task_ID type
58 -- used for Raise_Exception
60 with System.Task_Primitives;
64 with System.Interrupt_Management;
68 -- Abort_Task_Interrupt
70 with System.Interrupt_Management.Operations;
71 -- used for Thread_Block_Interrupt
72 -- Thread_Unblock_Interrupt
73 -- Install_Default_Action
74 -- Install_Ignore_Action
75 -- Copy_Interrupt_Mask
77 -- Empty_Interrupt_Mask
78 -- Fill_Interrupt_Mask
79 -- Add_To_Interrupt_Mask
80 -- Delete_From_Interrupt_Mask
82 -- Interrupt_Self_Process
88 pragma Elaborate_All (System.Interrupt_Management.Operations);
90 with System.Error_Reporting;
91 pragma Warnings (Off, System.Error_Reporting);
94 with System.Task_Primitives.Operations;
95 -- used for Write_Lock
102 with System.Task_Primitives.Interrupt_Operations;
103 -- used for Set_Interrupt_ID
105 with System.Storage_Elements;
106 -- used for To_Address
115 -- Interrupt_Manager_ID
117 with System.Tasking.Utilities;
118 -- used for Make_Independent
120 with System.Tasking.Rendezvous;
121 -- used for Call_Simple
122 pragma Elaborate_All (System.Tasking.Rendezvous);
124 with System.Tasking.Initialization;
125 -- used for Defer_Abort
128 with Unchecked_Conversion;
130 package body System.Interrupts is
133 use System.Error_Reporting;
136 package PRI renames System.Task_Primitives;
137 package POP renames System.Task_Primitives.Operations;
138 package PIO renames System.Task_Primitives.Interrupt_Operations;
139 package IMNG renames System.Interrupt_Management;
140 package IMOP renames System.Interrupt_Management.Operations;
142 function To_System is new Unchecked_Conversion
143 (Ada.Task_Identification.Task_Id, Task_ID);
149 -- WARNING: System.Tasking.Utilities performs calls to this task
150 -- with low-level constructs. Do not change this spec without synchro-
153 task Interrupt_Manager is
154 entry Initialize (Mask : IMNG.Interrupt_Mask);
157 (New_Handler : in Parameterless_Handler;
158 Interrupt : in Interrupt_ID;
160 Restoration : in Boolean := False);
162 entry Exchange_Handler
163 (Old_Handler : out Parameterless_Handler;
164 New_Handler : in Parameterless_Handler;
165 Interrupt : in Interrupt_ID;
166 Static : in Boolean);
169 (Interrupt : in Interrupt_ID;
170 Static : in Boolean);
172 entry Bind_Interrupt_To_Entry
174 E : Task_Entry_Index;
175 Interrupt : Interrupt_ID);
177 entry Detach_Interrupt_Entries (T : Task_ID);
179 entry Block_Interrupt (Interrupt : Interrupt_ID);
181 entry Unblock_Interrupt (Interrupt : Interrupt_ID);
183 entry Ignore_Interrupt (Interrupt : Interrupt_ID);
185 entry Unignore_Interrupt (Interrupt : Interrupt_ID);
187 pragma Interrupt_Priority (System.Interrupt_Priority'Last);
188 end Interrupt_Manager;
190 task type Server_Task (Interrupt : Interrupt_ID) is
191 pragma Priority (System.Interrupt_Priority'Last);
194 type Server_Task_Access is access Server_Task;
196 --------------------------------
197 -- Local Types and Variables --
198 --------------------------------
200 type Entry_Assoc is record
202 E : Task_Entry_Index;
205 type Handler_Assoc is record
206 H : Parameterless_Handler;
207 Static : Boolean; -- Indicates static binding;
210 User_Handler : array (Interrupt_ID'Range) of Handler_Assoc :=
211 (others => (null, Static => False));
212 pragma Volatile_Components (User_Handler);
213 -- Holds the protected procedure handler (if any) and its Static
214 -- information for each interrupt. A handler is a Static one if
215 -- it is specified through the pragma Attach_Handler.
216 -- Attach_Handler. Otherwise, not static)
218 User_Entry : array (Interrupt_ID'Range) of Entry_Assoc :=
219 (others => (T => Null_Task, E => Null_Task_Entry));
220 pragma Volatile_Components (User_Entry);
221 -- Holds the task and entry index (if any) for each interrupt
223 Blocked : array (Interrupt_ID'Range) of Boolean := (others => False);
224 pragma Volatile_Components (Blocked);
225 -- True iff the corresponding interrupt is blocked in the process level
227 Ignored : array (Interrupt_ID'Range) of Boolean := (others => False);
228 pragma Volatile_Components (Ignored);
229 -- True iff the corresponding interrupt is blocked in the process level
232 array (Interrupt_ID'Range) of Task_ID := (others => Null_Task);
233 pragma Volatile_Components (Last_Unblocker);
234 -- Holds the ID of the last Task which Unblocked this Interrupt.
235 -- It contains Null_Task if no tasks have ever requested the
236 -- Unblocking operation or the Interrupt is currently Blocked.
238 Server_ID : array (Interrupt_ID'Range) of Task_ID :=
239 (others => Null_Task);
240 pragma Atomic_Components (Server_ID);
241 -- Holds the Task_ID of the Server_Task for each interrupt.
242 -- Task_ID is needed to accomplish locking per Interrupt base. Also
243 -- is needed to decide whether to create a new Server_Task.
245 -- Type and Head, Tail of the list containing Registered Interrupt
246 -- Handlers. These definitions are used to register the handlers
247 -- specified by the pragma Interrupt_Handler.
249 type Registered_Handler;
250 type R_Link is access all Registered_Handler;
252 type Registered_Handler is record
253 H : System.Address := System.Null_Address;
254 Next : R_Link := null;
257 Registered_Handler_Head : R_Link := null;
258 Registered_Handler_Tail : R_Link := null;
260 Access_Hold : Server_Task_Access;
261 -- variable used to allocate Server_Task using "new".
263 L : aliased PRI.RTS_Lock;
264 -- L protects contents in tables above corresponding to interrupts
265 -- for which Server_ID (T) = null.
267 -- If Server_ID (T) /= null then protection is via
268 -- per-task (TCB) lock of Server_ID (T).
270 -- For deadlock prevention, L should not be locked after
271 -- any other lock is held.
273 Task_Lock : array (Interrupt_ID'Range) of Boolean := (others => False);
274 -- Boolean flags to give matching Locking and Unlocking. See the comments
275 -- in Lock_Interrupt.
277 -----------------------
278 -- Local Subprograms --
279 -----------------------
281 procedure Lock_Interrupt
283 Interrupt : Interrupt_ID);
284 -- protect the tables using L or per-task lock. Set the Boolean
285 -- value Task_Lock if the lock is made using per-task lock.
286 -- This information is needed so that Unlock_Interrupt
287 -- performs unlocking on the same lock. The situation we are preventing
288 -- is, for example, when Attach_Handler is called for the first time
289 -- we lock L and create an Server_Task. For a matching unlocking, if we
290 -- rely on the fact that there is a Server_Task, we will unlock the
293 procedure Unlock_Interrupt
295 Interrupt : Interrupt_ID);
297 function Is_Registered (Handler : Parameterless_Handler) return Boolean;
304 -- This package has been modified several times.
305 -- Do we still need this fancy locking scheme, now that more operations
306 -- are entries of the interrupt manager task?
308 -- More likely, we will need to convert one or more entry calls to
309 -- protected operations, because presently we are violating locking order
310 -- rules by calling a task entry from within the runtime system.
312 procedure Lock_Interrupt
314 Interrupt : Interrupt_ID)
317 Initialization.Defer_Abort (Self_ID);
319 POP.Write_Lock (L'Access);
321 if Task_Lock (Interrupt) then
323 -- We need to use per-task lock.
325 POP.Unlock (L'Access);
326 POP.Write_Lock (Server_ID (Interrupt));
328 -- Rely on the fact that once Server_ID is set to a non-null
329 -- value it will never be set back to null.
331 elsif Server_ID (Interrupt) /= Null_Task then
333 -- We need to use per-task lock.
335 Task_Lock (Interrupt) := True;
336 POP.Unlock (L'Access);
337 POP.Write_Lock (Server_ID (Interrupt));
341 ----------------------
342 -- Unlock_Interrupt --
343 ----------------------
345 procedure Unlock_Interrupt
347 Interrupt : Interrupt_ID)
350 if Task_Lock (Interrupt) then
351 POP.Unlock (Server_ID (Interrupt));
353 POP.Unlock (L'Access);
356 Initialization.Undefer_Abort (Self_ID);
357 end Unlock_Interrupt;
359 ----------------------------------
360 -- Register_Interrupt_Handler --
361 ----------------------------------
363 procedure Register_Interrupt_Handler (Handler_Addr : System.Address) is
364 New_Node_Ptr : R_Link;
367 -- This routine registers the Handler as usable for Dynamic
368 -- Interrupt Handler. Routines attaching and detaching Handler
369 -- dynamically should first consult if the Handler is rgistered.
370 -- A Program Error should be raised if it is not registered.
372 -- The pragma Interrupt_Handler can only appear in the library
373 -- level PO definition and instantiation. Therefore, we do not need
374 -- to implement Unregistering operation. Neither we need to
375 -- protect the queue structure using a Lock.
377 pragma Assert (Handler_Addr /= System.Null_Address);
379 New_Node_Ptr := new Registered_Handler;
380 New_Node_Ptr.H := Handler_Addr;
382 if Registered_Handler_Head = null then
383 Registered_Handler_Head := New_Node_Ptr;
384 Registered_Handler_Tail := New_Node_Ptr;
387 Registered_Handler_Tail.Next := New_Node_Ptr;
388 Registered_Handler_Tail := New_Node_Ptr;
390 end Register_Interrupt_Handler;
396 -- See if the Handler has been "pragma"ed using Interrupt_Handler.
397 -- Always consider a null handler as registered.
399 function Is_Registered (Handler : Parameterless_Handler) return Boolean is
401 type Fat_Ptr is record
402 Object_Addr : System.Address;
403 Handler_Addr : System.Address;
406 function To_Fat_Ptr is new Unchecked_Conversion
407 (Parameterless_Handler, Fat_Ptr);
413 if Handler = null then
417 Fat := To_Fat_Ptr (Handler);
419 Ptr := Registered_Handler_Head;
421 while (Ptr /= null) loop
422 if Ptr.H = Fat.Handler_Addr then
437 function Is_Reserved (Interrupt : Interrupt_ID) return Boolean is
439 return IMNG.Reserve (IMNG.Interrupt_ID (Interrupt));
442 -----------------------
443 -- Is_Entry_Attached --
444 -----------------------
446 function Is_Entry_Attached (Interrupt : Interrupt_ID) return Boolean is
448 if Is_Reserved (Interrupt) then
449 Raise_Exception (Program_Error'Identity, "Interrupt" &
450 Interrupt_ID'Image (Interrupt) & " is reserved");
453 return User_Entry (Interrupt).T /= Null_Task;
454 end Is_Entry_Attached;
456 -------------------------
457 -- Is_Handler_Attached --
458 -------------------------
460 function Is_Handler_Attached (Interrupt : Interrupt_ID) return Boolean is
462 if Is_Reserved (Interrupt) then
463 Raise_Exception (Program_Error'Identity, "Interrupt" &
464 Interrupt_ID'Image (Interrupt) & " is reserved");
467 return User_Handler (Interrupt).H /= null;
468 end Is_Handler_Attached;
474 function Is_Blocked (Interrupt : Interrupt_ID) return Boolean is
476 if Is_Reserved (Interrupt) then
477 Raise_Exception (Program_Error'Identity, "Interrupt" &
478 Interrupt_ID'Image (Interrupt) & " is reserved");
481 return Blocked (Interrupt);
488 function Is_Ignored (Interrupt : Interrupt_ID) return Boolean is
490 if Is_Reserved (Interrupt) then
491 Raise_Exception (Program_Error'Identity, "Interrupt" &
492 Interrupt_ID'Image (Interrupt) & " is reserved");
495 return Ignored (Interrupt);
498 ---------------------
499 -- Current_Handler --
500 ---------------------
502 function Current_Handler (Interrupt : Interrupt_ID)
503 return Parameterless_Handler is
505 if Is_Reserved (Interrupt) then
506 Raise_Exception (Program_Error'Identity, "Interrupt" &
507 Interrupt_ID'Image (Interrupt) & " is reserved");
510 -- ??? Since Parameterless_Handler is not Atomic, the
511 -- current implementation is wrong. We need a new service in
512 -- Interrupt_Manager to ensure atomicity.
514 return User_Handler (Interrupt).H;
521 -- Calling this procedure with New_Handler = null and Static = True
522 -- means we want to detach the current handler regardless of the
523 -- previous handler's binding status (ie. do not care if it is a
524 -- dynamic or static handler).
526 -- This option is needed so that during the finalization of a PO, we
527 -- can detach handlers attached through pragma Attach_Handler.
529 procedure Attach_Handler
530 (New_Handler : in Parameterless_Handler;
531 Interrupt : in Interrupt_ID;
532 Static : in Boolean := False)
535 if Is_Reserved (Interrupt) then
536 Raise_Exception (Program_Error'Identity, "Interrupt" &
537 Interrupt_ID'Image (Interrupt) & " is reserved");
540 Interrupt_Manager.Attach_Handler (New_Handler, Interrupt, Static);
544 ----------------------
545 -- Exchange_Handler --
546 ----------------------
548 -- Calling this procedure with New_Handler = null and Static = True
549 -- means we want to detach the current handler regardless of the
550 -- previous handler's binding status (ie. do not care if it is a
551 -- dynamic or static handler).
553 -- This option is needed so that during the finalization of a PO, we
554 -- can detach handlers attached through pragma Attach_Handler.
556 procedure Exchange_Handler
557 (Old_Handler : out Parameterless_Handler;
558 New_Handler : in Parameterless_Handler;
559 Interrupt : in Interrupt_ID;
560 Static : in Boolean := False)
563 if Is_Reserved (Interrupt) then
564 Raise_Exception (Program_Error'Identity, "Interrupt" &
565 Interrupt_ID'Image (Interrupt) & " is reserved");
568 Interrupt_Manager.Exchange_Handler
569 (Old_Handler, New_Handler, Interrupt, Static);
571 end Exchange_Handler;
577 -- Calling this procedure with Static = True means we want to Detach the
578 -- current handler regardless of the previous handler's binding status
579 -- (i.e. do not care if it is a dynamic or static handler).
581 -- This option is needed so that during the finalization of a PO, we can
582 -- detach handlers attached through pragma Attach_Handler.
584 procedure Detach_Handler
585 (Interrupt : in Interrupt_ID;
586 Static : in Boolean := False)
589 if Is_Reserved (Interrupt) then
590 Raise_Exception (Program_Error'Identity, "Interrupt" &
591 Interrupt_ID'Image (Interrupt) & " is reserved");
594 Interrupt_Manager.Detach_Handler (Interrupt, Static);
602 function Reference (Interrupt : Interrupt_ID) return System.Address is
604 if Is_Reserved (Interrupt) then
605 Raise_Exception (Program_Error'Identity, "Interrupt" &
606 Interrupt_ID'Image (Interrupt) & " is reserved");
609 return Storage_Elements.To_Address
610 (Storage_Elements.Integer_Address (Interrupt));
613 -----------------------------
614 -- Bind_Interrupt_To_Entry --
615 -----------------------------
617 -- This procedure raises a Program_Error if it tries to
618 -- bind an interrupt to which an Entry or a Procedure is
621 procedure Bind_Interrupt_To_Entry
623 E : Task_Entry_Index;
624 Int_Ref : System.Address)
626 Interrupt : constant Interrupt_ID :=
627 Interrupt_ID (Storage_Elements.To_Integer (Int_Ref));
630 if Is_Reserved (Interrupt) then
631 Raise_Exception (Program_Error'Identity, "Interrupt" &
632 Interrupt_ID'Image (Interrupt) & " is reserved");
635 Interrupt_Manager.Bind_Interrupt_To_Entry (T, E, Interrupt);
637 end Bind_Interrupt_To_Entry;
639 ------------------------------
640 -- Detach_Interrupt_Entries --
641 ------------------------------
643 procedure Detach_Interrupt_Entries (T : Task_ID) is
645 Interrupt_Manager.Detach_Interrupt_Entries (T);
646 end Detach_Interrupt_Entries;
648 ---------------------
649 -- Block_Interrupt --
650 ---------------------
652 procedure Block_Interrupt (Interrupt : Interrupt_ID) is
654 if Is_Reserved (Interrupt) then
655 Raise_Exception (Program_Error'Identity, "Interrupt" &
656 Interrupt_ID'Image (Interrupt) & " is reserved");
659 Interrupt_Manager.Block_Interrupt (Interrupt);
662 -----------------------
663 -- Unblock_Interrupt --
664 -----------------------
666 procedure Unblock_Interrupt (Interrupt : Interrupt_ID) is
668 if Is_Reserved (Interrupt) then
669 Raise_Exception (Program_Error'Identity, "Interrupt" &
670 Interrupt_ID'Image (Interrupt) & " is reserved");
673 Interrupt_Manager.Unblock_Interrupt (Interrupt);
674 end Unblock_Interrupt;
680 function Unblocked_By
681 (Interrupt : Interrupt_ID)
682 return System.Tasking.Task_ID
685 if Is_Reserved (Interrupt) then
686 Raise_Exception (Program_Error'Identity, "Interrupt" &
687 Interrupt_ID'Image (Interrupt) & " is reserved");
690 return Last_Unblocker (Interrupt);
693 ----------------------
694 -- Ignore_Interrupt --
695 ----------------------
697 procedure Ignore_Interrupt (Interrupt : Interrupt_ID) is
699 if Is_Reserved (Interrupt) then
700 Raise_Exception (Program_Error'Identity, "Interrupt" &
701 Interrupt_ID'Image (Interrupt) & " is reserved");
704 Interrupt_Manager.Ignore_Interrupt (Interrupt);
705 end Ignore_Interrupt;
707 ------------------------
708 -- Unignore_Interrupt --
709 ------------------------
711 procedure Unignore_Interrupt (Interrupt : Interrupt_ID) is
713 if Is_Reserved (Interrupt) then
714 Raise_Exception (Program_Error'Identity, "Interrupt" &
715 Interrupt_ID'Image (Interrupt) & " is reserved");
718 Interrupt_Manager.Unignore_Interrupt (Interrupt);
719 end Unignore_Interrupt;
721 -----------------------
722 -- Interrupt_Manager --
723 -----------------------
725 task body Interrupt_Manager is
727 ----------------------
728 -- Local Variables --
729 ----------------------
731 Intwait_Mask : aliased IMNG.Interrupt_Mask;
732 Ret_Interrupt : Interrupt_ID;
733 Old_Mask : aliased IMNG.Interrupt_Mask;
734 Self_ID : Task_ID := POP.Self;
736 ---------------------
738 ---------------------
740 procedure Unprotected_Exchange_Handler
741 (Old_Handler : out Parameterless_Handler;
742 New_Handler : in Parameterless_Handler;
743 Interrupt : in Interrupt_ID;
745 Restoration : in Boolean := False);
747 procedure Unprotected_Detach_Handler
748 (Interrupt : in Interrupt_ID;
749 Static : in Boolean);
751 ----------------------------------
752 -- Unprotected_Exchange_Handler --
753 ----------------------------------
755 procedure Unprotected_Exchange_Handler
756 (Old_Handler : out Parameterless_Handler;
757 New_Handler : in Parameterless_Handler;
758 Interrupt : in Interrupt_ID;
760 Restoration : in Boolean := False)
763 if User_Entry (Interrupt).T /= Null_Task then
765 -- In case we have an Interrupt Entry already installed.
766 -- raise a program error. (propagate it to the caller).
768 Unlock_Interrupt (Self_ID, Interrupt);
769 Raise_Exception (Program_Error'Identity,
770 "An interrupt is already installed");
773 -- Note : A null handler with Static = True will
774 -- pass the following check. That is the case when we want to
775 -- Detach a handler regardless of the Static status
776 -- of the current_Handler.
777 -- We don't check anything if Restoration is True, since we
778 -- may be detaching a static handler to restore a dynamic one.
780 if not Restoration and then not Static
782 -- Tries to overwrite a static Interrupt Handler with a
785 and then (User_Handler (Interrupt).Static
787 -- The new handler is not specified as an
788 -- Interrupt Handler by a pragma.
790 or else not Is_Registered (New_Handler))
792 Unlock_Interrupt (Self_ID, Interrupt);
793 Raise_Exception (Program_Error'Identity,
794 "Trying to overwrite a static Interrupt Handler with a " &
798 -- The interrupt should no longer be ingnored if
799 -- it was ever ignored.
801 Ignored (Interrupt) := False;
803 -- Save the old handler
805 Old_Handler := User_Handler (Interrupt).H;
809 User_Handler (Interrupt).H := New_Handler;
811 if New_Handler = null then
813 -- The null handler means we are detaching the handler.
815 User_Handler (Interrupt).Static := False;
818 User_Handler (Interrupt).Static := Static;
821 -- Invoke a corresponding Server_Task if not yet created.
822 -- Place Task_ID info in Server_ID array.
824 if Server_ID (Interrupt) = Null_Task then
825 Access_Hold := new Server_Task (Interrupt);
826 Server_ID (Interrupt) := To_System (Access_Hold.all'Identity);
828 POP.Wakeup (Server_ID (Interrupt), Interrupt_Server_Idle_Sleep);
831 end Unprotected_Exchange_Handler;
833 --------------------------------
834 -- Unprotected_Detach_Handler --
835 --------------------------------
837 procedure Unprotected_Detach_Handler
838 (Interrupt : in Interrupt_ID;
841 Old_Handler : Parameterless_Handler;
844 if User_Entry (Interrupt).T /= Null_Task then
846 -- In case we have an Interrupt Entry installed.
847 -- raise a program error. (propagate it to the caller).
849 Unlock_Interrupt (Self_ID, Interrupt);
850 Raise_Exception (Program_Error'Identity,
851 "An interrupt entry is already installed");
854 -- Note : Static = True will pass the following check. That is the
855 -- case when we want to detach a handler regardless of the static
856 -- status of the current_Handler.
858 if not Static and then User_Handler (Interrupt).Static then
860 -- Tries to detach a static Interrupt Handler.
861 -- raise a program error.
863 Unlock_Interrupt (Self_ID, Interrupt);
864 Raise_Exception (Program_Error'Identity,
865 "Trying to detach a static Interrupt Handler");
868 -- The interrupt should no longer be ignored if
869 -- it was ever ignored.
871 Ignored (Interrupt) := False;
873 Old_Handler := User_Handler (Interrupt).H;
877 User_Handler (Interrupt).H := null;
878 User_Handler (Interrupt).Static := False;
879 IMOP.Interrupt_Self_Process (IMNG.Interrupt_ID (Interrupt));
881 end Unprotected_Detach_Handler;
883 -- Start of processing for Interrupt_Manager
886 -- By making this task independent of master, when the process
887 -- goes away, the Interrupt_Manager will terminate gracefully.
889 System.Tasking.Utilities.Make_Independent;
891 -- Environmen task gets its own interrupt mask, saves it,
892 -- and then masks all interrupts except the Keep_Unmasked set.
894 -- During rendezvous, the Interrupt_Manager receives the old
895 -- interrupt mask of the environment task, and sets its own
896 -- interrupt mask to that value.
898 -- The environment task will call the entry of Interrupt_Manager some
899 -- during elaboration of the body of this package.
901 accept Initialize (Mask : IMNG.Interrupt_Mask) do
905 -- Note: All tasks in RTS will have all the Reserve Interrupts
906 -- being masked (except the Interrupt_Manager) and Keep_Unmasked
907 -- unmasked when created.
909 -- Abort_Task_Interrupt is one of the Interrupt unmasked
910 -- in all tasks. We mask the Interrupt in this particular task
911 -- so that "sigwait" is possible to catch an explicitly sent
912 -- Abort_Task_Interrupt from the Server_Tasks.
914 -- This sigwaiting is needed so that we make sure a Server_Task is
915 -- out of its own sigwait state. This extra synchronization is
916 -- necessary to prevent following senarios.
918 -- 1) Interrupt_Manager sends an Abort_Task_Interrupt to the
919 -- Server_Task then changes its own interrupt mask (OS level).
920 -- If an interrupt (corresponding to the Server_Task) arrives
921 -- in the nean time we have the Interrupt_Manager umnasked and
922 -- the Server_Task waiting on sigwait.
924 -- 2) For unbinding handler, we install a default action in the
925 -- Interrupt_Manager. POSIX.1c states that the result of using
926 -- "sigwait" and "sigaction" simaltaneously on the same interrupt
927 -- is undefined. Therefore, we need to be informed from the
928 -- Server_Task of the fact that the Server_Task is out of its
932 -- A block is needed to absorb Program_Error exception
935 Old_Handler : Parameterless_Handler;
940 accept Attach_Handler
941 (New_Handler : in Parameterless_Handler;
942 Interrupt : in Interrupt_ID;
944 Restoration : in Boolean := False)
946 Lock_Interrupt (Self_ID, Interrupt);
947 Unprotected_Exchange_Handler
948 (Old_Handler, New_Handler, Interrupt, Static, Restoration);
949 Unlock_Interrupt (Self_ID, Interrupt);
952 or accept Exchange_Handler
953 (Old_Handler : out Parameterless_Handler;
954 New_Handler : in Parameterless_Handler;
955 Interrupt : in Interrupt_ID;
958 Lock_Interrupt (Self_ID, Interrupt);
959 Unprotected_Exchange_Handler
960 (Old_Handler, New_Handler, Interrupt, Static);
961 Unlock_Interrupt (Self_ID, Interrupt);
962 end Exchange_Handler;
964 or accept Detach_Handler
965 (Interrupt : in Interrupt_ID;
968 Lock_Interrupt (Self_ID, Interrupt);
969 Unprotected_Detach_Handler (Interrupt, Static);
970 Unlock_Interrupt (Self_ID, Interrupt);
973 or accept Bind_Interrupt_To_Entry
975 E : Task_Entry_Index;
976 Interrupt : Interrupt_ID)
978 Lock_Interrupt (Self_ID, Interrupt);
980 -- if there is a binding already (either a procedure or an
981 -- entry), raise Program_Error (propagate it to the caller).
983 if User_Handler (Interrupt).H /= null
984 or else User_Entry (Interrupt).T /= Null_Task
986 Unlock_Interrupt (Self_ID, Interrupt);
987 Raise_Exception (Program_Error'Identity,
988 "A binding for this interrupt is already present");
991 -- The interrupt should no longer be ingnored if
992 -- it was ever ignored.
994 Ignored (Interrupt) := False;
995 User_Entry (Interrupt) := Entry_Assoc' (T => T, E => E);
997 -- Indicate the attachment of Interrupt Entry in ATCB.
998 -- This is need so that when an Interrupt Entry task
999 -- terminates the binding can be cleaned.
1000 -- The call to unbinding must be
1001 -- make by the task before it terminates.
1003 T.Interrupt_Entry := True;
1005 -- Invoke a corresponding Server_Task if not yet created.
1006 -- Place Task_ID info in Server_ID array.
1008 if Server_ID (Interrupt) = Null_Task then
1010 Access_Hold := new Server_Task (Interrupt);
1011 Server_ID (Interrupt) :=
1012 To_System (Access_Hold.all'Identity);
1014 POP.Wakeup (Server_ID (Interrupt),
1015 Interrupt_Server_Idle_Sleep);
1018 Unlock_Interrupt (Self_ID, Interrupt);
1019 end Bind_Interrupt_To_Entry;
1021 or accept Detach_Interrupt_Entries (T : Task_ID)
1023 for I in Interrupt_ID'Range loop
1024 if not Is_Reserved (I) then
1025 Lock_Interrupt (Self_ID, I);
1027 if User_Entry (I).T = T then
1029 -- The interrupt should no longer be ignored if
1030 -- it was ever ignored.
1032 Ignored (I) := False;
1033 User_Entry (I) := Entry_Assoc'
1034 (T => Null_Task, E => Null_Task_Entry);
1035 IMOP.Interrupt_Self_Process (IMNG.Interrupt_ID (I));
1038 Unlock_Interrupt (Self_ID, I);
1042 -- Indicate in ATCB that no Interrupt Entries are attached.
1044 T.Interrupt_Entry := False;
1045 end Detach_Interrupt_Entries;
1047 or accept Block_Interrupt (Interrupt : Interrupt_ID) do
1048 raise Program_Error;
1049 end Block_Interrupt;
1051 or accept Unblock_Interrupt (Interrupt : Interrupt_ID) do
1052 raise Program_Error;
1053 end Unblock_Interrupt;
1055 or accept Ignore_Interrupt (Interrupt : Interrupt_ID) do
1056 raise Program_Error;
1057 end Ignore_Interrupt;
1059 or accept Unignore_Interrupt (Interrupt : Interrupt_ID) do
1060 raise Program_Error;
1061 end Unignore_Interrupt;
1067 -- If there is a program error we just want to propagate it
1068 -- to the caller and do not want to stop this task.
1070 when Program_Error =>
1075 (Shutdown ("Interrupt_Manager---exception not expected"));
1081 pragma Assert (Shutdown ("Interrupt_Manager---should not get here"));
1083 end Interrupt_Manager;
1089 task body Server_Task is
1090 Self_ID : Task_ID := Self;
1091 Tmp_Handler : Parameterless_Handler;
1093 Tmp_Entry_Index : Task_Entry_Index;
1094 Intwait_Mask : aliased IMNG.Interrupt_Mask;
1095 Ret_Interrupt : IMNG.Interrupt_ID;
1098 -- By making this task independent of master, when the process
1099 -- goes away, the Server_Task will terminate gracefully.
1101 System.Tasking.Utilities.Make_Independent;
1103 -- Install default action in system level.
1105 IMOP.Install_Default_Action (IMNG.Interrupt_ID (Interrupt));
1107 -- Set up the mask (also clears the event flag)
1109 IMOP.Empty_Interrupt_Mask (Intwait_Mask'Access);
1110 IMOP.Add_To_Interrupt_Mask
1111 (Intwait_Mask'Access, IMNG.Interrupt_ID (Interrupt));
1113 -- Remember the Interrupt_ID for Abort_Task.
1115 PIO.Set_Interrupt_ID (IMNG.Interrupt_ID (Interrupt), Self_ID);
1117 -- Note: All tasks in RTS will have all the Reserve Interrupts
1118 -- being masked (except the Interrupt_Manager) and Keep_Unmasked
1119 -- unmasked when created.
1122 System.Tasking.Initialization.Defer_Abort (Self_ID);
1124 -- A Handler or an Entry is installed. At this point all tasks
1125 -- mask for the Interrupt is masked. Catch the Interrupt using
1128 -- This task may wake up from sigwait by receiving an interrupt
1129 -- (Abort_Task_Interrupt) from the Interrupt_Manager for unbinding
1130 -- a Procedure Handler or an Entry. Or it could be a wake up
1131 -- from status change (Unblocked -> Blocked). If that is not
1132 -- the case, we should exceute the attached Procedure or Entry.
1134 POP.Write_Lock (Self_ID);
1136 if User_Handler (Interrupt).H = null
1137 and then User_Entry (Interrupt).T = Null_Task
1139 -- No Interrupt binding. If there is an interrupt,
1140 -- Interrupt_Manager will take default action.
1142 Self_ID.Common.State := Interrupt_Server_Idle_Sleep;
1143 POP.Sleep (Self_ID, Interrupt_Server_Idle_Sleep);
1144 Self_ID.Common.State := Runnable;
1148 Self_ID.Common.State := Interrupt_Server_Blocked_On_Event_Flag;
1149 Ret_Interrupt := IMOP.Interrupt_Wait (Intwait_Mask'Access);
1150 Self_ID.Common.State := Runnable;
1152 if not (Self_ID.Deferral_Level = 0
1153 and then Self_ID.Pending_ATC_Level
1154 < Self_ID.ATC_Nesting_Level)
1156 if User_Handler (Interrupt).H /= null then
1157 Tmp_Handler := User_Handler (Interrupt).H;
1159 -- RTS calls should not be made with self being locked.
1161 POP.Unlock (Self_ID);
1164 POP.Write_Lock (Self_ID);
1166 elsif User_Entry (Interrupt).T /= Null_Task then
1167 Tmp_ID := User_Entry (Interrupt).T;
1168 Tmp_Entry_Index := User_Entry (Interrupt).E;
1170 -- RTS calls should not be made with self being locked.
1172 POP.Unlock (Self_ID);
1174 System.Tasking.Rendezvous.Call_Simple
1175 (Tmp_ID, Tmp_Entry_Index, System.Null_Address);
1177 POP.Write_Lock (Self_ID);
1182 POP.Unlock (Self_ID);
1183 System.Tasking.Initialization.Undefer_Abort (Self_ID);
1185 -- Undefer abort here to allow a window for this task
1186 -- to be aborted at the time of system shutdown.
1189 pragma Assert (Shutdown ("Server_Task---should not get here"));
1192 -------------------------------------
1193 -- Has_Interrupt_Or_Attach_Handler --
1194 -------------------------------------
1196 function Has_Interrupt_Or_Attach_Handler
1197 (Object : access Dynamic_Interrupt_Protection) return Boolean is
1200 end Has_Interrupt_Or_Attach_Handler;
1206 procedure Finalize (Object : in out Static_Interrupt_Protection) is
1208 -- ??? loop to be executed only when we're not doing library level
1209 -- finalization, since in this case all interrupt tasks are gone.
1210 if not Interrupt_Manager'Terminated then
1211 for N in reverse Object.Previous_Handlers'Range loop
1212 Interrupt_Manager.Attach_Handler
1213 (New_Handler => Object.Previous_Handlers (N).Handler,
1214 Interrupt => Object.Previous_Handlers (N).Interrupt,
1215 Static => Object.Previous_Handlers (N).Static,
1216 Restoration => True);
1220 Tasking.Protected_Objects.Entries.Finalize
1221 (Tasking.Protected_Objects.Entries.Protection_Entries (Object));
1224 -------------------------------------
1225 -- Has_Interrupt_Or_Attach_Handler --
1226 -------------------------------------
1228 function Has_Interrupt_Or_Attach_Handler
1229 (Object : access Static_Interrupt_Protection)
1234 end Has_Interrupt_Or_Attach_Handler;
1236 ----------------------
1237 -- Install_Handlers --
1238 ----------------------
1240 procedure Install_Handlers
1241 (Object : access Static_Interrupt_Protection;
1242 New_Handlers : in New_Handler_Array)
1245 for N in New_Handlers'Range loop
1247 -- We need a lock around this ???
1249 Object.Previous_Handlers (N).Interrupt := New_Handlers (N).Interrupt;
1250 Object.Previous_Handlers (N).Static := User_Handler
1251 (New_Handlers (N).Interrupt).Static;
1253 -- We call Exchange_Handler and not directly Interrupt_Manager.
1254 -- Exchange_Handler so we get the Is_Reserved check.
1257 (Old_Handler => Object.Previous_Handlers (N).Handler,
1258 New_Handler => New_Handlers (N).Handler,
1259 Interrupt => New_Handlers (N).Interrupt,
1262 end Install_Handlers;
1264 -- Elaboration code for package System.Interrupts
1267 -- Get Interrupt_Manager's ID so that Abort_Interrupt can be sent.
1269 Interrupt_Manager_ID := To_System (Interrupt_Manager'Identity);
1271 -- Initialize the lock L.
1273 Initialization.Defer_Abort (Self);
1274 POP.Initialize_Lock (L'Access, POP.ATCB_Level);
1275 Initialization.Undefer_Abort (Self);
1277 -- During the elaboration of this package body we want RTS to
1278 -- inherit the interrupt mask from the Environment Task.
1280 -- The Environment Task should have gotten its mask from
1281 -- the enclosing process during the RTS start up. (See
1282 -- in s-inmaop.adb). Pass the Interrupt_Mask of the Environment
1283 -- task to the Interrupt_Manager.
1285 -- Note : At this point we know that all tasks (including
1286 -- RTS internal servers) are masked for non-reserved signals
1287 -- (see s-taprop.adb). Only the Interrupt_Manager will have
1288 -- masks set up differently inheriting the original Environment
1291 Interrupt_Manager.Initialize (IMOP.Environment_Mask);
1292 end System.Interrupts;