------------------------------------------------------------------------------
--                                                                         --
--                GNU ADA RUNTIME LIBRARY (GNARL) COMPONENTS               --
--                                                                         --
--              S Y S T E M . T A S K I N G . A B O R T I O N              --
--                                                                         --
--                                 B o d y                                 --
--                                                                         --
--                            $Revision: 1.14 $                             --
--                                                                         --
--       Copyright (c) 1991,1992,1993,1994, 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.                                      --
--                                                                          --
------------------------------------------------------------------------------

with System.Tasking.Utilities;
--  Used for,  Utilities.All_Tasks_L,
--            Utilities.All_Tasks_List
--            Utilities.Abort_To_Level,
--            Utilities.Abort_Dependents

with System.Task_Primitives; use System.Task_Primitives;

package body System.Tasking.Abortion is

   --------------------------
   -- 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;

   --------------------
   -- Defer_Abortion --
   --------------------

   procedure Defer_Abortion is
      T : Task_ID := Self;

   begin
      T.Deferral_Level := T.Deferral_Level + 1;
   end Defer_Abortion;

   ----------------------
   -- Undefer_Abortion --
   ----------------------

   --  Precondition : Self does not hold any locks!

   --  Undefer_Abortion is called on any abortion completion point (aka.
   --  synchonization 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;

   -----------------
   -- Abort_Tasks --
   -----------------

   --  Called to initiate abortion, however, the actual abortion
   --  is done by abortee by means of Abort_Handler

   procedure Abort_Tasks (Tasks : Task_List) is
      Abortee               : Task_ID;
      Aborter               : Task_ID;
      Activator             : Task_ID;
      TAS_Result            : Boolean;
      Old_Pending_ATC_Level : ATC_Level_Base;

   begin
      Defer_Abortion;

      --  Begin non-abortable section

      Aborter := Self;

      for J in Tasks'Range loop

         Abortee := Tasks (J);

         if Abortee.Stage = Created then
            Utilities.Complete (Abortee);
            Abortee.Stage := Terminated;
            --  Task aborted before activation is safe to complete
            --  Mark This task to be terminated.
         else
            Abortee.Accepting := Not_Accepting;
            Utilities.Complete_on_Sync_Point (Abortee);
            Utilities.Abort_To_Level (Abortee, 0);
            --  Process abortion of child tasks
            Utilities.Abort_Dependents (Abortee);
         end if;

      end loop;

      --  End non-abortable section

      Undefer_Abortion;
   end Abort_Tasks;

end System.Tasking.Abortion;
