home *** CD-ROM | disk | FTP | other *** search
- ------------------------------------------------------------------------------
- -- --
- -- GNU ADA RUNTIME LIBRARY (GNARL) COMPONENTS --
- -- --
- -- S Y S T E M . T A S K I N G . I N I T I A L I Z A T I O N --
- -- --
- -- B o d y --
- -- --
- -- $Revision: 1.1 $ --
- -- --
- -- Copyright (c) 1991,1992,1993,1994,1995 FSU, All Rights Reserved --
- -- --
- -- GNARL is free software; you can redistribute it and/or modify it under --
- -- terms of the GNU Library General Public License as published by the --
- -- Free Software Foundation; either version 2, or (at your option) any --
- -- later version. GNARL is distributed in the hope that it will be use- --
- -- ful, but but WITHOUT ANY WARRANTY; without even the implied warranty of --
- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Gen- --
- -- eral Library Public License for more details. You should have received --
- -- a copy of the GNU Library General Public License along with GNARL; see --
- -- file COPYING.LIB. If not, write to the Free Software Foundation, 675 --
- -- Mass Ave, Cambridge, MA 02139, USA. --
- -- --
- ------------------------------------------------------------------------------
-
- -- This package provides overall initialization of the tasking portion
- -- of the RTS. This package must be elaborated before any tasking
- -- features are used. It also contains initialization for
- -- Ada Task Control Block (ATCB) records.
-
- with System.Task_Primitives; use System.Task_Primitives;
-
- with System.Tasking_Soft_Links;
- -- Used for, Abort_Defer, Abort_Undefer, Get_TSD_Address
- -- These are procedure pointers to non-tasking routines that use
- -- task specific data. In the absence of tasking, these routines
- -- refer to global data. In the presense of tasking, they must be
- -- replaced with pointers to task-specific versions.
-
- with System.Task_Memory;
- -- Used for, Task_Memory.Low_Level_New,
- -- Task_Memory.Unsafe_Low_Level_New,
- -- Task_Memory.Low_Level_Free
-
- with System.Compiler_Options;
- -- Used for, Main_Priority
-
- with System.Task_Specific_Data;
- -- Used for, Create_TSD, Destroy_TSD
- -- This package provides initialization routines for task specific data.
- -- The GNARL must call these to be sure that all non-tasking
- -- Ada constructs will work.
-
- pragma Elaborate_All (System.Task_Primitives);
- pragma Elaborate_All (System.Task_Memory);
-
- pragma Elaborate_All (System.Tasking_Soft_Links);
- -- This must be elaborated first, to prevent its initialization of
- -- the global procedure pointers from overwriting the pointers installed
- -- by Stages.
-
- with Unchecked_Deallocation;
-
- package body System.Tasking.Initialization is
-
- Global_Task_Lock : Lock;
- -- This is a global lock; it is used to execute in mutual exclusion
- -- from all other tasks. It is only used by Task_Lock and
- -- Task_Unlock.
-
- -----------------------------------------------------------------
- -- Tasking versions of services needed by non-tasking programs --
- -----------------------------------------------------------------
-
- function Get_TSD_Address (Dummy : Boolean) return Address;
- -- This procedure returns the task-specific data pointer installed at
- -- task creation time by the GNARL on behalf of the compiler. A pointer
- -- to this routine replaces the default pointer installed for the
- -- non-tasking case.
- -- The dummy parameter avoids a bug in GNAT.
-
- procedure Task_Lock;
- -- Locks out other tasks. Preceding a section of code by Task_Lock and
- -- following it by Task_Unlock creates a critical region. This is used
- -- for ensuring that a region of non-tasking code (such as code used to
- -- allocate memory) is tasking safe. Note that it is valid for calls to
- -- Task_Lock/Task_Unlock to be nested, and this must work properly, i.e.
- -- only the corresponding outer level Task_Unlock will actually unlock.
-
- procedure Task_Unlock;
- -- Releases lock previously set by call to Task_Lock. In the nested case,
- -- all nested locks must be released before other tasks competing for the
- -- tasking lock are released.
-
- ----------------------------
- -- Tasking Initialization --
- ----------------------------
-
- procedure Init_RTS (Main_Task_Priority : System.Priority);
- -- This procedure initializes the GNARL. This includes creating
- -- data structures to make the initial thread into the environment
- -- task, setting up handlers for ATC and errors, and
- -- installing tasking versions of certain operations used by the
- -- compiler. Init_RTS is called during elaboration.
-
- -------------------
- -- Abort_Handler --
- -------------------
-
- procedure Abort_Handler
- (Context : Task_Primitives.Pre_Call_State)
- is
- T : Task_ID := Self;
-
- begin
- if T.Deferral_Level = 0
- and then T.Pending_ATC_Level < T.ATC_Nesting_Level
- then
-
- -- ??? This is implementation dependent. Some implementations
- -- might not allow an exception to be propagated out of a
- -- handler, and others might leave the signal or interrupt
- -- that invoked this handler masked after the exceptional
- -- return to the application code.
- -- GNAT exceptions are originally implemented using
- -- setjmp()/longjmp(). On most UNIX systems, this will
- -- allow transfer out of a signal handler, which is
- -- usually the only mechanism available for implementing
- -- asynchronous handlers of this kind. However, some
- -- systems do not restore the signal mask, leaving the
- -- abortion signal masked.
- -- Possible solutions:
- --
- -- 1. Change the PC saved in the system-dependent Context
- -- parameter to point to code that raises the exception.
- -- Normal return from this handler will then raise
- -- the exception after the mask and other system state has
- -- been restored.
- -- 2. Use siglongjmp()/sigsetjmp() to implement exceptions.
- -- 3. Unmask the signal in the Abortion exception handler
- -- (in the RTS).
-
- raise Standard'Abort_Signal;
-
- end if;
- end Abort_Handler;
-
- --------------------------
- -- Change_Base_Priority --
- --------------------------
-
- procedure Change_Base_Priority (T : Task_ID) is
-
- begin
- -- check for ceiling violations ???
- T.Pending_Priority_Change := False;
- T.Base_Priority := T.New_Base_Priority;
- T.Current_Priority := T.Base_Priority;
- Set_Priority (T.LL_TCB'Access, T. Current_Priority);
- end Change_Base_Priority;
-
- ----------------------
- -- Decrement_Master --
- ----------------------
-
- function Decrement_Master (M : Master_ID) return Master_ID is
- begin
- return M - 1;
- end Decrement_Master;
-
- --------------------
- -- Defer_Abortion --
- --------------------
-
- procedure Defer_Abortion is
- T : Task_ID := Self;
-
- begin
- T.Deferral_Level := T.Deferral_Level + 1;
- end Defer_Abortion;
-
- ---------------
- -- Free_ATCB --
- ---------------
-
- procedure Free_ATCB (T : in out Task_ID) is
- procedure Free is new Unchecked_Deallocation (
- Ada_Task_Control_Block, Task_ID);
- Error : Boolean;
- begin
- Finalize_Lock (T.L);
- Finalize_Cond (T.Cond);
- Free (T);
- end Free_ATCB;
-
- ---------------------
- -- Get_TSD_Address --
- ---------------------
-
- function Get_TSD_Address (Dummy : Boolean) return Address is
- T : Task_ID := Self;
- begin
- return T.Compiler_Data;
- end Get_TSD_Address;
-
- ----------------------
- -- Increment_Master --
- ----------------------
-
- function Increment_Master (M : Master_ID) return Master_ID is
- begin
- return M + 1;
- end Increment_Master;
-
- ---------------------
- -- Initialize_ATCB --
- ---------------------
-
- procedure Initialize_ATCB
- (T : Task_ID;
- Init : ATCB_Init)
- is
- Error : Boolean;
- begin
- -- Initialize all fields of the TCB
-
- Initialize_Lock (System.Priority'Last, T.L);
- Initialize_Cond (T.Cond);
- T.Activation_Count := 0;
- T.Awake_Count := 1; -- Counting this task.
- T.Awaited_Dependent_Count := 0;
- T.Terminating_Dependent_Count := 0;
- T.Pending_Action := False;
- T.Pending_ATC_Level := ATC_Level_Infinity;
- T.ATC_Nesting_Level := 1; -- 1 deep; 0 = abnormal.
- T.Deferral_Level := 1; -- Start out deferred.
- T.Stage := Created;
- T.Global_Task_Lock_Nesting := 0;
- T.Exception_To_Raise := Compiler_Exceptions.Null_Exception;
- T.Accepting := Not_Accepting;
- T.Aborting := False;
- T.Call := null;
- T.Elaborated := Init.Elaborated;
- T.Parent := Init.Parent;
- T.Task_Entry_Point := Init.Task_Entry_Point;
- T.Task_Arg := Init.Task_Arg;
- T.Stack_Size := Init.Stack_Size;
- T.Current_Priority := Init.Priority;
- T.Base_Priority := Init.Priority;
- T.Pending_Priority_Change := False;
- T.Activator := Init.Activator;
- T.Master_of_Task := Init.Master_of_Task;
- T.Master_Within := Increment_Master (Init.Master_of_Task);
- T.Terminate_Alternative := false;
-
- for J in 1 .. T.Entry_Num loop
- T.Entry_Queues (J).Head := null;
- T.Entry_Queues (J).Tail := null;
- end loop;
-
- for L in T.Entry_Calls'Range loop
- T.Entry_Calls (L).Next := null;
- T.Entry_Calls (L).Self := T;
- T.Entry_Calls (L).Level := L;
- end loop;
-
- -- Link the task into the list of all tasks.
-
- if T.Parent /= null then
- Defer_Abortion;
- Write_Lock (All_Tasks_L, Error);
- end if;
-
- T.All_Tasks_Link := All_Tasks_List;
- All_Tasks_List := T;
-
- if T.Parent /= null then
- Unlock (All_Tasks_L);
- Undefer_Abortion;
- end if;
- end Initialize_ATCB;
-
- -----------------
- -- Init_Master --
- -----------------
-
- procedure Init_Master (M : out Master_ID) is
- begin
- M := 0;
- end Init_Master;
-
- --------------
- -- Init_RTS --
- --------------
-
- procedure Init_RTS (Main_Task_Priority : System.Priority) is
- T : Task_ID;
- Init : ATCB_Init;
-
- begin
- All_Tasks_List := null;
- Init.Entry_Num := 0;
- Init.Parent := null;
-
- Init.Task_Entry_Point := null;
-
- Init.Stack_Size := 0;
- Init.Activator := null;
- Init_Master (Init.Master_of_Task);
- Init.Elaborated := null;
- if Main_Task_Priority = Unspecified_Priority then
- Init.Priority := Default_Priority;
- else
- Init.Priority := Main_Task_Priority;
- end if;
-
- T := Unsafe_New_ATCB (Init);
-
- T.Compiler_Data := Task_Specific_Data.Create_TSD;
- -- This needs to be done as early as possible in the creation
- -- of a task, since the operation of Ada code within the task may
- -- depend on task specific data.
-
- Initialize_LL_Tasks (T.LL_TCB'Access);
- Initialize_ATCB (T, Init);
-
- T.Stage := Active;
-
- -- The allocation of the initial task ATCB is different from
- -- that of subsequent ATCBs, which are allocated with ATCB.New_ATCB.
- -- New_ATCB performs all of the functions of Unsafe_New_ATCB
- -- and Initialize_ATCB. However, it uses GNULLI operations, which
- -- should not be called until after Initialize_LL_Tasks. Since
- -- Initialize_LL_Tasks needs the initial ATCB, New_ATCB was broken
- -- down into two parts, the first of which allocates the ATCB without
- -- calling any GNULLI operations.
-
- Set_Own_Priority (T.Current_Priority);
-
- Initialize_Lock (Priority'Last, All_Tasks_L);
- -- Initialize the lock used to synchronize chain of all ATCBs.
-
- Initialize_Lock (Priority'Last, Global_Task_Lock);
- -- Initialize the lock used to implement mutual exclusion between
- -- all tasks.
-
- -- This is not according the the GNULLI, which specifies
- -- access procedure (Context: Pre_Call_State) for the handler.
- -- This may be a mistake in the interface. ???
-
- Install_Abort_Handler (Abort_Handler'Access);
-
- -- Install handlers for asynchronous error signals.
-
- -- This is not according the the GNULLI, which specifies
- -- access procedure(...) for the handler.
- -- This may be a mistake in the interface. ???
-
- Install_Error_Handler (Compiler_Exceptions.Notify_Exception'Address);
-
- -- Set up the soft links to tasking services used in the absence of
- -- tasking. These replace tasking-free defaults.
-
- System.Tasking_Soft_Links.Abort_Defer :=
- Defer_Abortion'Access;
- System.Tasking_Soft_Links.Abort_Undefer :=
- Undefer_Abortion'Access;
- System.Tasking_Soft_Links.Get_TSD_Address :=
- Get_TSD_Address'Access;
- System.Tasking_Soft_Links.Lock_Task :=
- Task_Lock'Access;
- System.Tasking_Soft_Links.Unlock_Task :=
- Task_Unlock'Access;
-
- -- Abortion is deferred in a new ATCB, so we need to undefer abortion
- -- at this stage to make the environment task abortable.
-
- Undefer_Abortion;
-
- end Init_RTS;
-
- --------------
- -- New_ATCB --
- --------------
-
- function New_ATCB
- (Init : ATCB_Init)
- return Task_ID
- is
- T : Task_ID;
- Error : Boolean;
- begin
- T := new Ada_Task_Control_Block (Init.Entry_Num);
- Initialize_ATCB (T, Init);
- return T;
- end New_ATCB;
-
- --------------------------------
- -- Remove_From_All_Tasks_List --
- --------------------------------
-
- procedure Remove_From_All_Tasks_List (
- Source : Task_ID;
- Result : out Boolean) is
-
- C : Task_ID;
- P : Task_ID;
- Previous : Task_ID;
- Error : Boolean;
- begin
-
- Write_Lock (All_Tasks_L, Error);
-
- Result := False;
-
- Previous := Null_Task;
- C := All_Tasks_List;
-
- while C /= Null_Task loop
- if C = Source then
- Result := True;
-
- if Previous = Null_Task then
- All_Tasks_List :=
- All_Tasks_List.All_Tasks_Link;
- else
- Previous.All_Tasks_Link := C.All_Tasks_Link;
- end if;
-
- exit;
-
- end if;
-
- Previous := C;
- C := C.All_Tasks_Link;
-
- end loop;
-
- Unlock (All_Tasks_L);
-
- end Remove_From_All_Tasks_List;
-
- -----------------------------
- -- Runtime_Assert_Shutdown --
- -----------------------------
-
- function Runtime_Assert_Shutdown (Msg : in String) return boolean is
- begin
- LL_Assert (false, Msg);
- -- This call should never return
- return false;
- end Runtime_Assert_Shutdown;
-
- ---------------
- -- Task_Lock --
- ---------------
-
- procedure Task_Lock is
- T : Task_ID := Self;
- Error : Boolean;
- begin
- T.Global_Task_Lock_Nesting := T.Global_Task_Lock_Nesting + 1;
- if T.Global_Task_Lock_Nesting = 1 then
- Write_Lock (Global_Task_Lock, Error);
- end if;
- end Task_Lock;
-
- -----------------
- -- Task_Unlock --
- -----------------
-
- procedure Task_Unlock is
- T : Task_ID := Self;
- begin
- pragma Assert (
- T.Global_Task_Lock_Nesting > 0 or else
- Runtime_Assert_Shutdown (
- "Unlock_Task_T: Improper lock nesting"));
-
- T.Global_Task_Lock_Nesting := T.Global_Task_Lock_Nesting - 1;
- if T.Global_Task_Lock_Nesting = 0 then
- Unlock (Global_Task_Lock);
- end if;
- end Task_Unlock;
-
- ----------------------
- -- Undefer_Abortion --
- ----------------------
-
- -- Precondition : Self does not hold any locks!
-
- -- Undefer_Abortion is called on any abortion completion point (aka.
- -- synchronization point). It performs the following actions if they
- -- are pending: (1) change the base priority, (2) abort the task.
- -- The priority change has to occur before abortion. Otherwise, it would
- -- take effect no earlier than the next abortion completion point.
- -- This version of Undefer_Abortion redefers abortion if abortion is
- -- in progress. There has been some discussion of having
- -- the raise statement defer abortion to prevent abortion of
- -- handlers performing required completion. This would make
- -- the explicit deferral unnecessary. ???
-
- procedure Undefer_Abortion is
- T : Task_ID := Self;
- Error : Boolean;
-
- begin
- T.Deferral_Level := T.Deferral_Level - 1;
-
- if T.Deferral_Level = ATC_Level'First and then T.Pending_Action then
- Write_Lock (T.L, Error);
- T.Pending_Action := False;
-
- if T.Pending_Priority_Change then
- Change_Base_Priority (T);
- end if;
-
- Unlock (T.L);
-
- if T.Pending_ATC_Level < T.ATC_Nesting_Level then
- raise Standard'Abort_Signal;
- end if;
- end if;
-
- end Undefer_Abortion;
-
- ---------------------
- -- Unsafe_New_ATCB --
- ---------------------
-
- function Unsafe_New_ATCB
- (Init : ATCB_Init)
- return Task_ID
- is
- begin
- return new Ada_Task_Control_Block (Init.Entry_Num);
- end Unsafe_New_ATCB;
-
- -----------------------------------
- -- Tasking System Initialization --
- -----------------------------------
-
- begin
- Init_RTS (Compiler_Options.Main_Priority);
- end System.Tasking.Initialization;
-