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-taasde.adb < prev    next >
Text File  |  2000-07-19  |  14KB  |  373 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 . A S Y N C _ D E L A Y S          --
  6. --                                                                          --
  7. --                                  B o d y                                 --
  8. --                                                                          --
  9. --                             $Revision: 1.3 $
  10. --                                                                          --
  11. --           Copyright (C) 1998-2000 Ada Core Technologies, Inc.            --
  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. pragma Polling (Off);
  38. --  Turn off polling, we do not want ATC polling to take place during
  39. --  tasking operations. It causes infinite loops and other problems.
  40.  
  41. with Ada.Exceptions;
  42. --  Used for Raise_Exception
  43.  
  44. with System.Task_Primitives.Operations;
  45. --  Used for Write_Lock,
  46. --           Unlock,
  47. --           Self,
  48. --           Clock,
  49. --           Self,
  50. --           Timed_Sleep,
  51. --           Wakeup,
  52. --           Yield
  53.  
  54. with System.Tasking.Utilities;
  55. --  Used for Make_Independent
  56.  
  57. with System.Tasking.Initialization;
  58. --  Used for Defer_Abort
  59. --           Undefer_Abort
  60.  
  61. with System.Tasking.Debug;
  62. --  Used for Trace
  63.  
  64. with System.OS_Primitives;
  65. --  used for Max_Sensible_Delay
  66.  
  67. with Ada.Task_Identification;
  68. --  used for Task_ID type
  69.  
  70. with Unchecked_Conversion;
  71.  
  72. package body System.Tasking.Async_Delays is
  73.  
  74.    package STPO renames System.Task_Primitives.Operations;
  75.    package ST renames System.Tasking;
  76.    package STU renames System.Tasking.Utilities;
  77.    package STI renames System.Tasking.Initialization;
  78.    package OSP renames System.OS_Primitives;
  79.  
  80.    function To_System is new Unchecked_Conversion
  81.      (Ada.Task_Identification.Task_Id, Task_ID);
  82.  
  83.    Timer_Server_ID : ST.Task_ID;
  84.  
  85.    Timer_Attention : Boolean := False;
  86.    pragma Atomic (Timer_Attention);
  87.  
  88.    task Timer_Server is
  89.       pragma Interrupt_Priority (System.Any_Priority'Last);
  90.    end Timer_Server;
  91.  
  92.    --  The timer queue is a circular doubly linked list, ordered by absolute
  93.    --  wakeup time. The first item in the queue is Timer_Queue.Succ.
  94.    --  It is given a Resume_Time that is larger than any legitimate wakeup
  95.    --  time, so that the ordered insertion will always stop searching when it
  96.    --  gets back to the queue header block.
  97.  
  98.    Timer_Queue : aliased Delay_Block;
  99.  
  100.    ------------------
  101.    -- Time_Enqueue --
  102.    ------------------
  103.  
  104.    --  Allocate a queue element for the wakeup time T and put it in the
  105.    --  queue in wakeup time order.  Assume we are on an asynchronous
  106.    --  select statement with delay trigger.  Put the calling task to
  107.    --  sleep until either the delay expires or is cancelled.
  108.  
  109.    --  We use one entry call record for this delay, since we have
  110.    --  to increment the ATC nesting level, but since it is not a
  111.    --  real entry call we do not need to use any of the fields of
  112.    --  the call record.  The following code implements a subset of
  113.    --  the actions for the asynchronous case of Protected_Entry_Call,
  114.    --  much simplified since we know this never blocks, and does not
  115.    --  have the full semantics of a protected entry call.
  116.  
  117.    procedure Time_Enqueue
  118.      (T : Duration;
  119.       D : Delay_Block_Access)
  120.    is
  121.       Self_Id : constant Task_ID  := STPO.Self;
  122.       Q       : Delay_Block_Access;
  123.  
  124.       use type ST.Task_ID;
  125.       --  for visibility of operator "="
  126.  
  127.    begin
  128.       pragma Debug (Debug.Trace (Self_Id, "Async_Delay", 'P'));
  129.       pragma Assert (Self_Id.Deferral_Level = 1,
  130.         "async delay from within abort-deferred region");
  131.  
  132.       if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
  133.          Ada.Exceptions.Raise_Exception (Storage_Error'Identity,
  134.            "not enough ATC nesting levels");
  135.       end if;
  136.  
  137.       Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
  138.  
  139.       pragma Debug
  140.         (Debug.Trace (Self_Id, "ASD: entered ATC level: " &
  141.          ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
  142.  
  143.       D.Level := Self_Id.ATC_Nesting_Level;
  144.       D.Self_Id := Self_Id;
  145.       D.Resume_Time := T;
  146.  
  147.       STI.Defer_Abort (Self_Id);
  148.       STPO.Write_Lock (Timer_Server_ID);
  149.  
  150.       --  Previously, there was code here to dynamically create
  151.       --  the Timer_Server task, if one did not already exist.
  152.       --  That code had a timing window that could allow multiple
  153.       --  timer servers to be created. Luckily, the need for
  154.       --  postponing creation of the timer server should now be
  155.       --  gone, since this package will only be linked in if
  156.       --  there are calls to enqueue calls on the timer server.
  157.  
  158.       --  Insert D in the timer queue, at the position determined
  159.       --  by the wakeup time T.
  160.  
  161.       Q := Timer_Queue.Succ;
  162.  
  163.       while Q.Resume_Time < T loop
  164.          Q := Q.Succ;
  165.       end loop;
  166.  
  167.       --  Q is the block that has Resume_Time equal to or greater than
  168.       --  T. After the insertion we want Q to be the successor of D.
  169.  
  170.       D.Succ := Q;
  171.       D.Pred := Q.Pred;
  172.       D.Pred.Succ := D;
  173.       Q.Pred := D;
  174.  
  175.       --  If the new element became the head of the queue,
  176.       --  signal the Timer_Server to wake up.
  177.  
  178.       if Timer_Queue.Succ = D then
  179.          Timer_Attention := True;
  180.          STPO.Wakeup (Timer_Server_ID, ST.Timer_Server_Sleep);
  181.       end if;
  182.  
  183.       STPO.Unlock (Timer_Server_ID);
  184.       STI.Undefer_Abort (Self_Id);
  185.    end Time_Enqueue;
  186.  
  187.    ------------------
  188.    -- Timer_Server --
  189.    ------------------
  190.  
  191.    task body Timer_Server is
  192.       Next_Wakeup_Time : Duration := Duration'Last;
  193.       Timedout         : Boolean;
  194.       Yielded          : Boolean;
  195.       Now              : Duration;
  196.       Dequeued,
  197.       Tpred,
  198.       Tsucc            : Delay_Block_Access;
  199.       Dequeued_Task    : Task_ID;
  200.  
  201.       --  Initialize_Timer_Queue returns null, but has critical side-effects
  202.       --  of initializing the timer queue.
  203.  
  204.    begin
  205.       Timer_Server_ID := STPO.Self;
  206.       STU.Make_Independent;
  207.  
  208.       --  Initialize the timer queue to empty, and make the wakeup time of the
  209.       --  header node be larger than any real wakeup time we will ever use.
  210.  
  211.       loop
  212.          STI.Defer_Abort (Timer_Server_ID);
  213.          STPO.Write_Lock (Timer_Server_ID);
  214.  
  215.          --  The timer server needs to catch pending aborts after finalization
  216.          --  of library packages. If it doesn't poll for it, the server will
  217.          --  sometimes hang.
  218.  
  219.          if not Timer_Attention then
  220.             Timer_Server_ID.Common.State := ST.Timer_Server_Sleep;
  221.  
  222.             if Next_Wakeup_Time = Duration'Last then
  223.                Timer_Server_ID.User_State := 1;
  224.                Next_Wakeup_Time := STPO.Clock + OSP.Max_Sensible_Delay;
  225.  
  226.             else
  227.                Timer_Server_ID.User_State := 2;
  228.             end if;
  229.  
  230.             STPO.Timed_Sleep
  231.               (Timer_Server_ID, Next_Wakeup_Time,
  232.                OSP.Absolute_RT, ST.Timer_Server_Sleep,
  233.                Timedout, Yielded);
  234.             Timer_Server_ID.Common.State := ST.Runnable;
  235.          end if;
  236.  
  237.          --  Service all of the wakeup requests on the queue whose times have
  238.          --  been reached, and update Next_Wakeup_Time to next wakeup time
  239.          --  after that (the wakeup time of the head of the queue if any, else
  240.          --  a time far in the future).
  241.  
  242.          Timer_Server_ID.User_State := 3;
  243.          Timer_Attention := False;
  244.  
  245.          Now := STPO.Clock;
  246.  
  247.          while Timer_Queue.Succ.Resume_Time <= Now loop
  248.  
  249.             --  Dequeue the waiting task from the front of the queue.
  250.  
  251.             pragma Debug (System.Tasking.Debug.Trace
  252.               ("Timer service: waking up waiting task", 'E'));
  253.  
  254.             Dequeued := Timer_Queue.Succ;
  255.             Timer_Queue.Succ := Dequeued.Succ;
  256.             Dequeued.Succ.Pred := Dequeued.Pred;
  257.             Dequeued.Succ := Dequeued;
  258.             Dequeued.Pred := Dequeued;
  259.  
  260.             --  We want to abort the queued task to the level of the async.
  261.             --  select statement with the delay. To do that, we need to lock
  262.             --  the ATCB of that task, but to avoid deadlock we need to release
  263.             --  the lock of the Timer_Server. This leaves a window in which
  264.             --  another task might perform an enqueue or dequeue operation on
  265.             --  the timer queue, but that is OK because we always restart the
  266.             --  next iteration at the head of the queue.
  267.  
  268.             STPO.Unlock (Timer_Server_ID);
  269.             STPO.Write_Lock (Dequeued.Self_Id);
  270.             Dequeued_Task := Dequeued.Self_Id;
  271.             Dequeued.Timed_Out := True;
  272.             STI.Locked_Abort_To_Level
  273.               (Timer_Server_ID, Dequeued_Task, Dequeued.Level - 1);
  274.             STPO.Unlock (Dequeued_Task);
  275.             STPO.Write_Lock (Timer_Server_ID);
  276.          end loop;
  277.  
  278.          Next_Wakeup_Time := Timer_Queue.Succ.Resume_Time;
  279.  
  280.          --  Service returns the Next_Wakeup_Time.
  281.          --  The Next_Wakeup_Time is either an infinity (no delay request)
  282.          --  or the wakeup time of the queue head. This value is used for
  283.          --  an actual delay in this server.
  284.  
  285.          STPO.Unlock (Timer_Server_ID);
  286.          STI.Undefer_Abort (Timer_Server_ID);
  287.       end loop;
  288.    end Timer_Server;
  289.  
  290.    ---------------------------
  291.    -- Enqueue_Time_Duration --
  292.    ---------------------------
  293.  
  294.    function Enqueue_Duration
  295.      (T : in Duration;
  296.       D : Delay_Block_Access) return Boolean is
  297.    begin
  298.       if T <= 0.0 then
  299.          D.Timed_Out := True;
  300.          STPO.Yield;
  301.          return False;
  302.  
  303.       else
  304.          STI.Defer_Abort (STPO.Self);
  305.          Time_Enqueue
  306.            (STPO.Clock + Duration'Min (T, OSP.Max_Sensible_Delay), D);
  307.          return True;
  308.       end if;
  309.    end Enqueue_Duration;
  310.  
  311.    ------------------------
  312.    -- Cancel_Async_Delay --
  313.    ------------------------
  314.  
  315.    --  This should (only) be called from the compiler-generated cleanup routine
  316.    --  for an async. select statement with delay statement as trigger. The
  317.    --  effect should be to remove the delay from the timer queue, and exit one
  318.    --  ATC nesting level.
  319.    --  The usage and logic are similar to Cancel_Protected_Entry_Call, but
  320.    --  simplified because this is not a true entry call.
  321.  
  322.    procedure Cancel_Async_Delay (D : Delay_Block_Access) is
  323.       Dpred,
  324.       Dsucc : Delay_Block_Access;
  325.  
  326.    begin
  327.       --  Note that we mark the delay as being cancelled
  328.       --  using a level value that is reserved.
  329.  
  330.       --  make this operation idempotent
  331.  
  332.       if D.Level = ATC_Level_Infinity then
  333.          return;
  334.       end if;
  335.  
  336.       D.Level := ATC_Level_Infinity;
  337.  
  338.       --  remove self from timer queue
  339.  
  340.       STI.Defer_Abort_Nestable (D.Self_Id);
  341.       STPO.Write_Lock (Timer_Server_ID);
  342.       Dpred := D.Pred;
  343.       Dsucc := D.Succ;
  344.       Dpred.Succ := Dsucc;
  345.       Dsucc.Pred := Dpred;
  346.       D.Succ := D;
  347.       D.Pred := D;
  348.       STPO.Unlock (Timer_Server_ID);
  349.  
  350.       --  Note that the above deletion code is required to be
  351.       --  idempotent, since the block may have been dequeued
  352.       --  previously by the Timer_Server.
  353.  
  354.       --  leave the asynchronous select
  355.  
  356.       STPO.Write_Lock (D.Self_Id);
  357.       STU.Exit_One_ATC_Level (D.Self_Id);
  358.       STPO.Unlock (D.Self_Id);
  359.       STI.Undefer_Abort_Nestable (D.Self_Id);
  360.    end Cancel_Async_Delay;
  361.  
  362.    function Timed_Out (D : Delay_Block_Access) return Boolean is
  363.    begin
  364.       return D.Timed_Out;
  365.    end Timed_Out;
  366.  
  367. begin
  368.    Timer_Queue.Succ := Timer_Queue'Unchecked_Access;
  369.    Timer_Queue.Pred := Timer_Queue'Unchecked_Access;
  370.    Timer_Queue.Resume_Time := Duration'Last;
  371.    Timer_Server_ID := To_System (Timer_Server'Identity);
  372. end System.Tasking.Async_Delays;
  373.