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-tpobop.adb < prev    next >
Text File  |  2000-07-19  |  34KB  |  980 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 . P R O T E C T E D _ O B J E C T S .     --
  6. --                            O P E R A T I O N S                           --
  7. --                                                                          --
  8. --                                  B o d y                                 --
  9. --                                                                          --
  10. --                             $Revision: 1.11 $
  11. --                                                                          --
  12. --            Copyright (C) 1991-1999, Florida State University             --
  13. --                                                                          --
  14. -- GNARL is free software; you can  redistribute it  and/or modify it under --
  15. -- terms of the  GNU General Public License as published  by the Free Soft- --
  16. -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
  17. -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
  18. -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  19. -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  20. -- for  more details.  You should have  received  a copy of the GNU General --
  21. -- Public License  distributed with GNARL; see file COPYING.  If not, write --
  22. -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
  23. -- MA 02111-1307, USA.                                                      --
  24. --                                                                          --
  25. -- As a special exception,  if other files  instantiate  generics from this --
  26. -- unit, or you link  this unit with other files  to produce an executable, --
  27. -- this  unit  does not  by itself cause  the resulting  executable  to  be --
  28. -- covered  by the  GNU  General  Public  License.  This exception does not --
  29. -- however invalidate  any other reasons why  the executable file  might be --
  30. -- covered by the  GNU Public License.                                      --
  31. --                                                                          --
  32. -- GNARL was developed by the GNARL team at Florida State University. It is --
  33. -- now maintained by Ada Core Technologies Inc. in cooperation with Florida --
  34. -- State University (http://www.gnat.com).                                  --
  35. --                                                                          --
  36. ------------------------------------------------------------------------------
  37.  
  38. --  This package contains all the extended primitives related to
  39. --  Protected_Objects with entries.
  40. --  The handling of protected objects with no entries is done in
  41. --  System.Tasking.Protected_Objects, the simple routines for protected
  42. --  objects with entries in System.Tasking.Protected_Objects.Entries.
  43. --  The split between Entries and Operations is needed to break circular
  44. --  dependencies inside the run time.
  45.  
  46. --  This package contains all primitives related to Protected_Objects.
  47. --  Note: the compiler generates direct calls to this interface, via Rtsfind.
  48.  
  49. with Ada.Exceptions;
  50. --  Used for Exception_ID
  51. --           Null_Id
  52. --           Raise_Exception
  53.  
  54. with System.Task_Primitives.Operations;
  55. --  used for Initialize_Lock
  56. --           Write_Lock
  57. --           Unlock
  58. --           Get_Priority
  59. --           Wakeup
  60.  
  61. with System.Tasking.Entry_Calls;
  62. --  used for Wait_For_Completion
  63. --           Wait_Until_Abortable
  64.  
  65. with System.Tasking.Initialization;
  66. --  Used for Defer_Abort,
  67. --           Undefer_Abort,
  68. --           Change_Base_Priority
  69.  
  70. pragma Elaborate_All (System.Tasking.Initialization);
  71. --  This insures that tasking is initialized if any protected objects are
  72. --  created.
  73.  
  74. with System.Tasking.Queuing;
  75. --  used for Enqueue
  76. --           Broadcast_Program_Error
  77. --           Select_Protected_Entry_Call
  78. --           Onqueue
  79. --           Count_Waiting
  80.  
  81. with System.Tasking.Rendezvous;
  82. --  used for Task_Do_Or_Queue
  83.  
  84. with System.Tasking.Debug;
  85. --  used for Trace
  86.  
  87. package body System.Tasking.Protected_Objects.Operations is
  88.  
  89.    package STPO renames System.Task_Primitives.Operations;
  90.  
  91.    use Task_Primitives;
  92.    use Tasking;
  93.    use Ada.Exceptions;
  94.    use Entries;
  95.  
  96.    -----------------------
  97.    -- Local Subprograms --
  98.    -----------------------
  99.  
  100.    procedure Update_For_Queue_To_PO
  101.      (Entry_Call : Entry_Call_Link;
  102.       With_Abort : Boolean);
  103.    pragma Inline (Update_For_Queue_To_PO);
  104.    --  Update the state of an existing entry call to reflect
  105.    --  the fact that it is being enqueued, based on
  106.    --  whether the current queuing action is with or without abort.
  107.    --  Call this only while holding the PO's lock.
  108.    --  It returns with the PO's lock still held.
  109.  
  110.    --------------
  111.    -- Enqueued --
  112.    --------------
  113.  
  114.    function Enqueued (Block : Communication_Block) return Boolean is
  115.    begin
  116.       return Block.Enqueued;
  117.    end Enqueued;
  118.  
  119.    ---------------------------------
  120.    -- Cancel_Protected_Entry_Call --
  121.    ---------------------------------
  122.  
  123.    --  Compiler interface only.  Do not call from within the RTS.
  124.    --  This should have analogous effect to Cancel_Task_Entry_Call,
  125.    --  setting the value of Block.Cancelled instead of returning
  126.    --  the parameter value Cancelled.
  127.  
  128.    --  The effect should be idempotent, since the call may already
  129.    --  have been dequeued.
  130.  
  131.    --  source code:
  132.  
  133.    --      select r.e;
  134.    --         ...A...
  135.    --      then abort
  136.    --         ...B...
  137.    --      end select;
  138.  
  139.    --  expanded code:
  140.  
  141.    --      declare
  142.    --         X : protected_entry_index := 1;
  143.    --         B80b : communication_block;
  144.    --         _init_proc (B80b);
  145.    --      begin
  146.    --         begin
  147.    --            A79b : label
  148.    --            A79b : declare
  149.    --               procedure _clean is
  150.    --               begin
  151.    --                  if enqueued (B80b) then
  152.    --                     cancel_protected_entry_call (B80b);
  153.    --                  end if;
  154.    --                  return;
  155.    --               end _clean;
  156.    --            begin
  157.    --               protected_entry_call (rTV!(r)._object'unchecked_access, X,
  158.    --                 null_address, asynchronous_call, B80b, objectF => 0);
  159.    --               if enqueued (B80b) then
  160.    --                  ...B...
  161.    --               end if;
  162.    --            at end
  163.    --               _clean;
  164.    --            end A79b;
  165.    --         exception
  166.    --            when _abort_signal =>
  167.    --               abort_undefer.all;
  168.    --               null;
  169.    --         end;
  170.    --         if not cancelled (B80b) then
  171.    --            x := ...A...
  172.    --         end if;
  173.    --      end;
  174.  
  175.    --  If the entry call completes after we get into the abortable part,
  176.    --  Abort_Signal should be raised and ATC will take us to the at-end
  177.    --  handler, which will call _clean.
  178.  
  179.    --  If the entry call returns with the call already completed,
  180.    --  we can skip this, and use the "if enqueued()" to go past
  181.    --  the at-end handler, but we will still call _clean.
  182.  
  183.    --  If the abortable part completes before the entry call is Done,
  184.    --  it will call _clean.
  185.  
  186.    --  If the entry call or the abortable part raises an exception,
  187.    --  we will still call _clean, but the value of Cancelled should not matter.
  188.  
  189.    --  Whoever calls _clean first gets to decide whether the call
  190.    --  has been "cancelled".
  191.  
  192.    --  Enqueued should be true if there is any chance that the call
  193.    --  is still on a queue.  It seems to be safe to make it True if
  194.    --  the call was Onqueue at some point before return from
  195.    --  Protected_Entry_Call.
  196.  
  197.    --  Cancelled should be true iff the abortable part completed
  198.    --  and succeeded in cancelling the entry call before it completed.
  199.  
  200.    --  ?????
  201.    --  The need for Enqueued is less obvious.
  202.    --  The  "if enqueued()" tests are not necessary, since both
  203.    --  Cancel_Protected_Entry_Call and Protected_Entry_Call must
  204.    --  do the same test internally, with locking.  The one that
  205.    --  makes cancellation conditional may be a useful heuristic
  206.    --  since at least 1/2 the time the call should be off-queue
  207.    --  by that point.  The other one seems totally useless, since
  208.    --  Protected_Entry_Call must do the same check and then
  209.    --  possibly wait for the call to be abortable, internally.
  210.  
  211.    --  We can check Call.State here without locking the caller's mutex,
  212.    --  since the call must be over after returning from Wait_For_Completion.
  213.    --  No other task can access the call record at this point.
  214.  
  215.    procedure Cancel_Protected_Entry_Call
  216.      (Block : in out Communication_Block)
  217.    is
  218.    begin
  219.       Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
  220.    end Cancel_Protected_Entry_Call;
  221.  
  222.    ---------------
  223.    -- Cancelled --
  224.    ---------------
  225.  
  226.    function Cancelled (Block : Communication_Block) return Boolean is
  227.    begin
  228.       return Block.Cancelled;
  229.    end Cancelled;
  230.  
  231.    --------------------
  232.    -- PO_Do_Or_Queue --
  233.    --------------------
  234.  
  235.    procedure PO_Do_Or_Queue
  236.      (Self_ID    : Task_ID;
  237.       Object     : Protection_Entries_Access;
  238.       Entry_Call : Entry_Call_Link;
  239.       With_Abort : Boolean)
  240.    is
  241.       E : Protected_Entry_Index := Protected_Entry_Index (Entry_Call.E);
  242.       New_Object        : Protection_Entries_Access;
  243.       Ceiling_Violation : Boolean;
  244.       Barrier_Value     : Boolean;
  245.  
  246.    begin
  247.       --  When the Action procedure for an entry body returns, it is either
  248.       --  completed (having called [Exceptional_]Complete_Entry_Body) or it
  249.       --  is queued, having executed a requeue statement.
  250.  
  251.       Barrier_Value :=
  252.         Object.Entry_Bodies (
  253.           Object.Find_Body_Index (Object.Compiler_Info, E)).
  254.             Barrier (Object.Compiler_Info, E);
  255.  
  256.       if Barrier_Value then
  257.  
  258.          --  Not abortable while service is in progress.
  259.  
  260.          if Entry_Call.State = Now_Abortable then
  261.             Entry_Call.State := Was_Abortable;
  262.          end if;
  263.  
  264.          Object.Call_In_Progress := Entry_Call;
  265.  
  266.          pragma Debug
  267.           (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
  268.          Object.Entry_Bodies (
  269.            Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
  270.              Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
  271.  
  272.          if Object.Call_In_Progress /= null then
  273.  
  274.             --  Body of current entry served call to completion
  275.  
  276.             Object.Call_In_Progress := null;
  277.             Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
  278.  
  279.          else
  280.             --  Body of current entry requeued the call
  281.             New_Object := To_Protection (Entry_Call.Called_PO);
  282.  
  283.             if New_Object = null then
  284.  
  285.                --  Call was requeued to a task
  286.  
  287.                if not Rendezvous.Task_Do_Or_Queue
  288.                  (Self_ID, Entry_Call,
  289.                   With_Abort => Entry_Call.Requeue_With_Abort)
  290.                then
  291.                   Queuing.Broadcast_Program_Error
  292.                    (Self_ID, Object, Entry_Call);
  293.                end if;
  294.                return;
  295.             end if;
  296.  
  297.             if Object /= New_Object then
  298.                --  Requeue is on a different object
  299.  
  300.                STPO.Write_Lock (New_Object.L'Access, Ceiling_Violation);
  301.  
  302.                if Ceiling_Violation then
  303.                   Object.Call_In_Progress := null;
  304.                   Queuing.Broadcast_Program_Error
  305.                    (Self_ID, Object, Entry_Call);
  306.  
  307.                else
  308.                   PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call, With_Abort);
  309.                   PO_Service_Entries (Self_ID, New_Object);
  310.                   Unlock_Entries (New_Object);
  311.                end if;
  312.  
  313.             else
  314.                --  Requeue is on same protected object
  315.  
  316.                if Entry_Call.Requeue_With_Abort
  317.                  and then Entry_Call.Cancellation_Attempted
  318.                then
  319.                   --  If this is a requeue with abort and someone tried
  320.                   --  to cancel this call, cancel it at this point.
  321.  
  322.                   Entry_Call.State := Cancelled;
  323.                   return;
  324.                end if;
  325.  
  326.                if not With_Abort or else
  327.                  Entry_Call.Mode /= Conditional_Call
  328.                then
  329.                   E := Protected_Entry_Index (Entry_Call.E);
  330.                   Queuing.Enqueue
  331.                     (New_Object.Entry_Queues (E), Entry_Call);
  332.                   Update_For_Queue_To_PO (Entry_Call, With_Abort);
  333.  
  334.                else
  335.                   --  ?????
  336.                   --  Can we convert this recursion to a loop?
  337.  
  338.                   PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call, With_Abort);
  339.                end if;
  340.             end if;
  341.          end if;
  342.  
  343.       elsif Entry_Call.Mode /= Conditional_Call or else
  344.         not With_Abort then
  345.          Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
  346.          Update_For_Queue_To_PO (Entry_Call, With_Abort);
  347.  
  348.       else
  349.          --  Conditional_Call and With_Abort
  350.  
  351.          STPO.Write_Lock (Entry_Call.Self);
  352.          pragma Assert (Entry_Call.State >= Was_Abortable);
  353.          Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
  354.          STPO.Unlock (Entry_Call.Self);
  355.       end if;
  356.  
  357.    exception
  358.       when others =>
  359.          Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
  360.    end PO_Do_Or_Queue;
  361.  
  362.    ---------------------
  363.    -- Service_Entries --
  364.    ---------------------
  365.  
  366.    procedure Service_Entries (Object : Protection_Entries_Access) is
  367.       Self_ID : constant Task_ID := STPO.Self;
  368.    begin
  369.       PO_Service_Entries (Self_ID, Object);
  370.    end Service_Entries;
  371.  
  372.    ------------------------
  373.    -- PO_Service_Entries --
  374.    ------------------------
  375.  
  376.    procedure PO_Service_Entries
  377.      (Self_ID : Task_ID;
  378.       Object : Protection_Entries_Access)
  379.    is
  380.       Entry_Call : Entry_Call_Link;
  381.       E          : Protected_Entry_Index;
  382.       Caller     : Task_ID;
  383.       New_Object : Protection_Entries_Access;
  384.       Ceiling_Violation : Boolean;
  385.  
  386.    begin
  387.       loop
  388.          Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
  389.  
  390.          if Entry_Call /= null then
  391.             E := Protected_Entry_Index (Entry_Call.E);
  392.  
  393.             --  Not abortable while service is in progress.
  394.  
  395.             if Entry_Call.State = Now_Abortable then
  396.                Entry_Call.State := Was_Abortable;
  397.             end if;
  398.  
  399.             Object.Call_In_Progress := Entry_Call;
  400.  
  401.             begin
  402.                pragma Debug
  403.                 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
  404.                Object.Entry_Bodies (
  405.                  Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
  406.                    Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
  407.             exception
  408.                when others =>
  409.                   Queuing.Broadcast_Program_Error
  410.                     (Self_ID, Object, Entry_Call);
  411.             end;
  412.  
  413.             if Object.Call_In_Progress /= null then
  414.                Object.Call_In_Progress := null;
  415.                Caller := Entry_Call.Self;
  416.                STPO.Write_Lock (Caller);
  417.                Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
  418.                STPO.Unlock (Caller);
  419.  
  420.             else
  421.                --  Call needs to be requeued
  422.  
  423.                New_Object := To_Protection (Entry_Call.Called_PO);
  424.  
  425.                if New_Object = null then
  426.  
  427.                   --  Call is to be requeued to a task entry
  428.  
  429.                   if not Rendezvous.Task_Do_Or_Queue
  430.                     (Self_ID, Entry_Call,
  431.                      With_Abort => Entry_Call.Requeue_With_Abort)
  432.                   then
  433.                      Queuing.Broadcast_Program_Error
  434.                       (Self_ID, Object, Entry_Call);
  435.                   end if;
  436.  
  437.                else
  438.                   --  Call should be requeued to a PO
  439.  
  440.                   if Object /= New_Object then
  441.                      --  Requeue is to different PO
  442.  
  443.                      STPO.Write_Lock (New_Object.L'Access, Ceiling_Violation);
  444.  
  445.                      if Ceiling_Violation then
  446.                         Object.Call_In_Progress := null;
  447.                         Queuing.Broadcast_Program_Error
  448.                           (Self_ID, Object, Entry_Call);
  449.  
  450.                      else
  451.                         PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call,
  452.                           Entry_Call.Requeue_With_Abort);
  453.                         PO_Service_Entries (Self_ID, New_Object);
  454.                         Unlock_Entries (New_Object);
  455.                      end if;
  456.  
  457.                   else
  458.                      --  Requeue is to same protected object
  459.  
  460.                      --  ??? Try to compensate apparent failure of the
  461.                      --  scheduler on some OS (e.g VxWorks) to give higher
  462.                      --  priority tasks a chance to run (see CXD6002).
  463.  
  464.                      STPO.Yield (False);
  465.  
  466.                      if Entry_Call.Requeue_With_Abort
  467.                        and then Entry_Call.Cancellation_Attempted
  468.                      then
  469.                         --  If this is a requeue with abort and someone tried
  470.                         --  to cancel this call, cancel it at this point.
  471.  
  472.                         Entry_Call.State := Cancelled;
  473.                         exit;
  474.                      end if;
  475.  
  476.                      if not Entry_Call.Requeue_With_Abort or else
  477.                        Entry_Call.Mode /= Conditional_Call
  478.                      then
  479.                         E := Protected_Entry_Index (Entry_Call.E);
  480.                         Queuing.Enqueue
  481.                           (New_Object.Entry_Queues (E), Entry_Call);
  482.                         Update_For_Queue_To_PO (Entry_Call,
  483.                           Entry_Call.Requeue_With_Abort);
  484.  
  485.                      else
  486.                         PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call,
  487.                           Entry_Call.Requeue_With_Abort);
  488.                      end if;
  489.                   end if;
  490.                end if;
  491.             end if;
  492.  
  493.          else
  494.             exit;
  495.          end if;
  496.       end loop;
  497.    end PO_Service_Entries;
  498.  
  499.    --------------------------
  500.    -- Protected_Entry_Call --
  501.    --------------------------
  502.  
  503.    --  Compiler interface only.  Do not call from within the RTS.
  504.  
  505.    --  select r.e;
  506.    --     ...A...
  507.    --  else
  508.    --     ...B...
  509.    --  end select;
  510.  
  511.    --  declare
  512.    --     X : protected_entry_index := 1;
  513.    --     B85b : communication_block;
  514.    --     _init_proc (B85b);
  515.    --  begin
  516.    --     protected_entry_call (rTV!(r)._object'unchecked_access, X,
  517.    --       null_address, conditional_call, B85b, objectF => 0);
  518.    --     if cancelled (B85b) then
  519.    --        ...B...
  520.    --     else
  521.    --        ...A...
  522.    --     end if;
  523.    --  end;
  524.  
  525.    --  See also Cancel_Protected_Entry_Call for code expansion of
  526.    --  asynchronous entry call.
  527.  
  528.    --  The initial part of this procedure does not need to lock the
  529.    --  the calling task's ATCB, up to the point where the call record
  530.    --  first may be queued (PO_Do_Or_Queue), since before that no
  531.    --  other task will have access to the record.
  532.  
  533.    --  If this is a call made inside of an abort deferred region,
  534.    --  the call should be never abortable.
  535.  
  536.    --  If the call was not queued abortably, we need to wait
  537.    --  until it is before proceeding with the abortable part.
  538.  
  539.    --  There are some heuristics here, just to save time for
  540.    --  frequently occurring cases.  For example, we check
  541.    --  Initially_Abortable to try to avoid calling the procedure
  542.    --  Wait_Until_Abortable, since the normal case for async.
  543.    --  entry calls is to be queued abortably.
  544.  
  545.    --  Another heuristic uses the Block.Enqueued to try to avoid
  546.    --  calling Cancel_Protected_Entry_Call if the call can be
  547.    --  served immediately.
  548.  
  549.    procedure Protected_Entry_Call
  550.      (Object              : Protection_Entries_Access;
  551.       E                   : Protected_Entry_Index;
  552.       Uninterpreted_Data  : System.Address;
  553.       Mode                : Call_Modes;
  554.       Block               : out Communication_Block)
  555.    is
  556.       Self_ID             : Task_ID  := STPO.Self;
  557.       Entry_Call          : Entry_Call_Link;
  558.       Initially_Abortable : Boolean;
  559.       Ceiling_Violation   : Boolean;
  560.  
  561.    begin
  562.       pragma Debug
  563.         (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
  564.  
  565.       if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
  566.          Raise_Exception (Storage_Error'Identity,
  567.            "not enough ATC nesting levels");
  568.       end if;
  569.  
  570.       Initialization.Defer_Abort (Self_ID);
  571.       STPO.Write_Lock (Object.L'Access, Ceiling_Violation);
  572.  
  573.       if Ceiling_Violation then
  574.  
  575.          --  Failed ceiling check
  576.  
  577.          Initialization.Undefer_Abort (Self_ID);
  578.          raise Program_Error;
  579.       end if;
  580.  
  581.       Block.Self := Self_ID;
  582.       Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
  583.       pragma Debug
  584.         (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
  585.          ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
  586.       Entry_Call :=
  587.          Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
  588.       Entry_Call.Next := null;
  589.       Entry_Call.Mode := Mode;
  590.       Entry_Call.Cancellation_Attempted := False;
  591.  
  592.       if Self_ID.Deferral_Level > 1 then
  593.          Entry_Call.State := Never_Abortable;
  594.       else
  595.          Entry_Call.State := Now_Abortable;
  596.       end if;
  597.  
  598.       Entry_Call.E := Entry_Index (E);
  599.       Entry_Call.Prio := STPO.Get_Priority (Self_ID);
  600.       Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
  601.       Entry_Call.Called_PO := To_Address (Object);
  602.       Entry_Call.Called_Task := null;
  603.       Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
  604.  
  605.       PO_Do_Or_Queue (Self_ID, Object, Entry_Call, With_Abort => True);
  606.       Initially_Abortable := Entry_Call.State = Now_Abortable;
  607.       PO_Service_Entries (Self_ID, Object);
  608.  
  609.       Unlock_Entries (Object);
  610.  
  611.       --  Try to prevent waiting later (in Cancel_Protected_Entry_Call)
  612.       --  for completed or cancelled calls.  (This is a heuristic, only.)
  613.  
  614.       if Entry_Call.State >= Done then
  615.  
  616.          --  Once State >= Done it will not change any more.
  617.  
  618.          Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level - 1;
  619.          pragma Debug
  620.            (Debug.Trace (Self_ID, "PEC: exited to ATC level: " &
  621.             ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
  622.          Block.Enqueued := False;
  623.          Block.Cancelled := Entry_Call.State = Cancelled;
  624.          Initialization.Undefer_Abort (Self_ID);
  625.          Entry_Calls.Check_Exception (Self_ID, Entry_Call);
  626.          return;
  627.  
  628.       else
  629.          --  In this case we cannot conclude anything,
  630.          --  since State can change concurrently.
  631.          null;
  632.       end if;
  633.  
  634.       --  Now for the general case.
  635.  
  636.       if Mode = Asynchronous_Call then
  637.  
  638.          --  Try to avoid an expensive call.
  639.  
  640.          if not Initially_Abortable then
  641.             Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
  642.          end if;
  643.  
  644.       elsif Mode < Asynchronous_Call then
  645.  
  646.          --  Simple_Call or Conditional_Call
  647.  
  648.          STPO.Write_Lock (Self_ID);
  649.          Entry_Calls.Wait_For_Completion (Self_ID, Entry_Call);
  650.          STPO.Unlock (Self_ID);
  651.          Block.Cancelled := Entry_Call.State = Cancelled;
  652.  
  653.       else
  654.          pragma Assert (False);
  655.          null;
  656.       end if;
  657.  
  658.       Initialization.Undefer_Abort (Self_ID);
  659.       Entry_Calls.Check_Exception (Self_ID, Entry_Call);
  660.  
  661.    end Protected_Entry_Call;
  662.  
  663.    --------------------------------
  664.    -- Timed_Protected_Entry_Call --
  665.    --------------------------------
  666.  
  667.    --  Compiler interface only.  Do not call from within the RTS.
  668.  
  669.    procedure Timed_Protected_Entry_Call
  670.      (Object                : Protection_Entries_Access;
  671.       E                     : Protected_Entry_Index;
  672.       Uninterpreted_Data    : System.Address;
  673.       Timeout               : Duration;
  674.       Mode                  : Delay_Modes;
  675.       Entry_Call_Successful : out Boolean)
  676.    is
  677.       Self_ID           : Task_ID  := STPO.Self;
  678.       Entry_Call        : Entry_Call_Link;
  679.       Ceiling_Violation : Boolean;
  680.  
  681.    begin
  682.       if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
  683.          Raise_Exception (Storage_Error'Identity,
  684.            "not enough ATC nesting levels");
  685.       end if;
  686.  
  687.       Initialization.Defer_Abort (Self_ID);
  688.       STPO.Write_Lock (Object.L'Access, Ceiling_Violation);
  689.  
  690.       if Ceiling_Violation then
  691.          Initialization.Undefer_Abort (Self_ID);
  692.          raise Program_Error;
  693.       end if;
  694.  
  695.       Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
  696.       pragma Debug
  697.         (Debug.Trace (Self_ID, "TPEC: exited to ATC level: " &
  698.          ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
  699.       Entry_Call :=
  700.         Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
  701.       Entry_Call.Next := null;
  702.       Entry_Call.Mode := Timed_Call;
  703.       Entry_Call.Cancellation_Attempted := False;
  704.  
  705.       if Self_ID.Deferral_Level > 1 then
  706.          Entry_Call.State := Never_Abortable;
  707.       else
  708.          Entry_Call.State := Now_Abortable;
  709.       end if;
  710.  
  711.       Entry_Call.E := Entry_Index (E);
  712.       Entry_Call.Prio := STPO.Get_Priority (Self_ID);
  713.       Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
  714.       Entry_Call.Called_PO := To_Address (Object);
  715.       Entry_Call.Called_Task := null;
  716.       Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
  717.  
  718.       PO_Do_Or_Queue (Self_ID, Object, Entry_Call, With_Abort => True);
  719.       PO_Service_Entries (Self_ID, Object);
  720.  
  721.       Unlock_Entries (Object);
  722.  
  723.       --  Try to avoid waiting for completed or cancelled calls.
  724.  
  725.       if Entry_Call.State >= Done then
  726.          Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level - 1;
  727.          pragma Debug
  728.            (Debug.Trace (Self_ID, "TPEC: exited to ATC level: " &
  729.             ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
  730.          Entry_Call_Successful := Entry_Call.State = Done;
  731.          Initialization.Undefer_Abort (Self_ID);
  732.          Entry_Calls.Check_Exception (Self_ID, Entry_Call);
  733.          return;
  734.       end if;
  735.  
  736.       Entry_Calls.Wait_For_Completion_With_Timeout
  737.         (Self_ID, Entry_Call, Timeout, Mode);
  738.       Initialization.Undefer_Abort (Self_ID);
  739.       Entry_Call_Successful := Entry_Call.State = Done;
  740.       Entry_Calls.Check_Exception (Self_ID, Entry_Call);
  741.    end Timed_Protected_Entry_Call;
  742.  
  743.    -------------------------
  744.    -- Complete_Entry_Body --
  745.    -------------------------
  746.  
  747.    procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
  748.    begin
  749.       Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
  750.    end Complete_Entry_Body;
  751.  
  752.    -------------------------------------
  753.    -- Exceptional_Complete_Entry_Body --
  754.    -------------------------------------
  755.  
  756.    procedure Exceptional_Complete_Entry_Body
  757.      (Object : Protection_Entries_Access;
  758.       Ex     : Ada.Exceptions.Exception_Id)
  759.    is
  760.       Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
  761.  
  762.    begin
  763.       pragma Debug
  764.        (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
  765.  
  766.       --  We must have abort deferred, since we are inside
  767.       --  a protected operation.
  768.  
  769.       if Entry_Call /= null then
  770.  
  771.          --  The call was not requeued.
  772.  
  773.          Entry_Call.Exception_To_Raise := Ex;
  774.  
  775. --  ?????
  776. --  The caller should do the following, after return from this
  777. --  procedure, if Call_In_Progress /= null
  778. --       Write_Lock (Entry_Call.Self);
  779. --       Initialization.Wakeup_Entry_Caller (STPO.Self, Entry_Call, Done);
  780. --       Unlock (Entry_Call.Self);
  781.  
  782.       end if;
  783.    end Exceptional_Complete_Entry_Body;
  784.  
  785.    -----------------------------
  786.    -- Requeue_Protected_Entry --
  787.    -----------------------------
  788.  
  789.    --  Compiler interface only.  Do not call from within the RTS.
  790.  
  791.    --  entry e when b is
  792.    --  begin
  793.    --     b := false;
  794.    --     ...A...
  795.    --     requeue e2;
  796.    --  end e;
  797.  
  798.    --  procedure rPT__E10b (O : address; P : address; E :
  799.    --    protected_entry_index) is
  800.    --     type rTVP is access rTV;
  801.    --     freeze rTVP []
  802.    --     _object : rTVP := rTVP!(O);
  803.    --  begin
  804.    --     declare
  805.    --        rR : protection renames _object._object;
  806.    --        vP : integer renames _object.v;
  807.    --        bP : boolean renames _object.b;
  808.    --     begin
  809.    --        b := false;
  810.    --        ...A...
  811.    --        requeue_protected_entry (rR'unchecked_access, rR'
  812.    --          unchecked_access, 2, false, objectF => 0, new_objectF =>
  813.    --          0);
  814.    --        return;
  815.    --     end;
  816.    --     complete_entry_body (_object._object'unchecked_access, objectF =>
  817.    --       0);
  818.    --     return;
  819.    --  exception
  820.    --     when others =>
  821.    --        abort_undefer.all;
  822.    --        exceptional_complete_entry_body (_object._object'
  823.    --          unchecked_access, current_exception, objectF => 0);
  824.    --        return;
  825.    --  end rPT__E10b;
  826.  
  827.    procedure Requeue_Protected_Entry
  828.      (Object     : Protection_Entries_Access;
  829.       New_Object : Protection_Entries_Access;
  830.       E          : Protected_Entry_Index;
  831.       With_Abort : Boolean)
  832.    is
  833.       Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
  834.  
  835.    begin
  836.       pragma Debug
  837.         (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
  838.       pragma Assert (STPO.Self.Deferral_Level > 0);
  839.  
  840.       Entry_Call.E := Entry_Index (E);
  841.       Entry_Call.Called_PO := To_Address (New_Object);
  842.       Entry_Call.Called_Task := null;
  843.       Entry_Call.Requeue_With_Abort := With_Abort;
  844.       Object.Call_In_Progress := null;
  845.    end Requeue_Protected_Entry;
  846.  
  847.    -------------------------------------
  848.    -- Requeue_Task_To_Protected_Entry --
  849.    -------------------------------------
  850.  
  851.    --  Compiler interface only.
  852.  
  853.    --    accept e1 do
  854.    --      ...A...
  855.    --      requeue r.e2;
  856.    --    end e1;
  857.  
  858.    --    A79b : address;
  859.    --    L78b : label
  860.    --    begin
  861.    --       accept_call (1, A79b);
  862.    --       ...A...
  863.    --       requeue_task_to_protected_entry (rTV!(r)._object'
  864.    --         unchecked_access, 2, false, new_objectF => 0);
  865.    --       goto L78b;
  866.    --       <<L78b>>
  867.    --       complete_rendezvous;
  868.    --    exception
  869.    --       when all others =>
  870.    --          exceptional_complete_rendezvous (get_gnat_exception);
  871.    --    end;
  872.  
  873.    procedure Requeue_Task_To_Protected_Entry
  874.      (New_Object : Protection_Entries_Access;
  875.       E          : Protected_Entry_Index;
  876.       With_Abort : Boolean)
  877.    is
  878.       Self_ID       : constant Task_ID := STPO.Self;
  879.       Entry_Call    : constant Entry_Call_Link := Self_ID.Common.Call;
  880.  
  881.    begin
  882.       Initialization.Defer_Abort (Self_ID);
  883.       STPO.Write_Lock (Self_ID);
  884.       Entry_Call.Needs_Requeue := True;
  885.       Entry_Call.Requeue_With_Abort := With_Abort;
  886.       Entry_Call.Called_PO := To_Address (New_Object);
  887.       Entry_Call.Called_Task := null;
  888.       STPO.Unlock (Self_ID);
  889.       Entry_Call.E := Entry_Index (E);
  890.       Initialization.Undefer_Abort (Self_ID);
  891.    end Requeue_Task_To_Protected_Entry;
  892.  
  893.    --  ??????
  894.    --  Do we really need to lock Self_ID above?
  895.    --  Might the caller be trying to cancel?
  896.    --  If so, it should fail, since the call state should not be
  897.    --  abortable while the call is in service.
  898.  
  899.    ---------------------
  900.    -- Protected_Count --
  901.    ---------------------
  902.  
  903.    function Protected_Count
  904.      (Object : Protection_Entries'Class;
  905.       E      : Protected_Entry_Index)
  906.       return   Natural
  907.    is
  908.    begin
  909.       return Queuing.Count_Waiting (Object.Entry_Queues (E));
  910.    end Protected_Count;
  911.  
  912.    ----------------------------
  913.    -- Protected_Entry_Caller --
  914.    ----------------------------
  915.  
  916.    function Protected_Entry_Caller (Object : Protection_Entries'Class)
  917.      return Task_ID is
  918.    begin
  919.       return Object.Call_In_Progress.Self;
  920.    end Protected_Entry_Caller;
  921.  
  922.    ----------------------------
  923.    -- Update_For_Queue_To_PO --
  924.    ----------------------------
  925.  
  926.    --  Update the state of an existing entry call, based on
  927.    --  whether the current queuing action is with or without abort.
  928.    --  Call this only while holding the server's lock.
  929.    --  It returns with the server's lock released.
  930.  
  931.    New_State : constant array (Boolean, Entry_Call_State)
  932.      of Entry_Call_State :=
  933.        (True =>
  934.          (Never_Abortable   => Never_Abortable,
  935.           Not_Yet_Abortable => Now_Abortable,
  936.           Was_Abortable     => Now_Abortable,
  937.           Now_Abortable     => Now_Abortable,
  938.           Done              => Done,
  939.           Cancelled         => Cancelled),
  940.         False =>
  941.          (Never_Abortable   => Never_Abortable,
  942.           Not_Yet_Abortable => Not_Yet_Abortable,
  943.           Was_Abortable     => Was_Abortable,
  944.           Now_Abortable     => Now_Abortable,
  945.           Done              => Done,
  946.           Cancelled         => Cancelled)
  947.        );
  948.  
  949.    procedure Update_For_Queue_To_PO
  950.      (Entry_Call : Entry_Call_Link;
  951.       With_Abort : Boolean)
  952.    is
  953.       Old : Entry_Call_State := Entry_Call.State;
  954.  
  955.    begin
  956.       pragma Assert (Old < Done);
  957.  
  958.       Entry_Call.State := New_State (With_Abort, Entry_Call.State);
  959.  
  960.       if Entry_Call.Mode = Asynchronous_Call then
  961.          if Old < Was_Abortable and then
  962.            Entry_Call.State = Now_Abortable
  963.          then
  964.             STPO.Write_Lock (Entry_Call.Self);
  965.  
  966.             if Entry_Call.Self.Common.State = Async_Select_Sleep then
  967.                STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
  968.             end if;
  969.  
  970.             STPO.Unlock (Entry_Call.Self);
  971.          end if;
  972.  
  973.       elsif Entry_Call.Mode = Conditional_Call then
  974.          pragma Assert (Entry_Call.State < Was_Abortable);
  975.          null;
  976.       end if;
  977.    end Update_For_Queue_To_PO;
  978.  
  979. end System.Tasking.Protected_Objects.Operations;
  980.