home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / adav313.zip / gnat-3_13p-os2-bin-20010916.zip / emx / gnatlib / s-tasuti.adb < prev    next >
Text File  |  2000-07-19  |  19KB  |  571 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS               --
  4. --                                                                          --
  5. --              S Y S T E M . T A S K I N G . U T I L I T I E S             --
  6. --                                                                          --
  7. --                                  B o d y                                 --
  8. --                                                                          --
  9. --                             $Revision: 1.66 $
  10. --                                                                          --
  11. --            Copyright (C) 1991-1999, Florida State University             --
  12. --                                                                          --
  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.                                                      --
  23. --                                                                          --
  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.                                      --
  30. --                                                                          --
  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).                                  --
  34. --                                                                          --
  35. ------------------------------------------------------------------------------
  36.  
  37. --  This package provides RTS Internal Declarations.
  38. --  These declarations are not part of the GNARLI
  39.  
  40. pragma Polling (Off);
  41. --  Turn off polling, we do not want ATC polling to take place during
  42. --  tasking operations. It causes infinite loops and other problems.
  43.  
  44. with System.Tasking.Debug;
  45. --  used for Known_Tasks
  46.  
  47. with System.Task_Primitives.Operations;
  48. --  used for Write_Lock
  49. --           Set_Priority
  50. --           Wakeup
  51. --           Unlock
  52. --           Sleep
  53. --           Abort_Task
  54. --           Lock/Unlock_All_Tasks_List
  55.  
  56. with System.Tasking.Initialization;
  57. --  Used for Defer_Abort
  58. --           Undefer_Abort
  59. --           Locked_Abort_To_Level
  60.  
  61. with System.Tasking.Queuing;
  62. --  used for Dequeue_Call
  63. --           Dequeue_Head
  64.  
  65. with System.Tasking.Debug;
  66. --  used for Trace
  67.  
  68. with Unchecked_Conversion;
  69.  
  70. package body System.Tasking.Utilities is
  71.  
  72.    package STPO renames System.Task_Primitives.Operations;
  73.  
  74.    use System.Tasking.Debug;
  75.    use System.Task_Primitives;
  76.    use System.Task_Primitives.Operations;
  77.  
  78.    procedure Locked_Abort_To_Level
  79.      (Self_Id : Task_ID;
  80.       T       : Task_ID;
  81.       L       : ATC_Level)
  82.    renames
  83.      Initialization.Locked_Abort_To_Level;
  84.  
  85.    procedure Defer_Abort (Self_Id : Task_ID) renames
  86.      System.Tasking.Initialization.Defer_Abort;
  87.  
  88.    procedure Defer_Abort_Nestable (Self_Id : Task_ID) renames
  89.      System.Tasking.Initialization.Defer_Abort_Nestable;
  90.  
  91.    procedure Undefer_Abort (Self_Id : Task_ID) renames
  92.      System.Tasking.Initialization.Undefer_Abort;
  93.  
  94.    procedure Undefer_Abort_Nestable (Self_Id : Task_ID) renames
  95.      System.Tasking.Initialization.Undefer_Abort_Nestable;
  96.  
  97.    procedure Wakeup_Entry_Caller
  98.      (Self_Id    : Task_ID;
  99.       Entry_Call : Entry_Call_Link;
  100.       New_State  : Entry_Call_State)
  101.    renames
  102.      Initialization.Wakeup_Entry_Caller;
  103.  
  104.    ----------------------
  105.    -- Make_Independent --
  106.    ----------------------
  107.  
  108.    --  Move the current task to the outermost level (level 2) of the master
  109.    --  hierarchy of the environment task. That is one level further out
  110.    --  than normal tasks defined in library-level packages (level 3). The
  111.    --  environment task will wait for level 3 tasks to terminate normally,
  112.    --  then it will abort all the level 2 tasks. See Finalize_Global_Tasks
  113.    --  procedure for more information.
  114.  
  115.    --  This is a dangerous operation, and should only be used on nested tasks
  116.    --  or tasks that depend on any objects that might be finalized earlier than
  117.    --  the termination of the environment task. It is for internal use by the
  118.    --  GNARL, to prevent such internal server tasks from preventing a partition
  119.    --  from terminating.
  120.  
  121.    --  Also note that the run time assumes that the parent of an independent
  122.    --  task is the environment task. If this is not the case, Make_Independent
  123.    --  will change the task's parent. This assumption is particularly
  124.    --  important for master level completion and for the computation of
  125.    --  Independent_Task_Count.
  126.  
  127.    --  See procedures Init_RTS and Finalize_Global_Tasks for related code.
  128.  
  129.    procedure Make_Independent is
  130.       Self_Id               : constant Task_ID := STPO.Self;
  131.       Environment_Task      : constant Task_ID := STPO.Environment_Task;
  132.       Parent                : constant Task_ID := Self_Id.Common.Parent;
  133.       Parent_Needs_Updating : Boolean := False;
  134.  
  135.    begin
  136.       if Self_Id.Known_Tasks_Index /= -1 then
  137.          Known_Tasks (Self_Id.Known_Tasks_Index) := null;
  138.       end if;
  139.  
  140.       Defer_Abort (Self_Id);
  141.       Write_Lock (Environment_Task);
  142.       Write_Lock (Self_Id);
  143.  
  144.       pragma Assert (Parent = Environment_Task
  145.         or else Self_Id.Master_of_Task = Library_Task_Level);
  146.  
  147.       Self_Id.Master_of_Task := Independent_Task_Level;
  148.  
  149.       --  The run time assumes that the parent of an independent task is the
  150.       --  environment task.
  151.  
  152.       if Parent /= Environment_Task then
  153.  
  154.          --  We can not lock three tasks at the same time, so defer the
  155.          --  operations on the parent.
  156.  
  157.          Parent_Needs_Updating := True;
  158.          Self_Id.Common.Parent := Environment_Task;
  159.       end if;
  160.  
  161.       --  Update Independent_Task_Count that is needed for the GLADE
  162.       --  termination rule. See also pending update in
  163.       --  System.Tasking.Stages.Check_Independent
  164.  
  165.       Independent_Task_Count := Independent_Task_Count + 1;
  166.  
  167.       Unlock (Self_Id);
  168.  
  169.       --  Changing the parent after creation is not trivial. Do not forget
  170.       --  to update the old parent counts, and the new parent (i.e. the
  171.       --  Environment_Task) counts.
  172.  
  173.       if Parent_Needs_Updating then
  174.          Write_Lock (Parent);
  175.          Parent.Awake_Count := Parent.Awake_Count - 1;
  176.          Parent.Alive_Count := Parent.Alive_Count - 1;
  177.          Environment_Task.Awake_Count := Environment_Task.Awake_Count + 1;
  178.          Environment_Task.Alive_Count := Environment_Task.Alive_Count + 1;
  179.          Unlock (Parent);
  180.       end if;
  181.  
  182.       Unlock (Environment_Task);
  183.       Undefer_Abort (Self_Id);
  184.    end Make_Independent;
  185.  
  186.    ------------------------
  187.    -- Exit_One_ATC_Level --
  188.    ------------------------
  189.  
  190.    --  Call only with abort deferred and holding lock of Self_Id.
  191.    --  This is a bit of common code for all entry calls.
  192.    --  The effect is to exit one level of ATC nesting.
  193.  
  194.    --  If we have reached the desired ATC nesting level, reset the
  195.    --  requested level to effective infinity, to allow further calls.
  196.    --  In any case, reset Self_Id.Aborting, to allow re-raising of
  197.    --  Abort_Signal.
  198.  
  199.    procedure Exit_One_ATC_Level (Self_ID : Task_ID) is
  200.    begin
  201.       Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level - 1;
  202.  
  203.       pragma Debug
  204.         (Debug.Trace (Self_ID, "EOAL: exited to ATC level: " &
  205.          ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
  206.  
  207.       pragma Assert (Self_ID.ATC_Nesting_Level >= 1);
  208.  
  209.       if Self_ID.Pending_ATC_Level < ATC_Level_Infinity then
  210.          if Self_ID.Pending_ATC_Level = Self_ID.ATC_Nesting_Level then
  211.             Self_ID.Pending_ATC_Level := ATC_Level_Infinity;
  212.             Self_ID.Aborting := False;
  213.          else
  214.             --  Force the next Undefer_Abort to re-raise Abort_Signal
  215.  
  216.             pragma Assert
  217.              (Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level);
  218.  
  219.             if Self_ID.Aborting then
  220.                Self_ID.ATC_Hack := True;
  221.                Self_ID.Pending_Action := True;
  222.             end if;
  223.          end if;
  224.       end if;
  225.    end Exit_One_ATC_Level;
  226.  
  227.    ----------------
  228.    -- Abort_Task --
  229.    ----------------
  230.  
  231.    --  Similar to Locked_Abort_To_Level (Self_ID, T, 0), but:
  232.    --    (1) caller should be holding no locks
  233.    --    (2) may be called for tasks that have not yet been activated
  234.    --    (3) always aborts whole task
  235.  
  236.    procedure Abort_One_Task
  237.      (Self_ID : Task_ID;
  238.       T       : Task_ID)
  239.    is
  240.    begin
  241.       Write_Lock (T);
  242.  
  243.       if T.Common.State = Unactivated then
  244.          T.Common.Activator := null;
  245.          T.Common.State := Terminated;
  246.          T.Callable := False;
  247.          Cancel_Queued_Entry_Calls (T);
  248.  
  249.       elsif T.Common.State /= Terminated then
  250.          Locked_Abort_To_Level (Self_ID, T, 0);
  251.       end if;
  252.  
  253.       Unlock (T);
  254.    end Abort_One_Task;
  255.  
  256.    -----------------
  257.    -- Abort_Tasks --
  258.    -----------------
  259.  
  260.    --  Compiler interface only: Do not call from within the RTS,
  261.  
  262.    --  except in the implementation of Ada.Task_Identification.
  263.    --  This must be called to implement the abort statement.
  264.    --  Much of the actual work of the abort is done by the abortee,
  265.    --  via the Abort_Handler signal handler, and propagation of the
  266.    --  Abort_Signal special exception.
  267.  
  268.    procedure Abort_Tasks (Tasks : Task_List) is
  269.       Self_Id : constant Task_ID := STPO.Self;
  270.       C       : Task_ID;
  271.       P       : Task_ID;
  272.  
  273.    begin
  274.       --  ????
  275.       --  Since this is a "potentially blocking operation", we should
  276.       --  add a separate check here that we are not inside a protected
  277.       --  operation.
  278.  
  279.       Defer_Abort_Nestable (Self_Id);
  280.  
  281.       --  ?????
  282.       --  Really should not be nested deferral here.
  283.       --  Patch for code generation error that defers abort before
  284.       --  evaluating parameters of an entry call (at least, timed entry
  285.       --  calls), and so may propagate an exception that causes abort
  286.       --  to remain undeferred indefinitely.  See C97404B.  When all
  287.       --  such bugs are fixed, this patch can be removed.
  288.  
  289.       for J in Tasks'Range loop
  290.          C := Tasks (J);
  291.          Abort_One_Task (Self_Id, C);
  292.       end loop;
  293.  
  294.       Lock_All_Tasks_List;
  295.       C := All_Tasks_List;
  296.  
  297.       while C /= null loop
  298.          if C.Pending_ATC_Level > 0 then
  299.             P := C.Common.Parent;
  300.  
  301.             while P /= null loop
  302.                if P.Pending_ATC_Level = 0 then
  303.                   Abort_One_Task (Self_Id, C);
  304.                   exit;
  305.                end if;
  306.  
  307.                P := P.Common.Parent;
  308.             end loop;
  309.          end if;
  310.  
  311.          C := C.Common.All_Tasks_Link;
  312.       end loop;
  313.  
  314.       Unlock_All_Tasks_List;
  315.       Undefer_Abort_Nestable (Self_Id);
  316.    end Abort_Tasks;
  317.  
  318.    ------------------
  319.    -- Make_Passive --
  320.    ------------------
  321.  
  322.    --  Update counts to indicate current task is either terminated
  323.    --  or accepting on a terminate alternative. Call holding no locks.
  324.  
  325.    procedure Make_Passive
  326.      (Self_ID        : Task_ID;
  327.       Task_Completed : Boolean)
  328.    is
  329.       C : Task_ID := Self_ID;
  330.       P : Task_ID := C.Common.Parent;
  331.  
  332.       Master_Completion_Phase : Integer;
  333.  
  334.    begin
  335.       if P /= null then
  336.          Write_Lock (P);
  337.       end if;
  338.  
  339.       Write_Lock (C);
  340.  
  341.       if Task_Completed then
  342.          Self_ID.Common.State := Terminated;
  343.  
  344.          if Self_ID.Awake_Count = 0 then
  345.  
  346.             --  We are completing via a terminate alternative.
  347.             --  Our parent should wait in Phase 2 of Complete_Master.
  348.  
  349.             Master_Completion_Phase := 2;
  350.  
  351.             pragma Assert (Task_Completed);
  352.             pragma Assert (Self_ID.Terminate_Alternative);
  353.             pragma Assert (Self_ID.Alive_Count = 1);
  354.  
  355.          else
  356.             --  We are NOT on a terminate alternative.
  357.             --  Our parent should wait in Phase 1 of Complete_Master.
  358.  
  359.             Master_Completion_Phase := 1;
  360.             pragma Assert (Self_ID.Awake_Count = 1);
  361.          end if;
  362.  
  363.       --  We are accepting with a terminate alternative.
  364.  
  365.       else
  366.          if Self_ID.Open_Accepts = null then
  367.  
  368.             --  Somebody started a rendezvous while we had our lock open.
  369.             --  Skip the terminate alternative.
  370.  
  371.             Unlock (C);
  372.  
  373.             if P /= null then
  374.                Unlock (P);
  375.             end if;
  376.  
  377.             return;
  378.          end if;
  379.  
  380.          Self_ID.Terminate_Alternative := True;
  381.          Master_Completion_Phase := 0;
  382.  
  383.          pragma Assert (Self_ID.Terminate_Alternative);
  384.          pragma Assert (Self_ID.Awake_Count >= 1);
  385.       end if;
  386.  
  387.       if Master_Completion_Phase = 2 then
  388.  
  389.          --  Since our Awake_Count is zero but our Alive_Count
  390.          --  is nonzero, we have been accepting with a terminate
  391.          --  alternative, and we now have been told to terminate
  392.          --  by a completed master (in some ancestor task) that
  393.          --  is waiting (with zero Awake_Count) in Phase 2 of
  394.          --  Complete_Master.
  395.  
  396.          pragma Debug
  397.            (Debug.Trace (Self_ID, "Make_Passive: Phase 2", 'M'));
  398.  
  399.          pragma Assert (P /= null);
  400.  
  401.          C.Alive_Count := C.Alive_Count - 1;
  402.  
  403.          if C.Alive_Count > 0 then
  404.             Unlock (C);
  405.             Unlock (P);
  406.             return;
  407.          end if;
  408.  
  409.          --  C's count just went to zero, indicating that
  410.          --  all of C's dependents are terminated.
  411.          --  C has a parent, P.
  412.  
  413.          loop
  414.             --  C's count just went to zero, indicating that all of C's
  415.             --  dependents are terminated. C has a parent, P. Notify P that
  416.             --  C and its dependents have all terminated.
  417.  
  418.             P.Alive_Count := P.Alive_Count - 1;
  419.             exit when P.Alive_Count > 0;
  420.             Unlock (C);
  421.             Unlock (P);
  422.             C := P;
  423.             P := C.Common.Parent;
  424.  
  425.             --  Environment task cannot have terminated yet
  426.  
  427.             pragma Assert (P /= null);
  428.  
  429.             Write_Lock (P);
  430.             Write_Lock (C);
  431.          end loop;
  432.  
  433.          pragma Assert (P.Awake_Count /= 0);
  434.  
  435.          if P.Common.State = Master_Phase_2_Sleep
  436.            and then C.Master_of_Task = P.Master_Within
  437.  
  438.          then
  439.             pragma Assert (P.Common.Wait_Count > 0);
  440.             P.Common.Wait_Count := P.Common.Wait_Count - 1;
  441.  
  442.             if P.Common.Wait_Count = 0 then
  443.                Wakeup (P, Master_Phase_2_Sleep);
  444.             end if;
  445.          end if;
  446.  
  447.          Unlock (C);
  448.          Unlock (P);
  449.          return;
  450.       end if;
  451.  
  452.       --  We are terminating in Phase 1 or Complete_Master,
  453.       --  or are accepting on a terminate alternative.
  454.  
  455.       C.Awake_Count := C.Awake_Count - 1;
  456.  
  457.       if Task_Completed then
  458.          pragma Assert (Self_ID.Awake_Count = 0);
  459.          C.Alive_Count := C.Alive_Count - 1;
  460.       end if;
  461.  
  462.       if C.Awake_Count > 0 or else P = null then
  463.          Unlock (C);
  464.  
  465.          if P /= null then
  466.             Unlock (P);
  467.          end if;
  468.  
  469.          return;
  470.       end if;
  471.  
  472.       --  C's count just went to zero, indicating that all of C's
  473.       --  dependents are terminated or accepting with terminate alt.
  474.       --  C has a parent, P.
  475.  
  476.       loop
  477.          --  Notify P that C has gone passive.
  478.  
  479.          P.Awake_Count := P.Awake_Count - 1;
  480.  
  481.          if Task_Completed and then C.Alive_Count = 0 then
  482.             P.Alive_Count := P.Alive_Count - 1;
  483.          end if;
  484.  
  485.          exit when P.Awake_Count > 0;
  486.          Unlock (C);
  487.          Unlock (P);
  488.          C := P;
  489.          P := C.Common.Parent;
  490.  
  491.          if P = null then
  492.             return;
  493.          end if;
  494.  
  495.          Write_Lock (P);
  496.          Write_Lock (C);
  497.       end loop;
  498.  
  499.       --  P has non-passive dependents.
  500.  
  501.       if P.Common.State = Master_Completion_Sleep and then
  502.          C.Master_of_Task = P.Master_Within
  503.       then
  504.          pragma Debug
  505.            (Debug.Trace
  506.             (Self_ID, "Make_Passive: Phase 1, parent waiting", 'M'));
  507.  
  508.          --  If parent is in Master_Completion_Sleep, it
  509.          --  cannot be on a terminate alternative, hence
  510.          --  it cannot have Awake_Count of zero.
  511.  
  512.          pragma Assert (P.Common.Wait_Count > 0);
  513.          P.Common.Wait_Count := P.Common.Wait_Count - 1;
  514.  
  515.          if P.Common.Wait_Count = 0 then
  516.             Wakeup (P, Master_Completion_Sleep);
  517.          end if;
  518.  
  519.       else
  520.          pragma Debug
  521.            (Debug.Trace
  522.              (Self_ID, "Make_Passive: Phase 1, parent awake", 'M'));
  523.          null;
  524.       end if;
  525.  
  526.       Unlock (C);
  527.       Unlock (P);
  528.    end Make_Passive;
  529.  
  530.    -------------------------------
  531.    -- Cancel_Queued_Entry_Calls --
  532.    -------------------------------
  533.  
  534.    --  Cancel any entry calls queued on target task. Call this only while
  535.    --  holding T locked, and nothing more. This should only be called by T,
  536.    --  unless T is a terminated previously unactivated task.
  537.  
  538.    procedure Cancel_Queued_Entry_Calls (T : Task_ID) is
  539.       Next_Entry_Call : Entry_Call_Link;
  540.       Entry_Call      : Entry_Call_Link;
  541.       Caller          : Task_ID;
  542.       Level           : Integer;
  543.       Self_Id         : constant Task_ID := STPO.Self;
  544.  
  545.    begin
  546.       pragma Assert (T = Self or else T.Common.State = Terminated);
  547.  
  548.       for J in 1 .. T.Entry_Num loop
  549.          Queuing.Dequeue_Head (T.Entry_Queues (J), Entry_Call);
  550.  
  551.          while Entry_Call /= null loop
  552.  
  553.             --  Leave Entry_Call.Done = False, since this is cancelled
  554.  
  555.             Caller := Entry_Call.Self;
  556.             Entry_Call.Exception_To_Raise := Tasking_Error'Identity;
  557.             Queuing.Dequeue_Head (T.Entry_Queues (J), Next_Entry_Call);
  558.             Level := Entry_Call.Level - 1;
  559.             Unlock (T);
  560.             Write_Lock (Entry_Call.Self);
  561.             Wakeup_Entry_Caller (Self_Id, Entry_Call, Cancelled);
  562.             Unlock (Entry_Call.Self);
  563.             Write_Lock (T);
  564.             Entry_Call.State := Done;
  565.             Entry_Call := Next_Entry_Call;
  566.          end loop;
  567.       end loop;
  568.    end Cancel_Queued_Entry_Calls;
  569.  
  570. end System.Tasking.Utilities;
  571.