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-taenca.adb < prev    next >
Text File  |  2000-07-19  |  27KB  |  712 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 . E N T R Y _ C A L L S          --
  6. --                                                                          --
  7. --                                  B o d y                                 --
  8. --                                                                          --
  9. --                             $Revision: 1.33 $
  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 internal RTS calls implementing operations
  38. --  that apply to general entry calls, that is, calls to either
  39. --  protected or task entries.
  40.  
  41. --  These declarations are not part of the GNARL interface
  42.  
  43. with System.Task_Primitives.Operations;
  44. --  used for STPO.Write_Lock
  45. --           Unlock
  46. --           STPO.Get_Priority
  47. --           Sleep
  48. --           Timed_Sleep
  49.  
  50. with System.Tasking.Initialization;
  51. --  used for Change_Base_Priority
  52. --           Poll_Base_Priority_Change_At_Entry_Call
  53. --           Dynamic_Priority_Support
  54. --           Defer_Abort/Undefer_Abort
  55.  
  56. with System.Tasking.Protected_Objects.Entries;
  57. --  used for To_Protection
  58.  
  59. with System.Tasking.Protected_Objects.Operations;
  60. --  used for PO_Service_Entries
  61.  
  62. with System.Tasking.Queuing;
  63. --  used for Requeue_Call_With_New_Prio
  64. --           Onqueue
  65. --           Dequeue_Call
  66.  
  67. with System.Tasking.Utilities;
  68. --  used for Exit_One_ATC_Level
  69.  
  70. package body System.Tasking.Entry_Calls is
  71.  
  72.    package STPO renames System.Task_Primitives.Operations;
  73.  
  74.    use System.Task_Primitives;
  75.    use System.Tasking.Protected_Objects.Entries;
  76.    use System.Tasking.Protected_Objects.Operations;
  77.  
  78.    --  DO NOT use Protected_Objects.Lock or Protected_Objects.Unlock
  79.    --  internally. Those operations will raise Program_Error, which
  80.    --  we do are not prepared to handle inside the RTS. Instead, use
  81.    --  System.Task_Primitives lock operations directly on Protection.L.
  82.  
  83.    -----------------------
  84.    -- Local Subprograms --
  85.    -----------------------
  86.  
  87.    procedure Lock_Server (Entry_Call : Entry_Call_Link);
  88.    --  This locks the server targeted by Entry_Call.
  89.    --
  90.    --  This may be a task or a protected object,
  91.    --  depending on the target of the original call or any subsequent
  92.    --  requeues.
  93.    --
  94.    --  This routine is needed because the field specifying the server
  95.    --  for this call must be protected by the server's mutex. If it were
  96.    --  protected by the caller's mutex, accessing the server's queues would
  97.    --  require locking the caller to get the server, locking the server,
  98.    --  and then accessing the queues. This involves holding two ATCB
  99.    --  locks at once, something which we can guarantee that it will always
  100.    --  be done in the same order, or locking a protected object while we
  101.    --  hold an ATCB lock, something which is not permitted. Since
  102.    --  the server cannot be obtained reliably, it must be obtained unreliably
  103.    --  and then checked again once it has been locked.
  104.  
  105.    procedure Unlock_Server (Entry_Call : Entry_Call_Link);
  106.    --  STPO.Unlock the server targeted by Entry_Call. The server must
  107.    --  be locked before calling this.
  108.  
  109.    procedure Unlock_And_Update_Server
  110.      (Self_ID    : Task_ID;
  111.       Entry_Call : Entry_Call_Link);
  112.    --  Similar to Unlock_Server, but services entry calls if the
  113.    --  server is a protected object.
  114.  
  115.    procedure Check_Pending_Actions_For_Entry_Call
  116.      (Self_ID    : Task_ID;
  117.       Entry_Call : Entry_Call_Link);
  118.    pragma Inline (Check_Pending_Actions_For_Entry_Call);
  119.    --  This procedure performs priority change of a queued call and
  120.    --  dequeuing of an entry call when an the call is cancelled.
  121.    --  If the call is dequeued the state should be set to Cancelled.
  122.  
  123.    procedure Poll_Base_Priority_Change_At_Entry_Call
  124.      (Self_ID    : Task_ID;
  125.       Entry_Call : Entry_Call_Link);
  126.    pragma Inline (Poll_Base_Priority_Change_At_Entry_Call);
  127.    --  Has to be called with the Self_ID's ATCB write-locked.
  128.    --  May temporariliy release the lock.
  129.  
  130.    -----------------
  131.    -- Lock_Server --
  132.    -----------------
  133.  
  134.    --  This should only be called by the Entry_Call.Self.
  135.    --  It should be holding no other ATCB locks at the time.
  136.  
  137.    procedure Lock_Server (Entry_Call : Entry_Call_Link) is
  138.       Test_Task         : Task_ID;
  139.       Test_PO           : Protection_Entries_Access;
  140.       Ceiling_Violation : Boolean;
  141.       Failures          : Integer := 0;
  142.  
  143.    begin
  144.       Test_Task := Entry_Call.Called_Task;
  145.  
  146.       loop
  147.          if Test_Task = null then
  148.  
  149.             --  Entry_Call was queued on a protected object,
  150.             --  or in transition, when we last fetched Test_Task.
  151.  
  152.             Test_PO := To_Protection (Entry_Call.Called_PO);
  153.  
  154.             if Test_PO = null then
  155.  
  156.                --  We had very bad luck, interleaving with TWO different
  157.                --  requeue operations. Go around the loop and try again.
  158.  
  159.                STPO.Yield;
  160.  
  161.             else
  162.                STPO.Write_Lock (Test_PO.L'Access, Ceiling_Violation);
  163.  
  164.                --  ????
  165.                --  The following code allows Lock_Server to be called
  166.                --  when cancelling a call, to allow for the possibility
  167.                --  that the priority of the caller has been raised
  168.                --  beyond that of the protected entry call by
  169.                --  Ada.Dynamic_Priorities.STPO.Set_Priority.
  170.  
  171.                --  If the current task has a higher priority than the ceiling
  172.                --  of the protected object, temporarily lower it. It will
  173.                --  be reset in Unlock.
  174.  
  175.                if Ceiling_Violation then
  176.                   declare
  177.                      Current_Task      : Task_ID := STPO.Self;
  178.                      Old_Base_Priority : System.Any_Priority;
  179.  
  180.                   begin
  181.                      STPO.Write_Lock (Current_Task);
  182.                      Old_Base_Priority := Current_Task.Common.Base_Priority;
  183.                      Current_Task.New_Base_Priority := Test_PO.Ceiling;
  184.                      System.Tasking.Initialization.Change_Base_Priority
  185.                        (Current_Task);
  186.                      STPO.Unlock (Current_Task);
  187.  
  188.                      --  Following lock should not fail
  189.                      STPO.Write_Lock (Test_PO.L'Access, Ceiling_Violation);
  190.  
  191.                      Test_PO.Old_Base_Priority := Old_Base_Priority;
  192.                      Test_PO.Pending_Action := True;
  193.                   end;
  194.                end if;
  195.  
  196.                exit when To_Address (Test_PO) = Entry_Call.Called_PO;
  197.                STPO.Unlock (Test_PO.L'Access);
  198.             end if;
  199.          else
  200.             STPO.Write_Lock (Test_Task);
  201.             exit when Test_Task = Entry_Call.Called_Task;
  202.             STPO.Unlock (Test_Task);
  203.          end if;
  204.  
  205.          Test_Task := Entry_Call.Called_Task;
  206.          Failures := Failures + 1;
  207.          pragma Assert (Failures <= 5);
  208.       end loop;
  209.    end Lock_Server;
  210.  
  211.    -------------------
  212.    -- Unlock_Server --
  213.    -------------------
  214.  
  215.    procedure Unlock_Server (Entry_Call : Entry_Call_Link) is
  216.       Caller    : Task_ID;
  217.       Called_PO : Protection_Entries_Access;
  218.  
  219.    begin
  220.       if Entry_Call.Called_Task /= null then
  221.          STPO.Unlock (Entry_Call.Called_Task);
  222.       else
  223.          Called_PO := To_Protection (Entry_Call.Called_PO);
  224.  
  225.          if Called_PO.Pending_Action then
  226.             Called_PO.Pending_Action := False;
  227.             Caller := STPO.Self;
  228.             STPO.Write_Lock (Caller);
  229.             Caller.New_Base_Priority := Called_PO.Old_Base_Priority;
  230.             Initialization.Change_Base_Priority (Caller);
  231.             STPO.Unlock (Caller);
  232.          end if;
  233.  
  234.          STPO.Unlock (Called_PO.L'Access);
  235.       end if;
  236.    end Unlock_Server;
  237.  
  238.    ------------------------------
  239.    -- Unlock_And_Update_Server --
  240.    ------------------------------
  241.  
  242.    procedure Unlock_And_Update_Server
  243.      (Self_ID    : Task_ID;
  244.       Entry_Call : Entry_Call_Link)
  245.    is
  246.       Called_PO : Protection_Entries_Access;
  247.       Caller    : Task_ID;
  248.  
  249.    begin
  250.       if Entry_Call.Called_Task /= null then
  251.          STPO.Unlock (Entry_Call.Called_Task);
  252.       else
  253.          Called_PO := To_Protection (Entry_Call.Called_PO);
  254.          PO_Service_Entries (Self_ID, Called_PO);
  255.  
  256.          if Called_PO.Pending_Action then
  257.             Called_PO.Pending_Action := False;
  258.             Caller := STPO.Self;
  259.             STPO.Write_Lock (Caller);
  260.             Caller.New_Base_Priority := Called_PO.Old_Base_Priority;
  261.             Initialization.Change_Base_Priority (Caller);
  262.             STPO.Unlock (Caller);
  263.          end if;
  264.  
  265.          STPO.Unlock (Called_PO.L'Access);
  266.       end if;
  267.    end Unlock_And_Update_Server;
  268.  
  269.    -------------------------
  270.    -- Wait_For_Completion--
  271.    -------------------------
  272.  
  273.    --  Call this only when holding Self_ID locked
  274.  
  275.    --  If this is a conditional call, it should be cancelled when it
  276.    --  becomes abortable. This is checked in the loop below.
  277.  
  278.    --  We do the same thing for Asynchronous_Call. Executing the following
  279.    --  loop will clear the Pending_Action field if there is no
  280.    --  Pending_Action. We want the call made from Cancel_Task_Entry_Call
  281.    --  to check the abortion level so that we make sure that the Cancelled
  282.    --  field reflect the status of an Asynchronous_Call properly.
  283.    --  This problem came up when the triggered statement and the abortable
  284.    --  part depend on entries of the same task. When a cancellation is
  285.    --  delivered, Undefer_Abort in the call made from abortable part
  286.    --  sets the Pending_Action bit to false. However, the call is actually
  287.    --  made to cancel the Asynchronous Call so that we need to check its
  288.    --  status here again. Otherwise we may end up waiting for a cancelled
  289.    --  call forever.
  290.  
  291.    --  ????? .........
  292.    --  Recheck the logic of the above old comment.  It may be stale.
  293.  
  294.    procedure Wait_For_Completion
  295.      (Self_ID    : Task_ID;
  296.       Entry_Call : Entry_Call_Link)
  297.    is
  298.    begin
  299.       pragma Assert (Self_ID = Entry_Call.Self);
  300.       Self_ID.Common.State := Entry_Caller_Sleep;
  301.  
  302.       loop
  303.          Check_Pending_Actions_For_Entry_Call (Self_ID, Entry_Call);
  304.          exit when Entry_Call.State >= Done;
  305.          STPO.Sleep (Self_ID, Entry_Caller_Sleep);
  306.       end loop;
  307.  
  308.       Self_ID.Common.State := Runnable;
  309.       Utilities.Exit_One_ATC_Level (Self_ID);
  310.    end Wait_For_Completion;
  311.  
  312.    --------------------------------------
  313.    -- Wait_For_Completion_With_Timeout --
  314.    --------------------------------------
  315.  
  316.    --  This routine will lock Self_ID.
  317.  
  318.    --  This procedure waits for the entry call to
  319.    --  be served, with a timeout.  It tries to cancel the
  320.    --  call if the timeout expires before the call is served.
  321.  
  322.    --  If we wake up from the timed sleep operation here,
  323.    --  it may be for several possible reasons:
  324.  
  325.    --  1) The entry call is done being served.
  326.    --  2) There is an abort or priority change to be served.
  327.    --  3) The timeout has expired (Timedout = True)
  328.    --  4) There has been a spurious wakeup.
  329.  
  330.    --  Once the timeout has expired we may need to continue to wait if
  331.    --  the call is already being serviced. In that case, we want to go
  332.    --  back to sleep, but without any timeout. The variable Timedout is
  333.    --  used to control this. If the Timedout flag is set, we do not need
  334.    --  to STPO.Sleep with a timeout. We just sleep until we get a wakeup for
  335.    --  some status change.
  336.  
  337.    --  The original call may have become abortable after waking up.
  338.    --  We want to check Check_Pending_Actions_For_Entry_Call again
  339.    --  in any case.
  340.  
  341.    procedure Wait_For_Completion_With_Timeout
  342.      (Self_ID     : Task_ID;
  343.       Entry_Call  : Entry_Call_Link;
  344.       Wakeup_Time : Duration;
  345.       Mode        : Delay_Modes)
  346.    is
  347.       Timedout : Boolean := False;
  348.       Yielded  : Boolean := False;
  349.  
  350.       use type Ada.Exceptions.Exception_Id;
  351.  
  352.    begin
  353.       Initialization.Defer_Abort_Nestable (Self_ID);
  354.       STPO.Write_Lock (Self_ID);
  355.  
  356.       pragma Assert (Entry_Call.Self = Self_ID);
  357.       pragma Assert (Entry_Call.Mode = Timed_Call);
  358.       Self_ID.Common.State := Entry_Caller_Sleep;
  359.  
  360.       --  Looping is necessary in case the task wakes up early from the
  361.       --  timed sleep, due to a "spurious wakeup". Spurious wakeups are
  362.       --  a weakness of POSIX condition variables. A thread waiting for
  363.       --  a condition variable is allowed to wake up at any time, not just
  364.       --  when the condition is signaled. See the same loop in the
  365.       --  ordinary Wait_For_Completion, above.
  366.  
  367.       loop
  368.          Check_Pending_Actions_For_Entry_Call (Self_ID, Entry_Call);
  369.          exit when Entry_Call.State >= Done;
  370.  
  371.          STPO.Timed_Sleep (Self_ID, Wakeup_Time, Mode,
  372.            Entry_Caller_Sleep, Timedout, Yielded);
  373.  
  374.          if Timedout then
  375.  
  376.             --  Try to cancel the call (see Try_To_Cancel_Entry_Call for
  377.             --  corresponding code in the ATC case).
  378.  
  379.             Entry_Call.Cancellation_Attempted := True;
  380.  
  381.             if Self_ID.Pending_ATC_Level >= Entry_Call.Level then
  382.                Self_ID.Pending_ATC_Level := Entry_Call.Level - 1;
  383.             end if;
  384.  
  385.             --  The following loop is the same as the loop and exit code
  386.             --  from the ordinary Wait_For_Completion. If we get here, we
  387.             --  have timed out but we need to keep waiting until the call
  388.             --  has actually completed or been cancelled successfully.
  389.  
  390.             loop
  391.                Check_Pending_Actions_For_Entry_Call (Self_ID, Entry_Call);
  392.                exit when Entry_Call.State >= Done;
  393.                STPO.Sleep (Self_ID, Entry_Caller_Sleep);
  394.             end loop;
  395.  
  396.             Self_ID.Common.State := Runnable;
  397.             Utilities.Exit_One_ATC_Level (Self_ID);
  398.  
  399.             STPO.Unlock (Self_ID);
  400.  
  401.             if Entry_Call.State = Cancelled then
  402.                Initialization.Undefer_Abort_Nestable (Self_ID);
  403.             else
  404.                --  ????
  405.  
  406.                Initialization.Undefer_Abort_Nestable (Self_ID);
  407.  
  408.                --  Ideally, abort should no longer be deferred at this
  409.                --  point, so we should be able to call Check_Exception.
  410.                --  The loop below should be considered temporary,
  411.                --  to work around the possiblility that abort may be
  412.                --  deferred more than one level deep.
  413.  
  414.                if Entry_Call.Exception_To_Raise /=
  415.                  Ada.Exceptions.Null_Id then
  416.  
  417.                   while Self_ID.Deferral_Level > 0 loop
  418.                      Initialization.Undefer_Abort_Nestable (Self_ID);
  419.                   end loop;
  420.  
  421.                   Entry_Calls.Check_Exception (Self_ID, Entry_Call);
  422.                end if;
  423.             end if;
  424.  
  425.             return;
  426.          end if;
  427.       end loop;
  428.  
  429.       --  This last part is the same as ordinary Wait_For_Completion,
  430.       --  and is only executed if the call completed without timing out.
  431.  
  432.       Self_ID.Common.State := Runnable;
  433.       Utilities.Exit_One_ATC_Level (Self_ID);
  434.       STPO.Unlock (Self_ID);
  435.  
  436.       Initialization.Undefer_Abort_Nestable (Self_ID);
  437.  
  438.       if not Yielded then
  439.          STPO.Yield;
  440.       end if;
  441.    end Wait_For_Completion_With_Timeout;
  442.  
  443.    --------------------------
  444.    -- Wait_Until_Abortable --
  445.    --------------------------
  446.  
  447.    --  Wait to start the abortable part of an async. select statement
  448.    --  until the trigger entry call becomes abortable.
  449.  
  450.    procedure Wait_Until_Abortable
  451.      (Self_ID   : Task_ID;
  452.       Call      : Entry_Call_Link)
  453.    is
  454.    begin
  455.       pragma Assert (Self_ID.ATC_Nesting_Level > 0);
  456.       pragma Assert (Call.Mode = Asynchronous_Call);
  457.  
  458.       STPO.Write_Lock (Self_ID);
  459.       Self_ID.Common.State := Entry_Caller_Sleep;
  460.  
  461.       loop
  462.          Check_Pending_Actions_For_Entry_Call (Self_ID, Call);
  463.          exit when Call.State >= Was_Abortable;
  464.          STPO.Sleep (Self_ID, Async_Select_Sleep);
  465.       end loop;
  466.  
  467.       Self_ID.Common.State := Runnable;
  468.       STPO.Unlock (Self_ID);
  469.    end Wait_Until_Abortable;
  470.  
  471.    --  It might seem that we should be holding the server's lock when
  472.    --  we test Call.State above.
  473.  
  474.    --  In an earlier version, the code above temporarily unlocked the
  475.    --  caller and locked the server just for checking Call.State.
  476.    --  The unlocking of the caller risked missing a wakeup
  477.    --  (an error) and locking the server had no apparent value.
  478.    --  We should not need the server's lock, since once Call.State
  479.    --  is set to Was_Abortable or beyond, it never goes back below
  480.    --  Was_Abortable until this task starts another entry call.
  481.  
  482.    --  ????
  483.    --  It seems that other calls to Lock_Server may also risk missing
  484.    --  wakeups.  We need to check that they do not have this problem.
  485.  
  486.    ---------------------------------------------
  487.    -- Poll_Base_Priority_Change_At_Entry_Call --
  488.    ---------------------------------------------
  489.  
  490.    --  A specialized version of Poll_Base_Priority_Change,
  491.    --  that does the optional entry queue reordering.
  492.  
  493.    procedure Poll_Base_Priority_Change_At_Entry_Call
  494.      (Self_ID    : Task_ID;
  495.       Entry_Call : Entry_Call_Link)
  496.    is
  497.    begin
  498.       if Initialization.Dynamic_Priority_Support
  499.         and then Self_ID.Pending_Priority_Change
  500.       then
  501.          --  Check for ceiling violations ???
  502.  
  503.          Self_ID.Pending_Priority_Change := False;
  504.  
  505.          if Self_ID.Common.Base_Priority = Self_ID.New_Base_Priority then
  506.             STPO.Unlock (Self_ID);
  507.             STPO.Yield;
  508.             STPO.Write_Lock (Self_ID);
  509.  
  510.          else
  511.             if Self_ID.Common.Base_Priority < Self_ID.New_Base_Priority then
  512.  
  513.                --  Raising priority
  514.  
  515.                Self_ID.Common.Base_Priority := Self_ID.New_Base_Priority;
  516.                STPO.Set_Priority (Self_ID, Self_ID.Common.Base_Priority);
  517.  
  518.             else
  519.                --  Lowering priority
  520.  
  521.                Self_ID.Common.Base_Priority := Self_ID.New_Base_Priority;
  522.                STPO.Set_Priority (Self_ID, Self_ID.Common.Base_Priority);
  523.                STPO.Unlock (Self_ID);
  524.                STPO.Yield;
  525.                STPO.Write_Lock (Self_ID);
  526.             end if;
  527.          end if;
  528.  
  529.          --  Requeue the entry call at the new priority.
  530.          --  We need to requeue even if the new priority is the same than
  531.          --  the previous (see ACVC cxd4006).
  532.  
  533.          STPO.Unlock (Self_ID);
  534.          Lock_Server (Entry_Call);
  535.          Queuing.Requeue_Call_With_New_Prio
  536.            (Entry_Call, STPO.Get_Priority (Self_ID));
  537.          Unlock_And_Update_Server (Self_ID, Entry_Call);
  538.          STPO.Write_Lock (Self_ID);
  539.       end if;
  540.    end Poll_Base_Priority_Change_At_Entry_Call;
  541.  
  542.    -----------------------------------------
  543.    -- Check_Pending_Actions_For_Entry_Call --
  544.    -----------------------------------------
  545.  
  546.    --  Call only with abort deferred and holding lock of Self_ID. This
  547.    --  is a bit of common code for all entry calls. The effect is to do
  548.    --  any deferred base priority change operation, in case some other
  549.    --  task called STPO.Set_Priority while the current task had abort deferred,
  550.    --  and to dequeue the call if the call has been aborted.
  551.  
  552.    procedure Check_Pending_Actions_For_Entry_Call
  553.      (Self_ID    : Task_ID;
  554.       Entry_Call : Entry_Call_Link)
  555.    is
  556.    begin
  557.       pragma Assert (Self_ID = Entry_Call.Self);
  558.  
  559.       Poll_Base_Priority_Change_At_Entry_Call (Self_ID, Entry_Call);
  560.  
  561.       if Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
  562.         and then Entry_Call.State = Now_Abortable
  563.       then
  564.          STPO.Unlock (Self_ID);
  565.          Lock_Server (Entry_Call);
  566.  
  567.          if Queuing.Onqueue (Entry_Call)
  568.            and then Entry_Call.State = Now_Abortable
  569.          then
  570.             Queuing.Dequeue_Call (Entry_Call);
  571.  
  572.             if Entry_Call.Cancellation_Attempted then
  573.                Entry_Call.State := Cancelled;
  574.             else
  575.                Entry_Call.State := Done;
  576.             end if;
  577.  
  578.             Unlock_And_Update_Server (Self_ID, Entry_Call);
  579.  
  580.          else
  581.             Unlock_Server (Entry_Call);
  582.          end if;
  583.  
  584.          STPO.Write_Lock (Self_ID);
  585.       end if;
  586.    end Check_Pending_Actions_For_Entry_Call;
  587.  
  588.    ------------------------------
  589.    -- Try_To_Cancel_Entry_Call --
  590.    ------------------------------
  591.  
  592.    --  This is used to implement the Cancel_Task_Entry_Call and
  593.    --  Cancel_Protected_Entry_Call.
  594.    --  Try to cancel async. entry call.
  595.    --  Effect includes Abort_To_Level and Wait_For_Completion.
  596.    --  Cancelled = True iff the cancelation was successful, i.e.,
  597.    --  the call was not Done before this call.
  598.    --  On return, the call is off-queue and the ATC level is reduced by one.
  599.  
  600.    procedure Try_To_Cancel_Entry_Call (Succeeded : out Boolean) is
  601.       Entry_Call : Entry_Call_Link;
  602.       Self_ID    : constant Task_ID := STPO.Self;
  603.  
  604.       use type Ada.Exceptions.Exception_Id;
  605.  
  606.    begin
  607.       Entry_Call := Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
  608.  
  609.       --  Experimentation has shown that abort is sometimes (but not
  610.       --  always) already deferred when Cancel_X_Entry_Call is called.
  611.       --  That may indicate an error. Find out what is going on. ???
  612.  
  613.       pragma Assert (Entry_Call.Mode = Asynchronous_Call);
  614.       pragma Assert (Self_ID = Self);
  615.  
  616.       Initialization.Defer_Abort_Nestable (Self_ID);
  617.       STPO.Write_Lock (Self_ID);
  618.       Entry_Call.Cancellation_Attempted := True;
  619.  
  620.       if Self_ID.Pending_ATC_Level >= Entry_Call.Level then
  621.          Self_ID.Pending_ATC_Level := Entry_Call.Level - 1;
  622.       end if;
  623.  
  624.       Entry_Calls.Wait_For_Completion (Self_ID, Entry_Call);
  625.       STPO.Unlock (Self_ID);
  626.       Succeeded := Entry_Call.State = Cancelled;
  627.  
  628.       if Succeeded then
  629.          Initialization.Undefer_Abort_Nestable (Self_ID);
  630.       else
  631.          --  ????
  632.  
  633.          Initialization.Undefer_Abort_Nestable (Self_ID);
  634.  
  635.          --  Ideally, abort should no longer be deferred at this
  636.          --  point, so we should be able to call Check_Exception.
  637.          --  The loop below should be considered temporary,
  638.          --  to work around the possiblility that abort may be deferred
  639.          --  more than one level deep.
  640.  
  641.          if Entry_Call.Exception_To_Raise /= Ada.Exceptions.Null_Id then
  642.             while Self_ID.Deferral_Level > 0 loop
  643.                System.Tasking.Initialization.Undefer_Abort_Nestable (Self_ID);
  644.             end loop;
  645.  
  646.             Entry_Calls.Check_Exception (Self_ID, Entry_Call);
  647.          end if;
  648.       end if;
  649.    end Try_To_Cancel_Entry_Call;
  650.  
  651.    --------------------
  652.    -- Reset_Priority --
  653.    --------------------
  654.  
  655.    --  Reset the priority of a task completing an accept statement to
  656.    --  the value it had before the call.
  657.  
  658.    procedure Reset_Priority
  659.      (Acceptor_Prev_Priority : Rendezvous_Priority;
  660.       Acceptor               : Task_ID) is
  661.    begin
  662.       if Acceptor_Prev_Priority /= Priority_Not_Boosted then
  663.          STPO.Set_Priority (Acceptor, Acceptor_Prev_Priority,
  664.            Loss_Of_Inheritance => True);
  665.       end if;
  666.    end Reset_Priority;
  667.  
  668.    --  ???
  669.    --  Check why we don't need any kind of lock to do this.
  670.    --  Do we limit this kind of "active" priority change to be done
  671.    --  by the task for itself only?
  672.  
  673.    ---------------------
  674.    -- Check_Exception --
  675.    ---------------------
  676.  
  677.    --  Raise any pending exception from the Entry_Call.
  678.  
  679.    --  This should be called at the end of every compiler interface
  680.    --  procedure that implements an entry call.
  681.  
  682.    --  In principle, the caller should not be abort-deferred (unless
  683.    --  the application program violates the Ada language rules by doing
  684.    --  entry calls from within protected operations -- an erroneous practice
  685.    --  apparently followed with success by some adventurous GNAT users).
  686.    --  Absolutely, the caller should not be holding any locks, or there
  687.    --  will be deadlock.
  688.  
  689.    procedure Check_Exception
  690.      (Self_ID    : Task_ID;
  691.       Entry_Call : Entry_Call_Link)
  692.    is
  693.       use type Ada.Exceptions.Exception_Id;
  694.  
  695.       procedure Internal_Raise (X : Ada.Exceptions.Exception_Id);
  696.       pragma Import (C, Internal_Raise, "__gnat_raise_with_msg");
  697.  
  698.       E : constant Ada.Exceptions.Exception_Id :=
  699.             Entry_Call.Exception_To_Raise;
  700.    begin
  701.       --  pragma Assert (Self_ID.Deferral_Level = 0);
  702.       --  The above may be useful for debugging, but the Florist packages
  703.       --  contain critical sections that defer abort and then do entry calls,
  704.       --  which causes the above Assert to trip.
  705.  
  706.       if E /= Ada.Exceptions.Null_Id then
  707.          Internal_Raise (E);
  708.       end if;
  709.    end Check_Exception;
  710.  
  711. end System.Tasking.Entry_Calls;
  712.