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-tasque.adb < prev    next >
Text File  |  2000-07-19  |  16KB  |  544 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 . Q U E U I N G              --
  6. --                                                                          --
  7. --                                  B o d y                                 --
  8. --                                                                          --
  9. --                             $Revision: 1.35 $
  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 version of the body implements queueing policy according to the
  38. --  policy specified by the pragma Queuing_Policy. When no such pragma
  39. --  is specified FIFO policy is used as default.
  40.  
  41. with System.Task_Primitives.Operations;
  42. --  used for Write_Lock
  43. --           Unlock
  44.  
  45. with System.Tasking.Initialization;
  46. --  used for Wakeup_Entry_Caller
  47.  
  48. package body System.Tasking.Queuing is
  49.  
  50.    use System.Task_Primitives.Operations;
  51.    use System.Tasking.Protected_Objects;
  52.    use System.Tasking.Protected_Objects.Entries;
  53.  
  54.    procedure Wakeup_Entry_Caller
  55.      (Self_ID    : Task_ID;
  56.       Entry_Call : Entry_Call_Link;
  57.       New_State  : Entry_Call_State)
  58.      renames Initialization.Wakeup_Entry_Caller;
  59.  
  60.    --  Entry Queues implemented as doubly linked list.
  61.  
  62.    Queuing_Policy : Character;
  63.    pragma Import (C, Queuing_Policy, "__gl_queuing_policy");
  64.  
  65.    Priority_Queuing : constant Boolean := Queuing_Policy = 'P';
  66.  
  67.    procedure Send_Program_Error
  68.      (Self_ID    : Task_ID;
  69.       Entry_Call : Entry_Call_Link);
  70.    --  Raise Program_Error in the caller of the specified entry call
  71.  
  72.    ------------------------
  73.    -- Send_Program_Error --
  74.    ------------------------
  75.  
  76.    procedure Send_Program_Error
  77.      (Self_ID    : Task_ID;
  78.       Entry_Call : Entry_Call_Link)
  79.    is
  80.       Caller : Task_ID;
  81.  
  82.    begin
  83.       Caller := Entry_Call.Self;
  84.       Entry_Call.Exception_To_Raise := Program_Error'Identity;
  85.       Write_Lock (Caller);
  86.       Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
  87.       Unlock (Caller);
  88.    end Send_Program_Error;
  89.  
  90.    -----------------------------
  91.    -- Broadcast_Program_Error --
  92.    -----------------------------
  93.  
  94.    procedure Broadcast_Program_Error
  95.      (Self_ID      : Task_ID;
  96.       Object       : Protection_Entries_Access;
  97.       Pending_Call : Entry_Call_Link)
  98.    is
  99.       Entry_Call   : Entry_Call_Link;
  100.  
  101.    begin
  102.       if Pending_Call /= null then
  103.          Send_Program_Error (Self_ID, Pending_Call);
  104.       end if;
  105.  
  106.       for E in Object.Entry_Queues'Range loop
  107.          Dequeue_Head (Object.Entry_Queues (E), Entry_Call);
  108.  
  109.          while Entry_Call /= null loop
  110.             pragma Assert (Entry_Call.Mode /= Conditional_Call);
  111.  
  112.             Send_Program_Error (Self_ID, Entry_Call);
  113.             Dequeue_Head (Object.Entry_Queues (E), Entry_Call);
  114.          end loop;
  115.       end loop;
  116.    end Broadcast_Program_Error;
  117.  
  118.    -------------
  119.    -- Enqueue --
  120.    -------------
  121.  
  122.    --  Enqueue call at the end of entry_queue E, for FIFO queuing policy.
  123.    --  Enqueue call priority ordered, FIFO at same priority level, for
  124.    --  Priority queuing policy.
  125.  
  126.    procedure Enqueue (E : in out Entry_Queue; Call : Entry_Call_Link) is
  127.       Temp : Entry_Call_Link := E.Head;
  128.  
  129.    begin
  130.       --  Priority Queuing
  131.  
  132.       if Priority_Queuing then
  133.          if Temp = null then
  134.             Call.Prev := Call;
  135.             Call.Next := Call;
  136.             E.Head := Call;
  137.             E.Tail := Call;
  138.  
  139.          else
  140.             loop
  141.                --  Find the entry that the new guy should precede
  142.  
  143.                exit when Call.Prio > Temp.Prio;
  144.                Temp := Temp.Next;
  145.  
  146.                if Temp = E.Head then
  147.                   Temp := null;
  148.                   exit;
  149.                end if;
  150.             end loop;
  151.  
  152.             if Temp = null then
  153.                --  Insert at tail
  154.  
  155.                Call.Prev := E.Tail;
  156.                Call.Next := E.Head;
  157.                E.Tail := Call;
  158.  
  159.             else
  160.                Call.Prev := Temp.Prev;
  161.                Call.Next := Temp;
  162.  
  163.                --  Insert at head
  164.  
  165.                if Temp = E.Head then
  166.                   E.Head := Call;
  167.                end if;
  168.             end if;
  169.  
  170.             Call.Prev.Next := Call;
  171.             Call.Next.Prev := Call;
  172.          end if;
  173.  
  174.          return;
  175.       end if;
  176.  
  177.       --  Priority Queuing
  178.  
  179.       if E.Head = null then
  180.          E.Head := Call;
  181.  
  182.       else
  183.          E.Tail.Next := Call;
  184.          Call.Prev   := E.Tail;
  185.       end if;
  186.  
  187.       E.Head.Prev := Call;
  188.       E.Tail := Call;
  189.       Call.Next := E.Head;
  190.    end Enqueue;
  191.  
  192.    -------------
  193.    -- Dequeue --
  194.    -------------
  195.  
  196.    --  Dequeue call from entry_queue E
  197.  
  198.    procedure Dequeue (E : in out Entry_Queue; Call : Entry_Call_Link) is
  199.    begin
  200.       --  If empty queue, simply return
  201.  
  202.       if E.Head = null then
  203.          return;
  204.       end if;
  205.  
  206.       Call.Prev.Next := Call.Next;
  207.       Call.Next.Prev := Call.Prev;
  208.  
  209.       if E.Head = Call then
  210.  
  211.          --  Case of one element
  212.  
  213.          if E.Tail = Call then
  214.             E.Head := null;
  215.             E.Tail := null;
  216.  
  217.          --  More than one element
  218.  
  219.          else
  220.             E.Head := Call.Next;
  221.          end if;
  222.  
  223.       elsif E.Tail = Call then
  224.          E.Tail := Call.Prev;
  225.       end if;
  226.  
  227.       --  Successfully dequeued
  228.  
  229.       Call.Prev := null;
  230.       Call.Next := null;
  231.    end Dequeue;
  232.  
  233.    ----------
  234.    -- Head --
  235.    ----------
  236.  
  237.    --  Return the head of entry_queue E
  238.  
  239.    function Head (E : in Entry_Queue) return Entry_Call_Link is
  240.    begin
  241.       return E.Head;
  242.    end Head;
  243.  
  244.    ------------------
  245.    -- Dequeue_Head --
  246.    ------------------
  247.  
  248.    --  Remove and return the head of entry_queue E
  249.  
  250.    procedure Dequeue_Head
  251.      (E    : in out Entry_Queue;
  252.       Call : out Entry_Call_Link)
  253.    is
  254.       Temp : Entry_Call_Link;
  255.  
  256.    begin
  257.       --  If empty queue, return null pointer
  258.  
  259.       if E.Head = null then
  260.          Call := null;
  261.          return;
  262.       end if;
  263.  
  264.       Temp := E.Head;
  265.  
  266.       --  Case of one element
  267.  
  268.       if E.Head = E.Tail then
  269.          E.Head := null;
  270.          E.Tail := null;
  271.  
  272.       --  More than one element
  273.  
  274.       else
  275.          E.Head := Temp.Next;
  276.          Temp.Prev.Next := Temp.Next;
  277.          Temp.Next.Prev := Temp.Prev;
  278.       end if;
  279.  
  280.       --  Successfully dequeued
  281.  
  282.       Temp.Prev := null;
  283.       Temp.Next := null;
  284.       Call := Temp;
  285.    end Dequeue_Head;
  286.  
  287.    -------------
  288.    -- Onqueue --
  289.    -------------
  290.  
  291.    --  Return True if Call is on any entry_queue at all
  292.  
  293.    function Onqueue (Call : Entry_Call_Link) return Boolean is
  294.    begin
  295.       --  Utilize the fact that every queue is circular, so if Call
  296.       --  is on any queue at all, Call.Next must NOT be null.
  297.  
  298.       return Call.Next /= null;
  299.    end Onqueue;
  300.  
  301.    -------------------
  302.    -- Count_Waiting --
  303.    -------------------
  304.  
  305.    --  Return number of calls on the waiting queue of E
  306.  
  307.    function Count_Waiting (E : in Entry_Queue) return Natural is
  308.       Count   : Natural;
  309.       Temp    : Entry_Call_Link;
  310.  
  311.    begin
  312.       Count := 0;
  313.  
  314.       if E.Head /= null then
  315.          Temp := E.Head;
  316.  
  317.          loop
  318.             Count := Count + 1;
  319.             exit when E.Tail = Temp;
  320.             Temp := Temp.Next;
  321.          end loop;
  322.       end if;
  323.  
  324.       return Count;
  325.    end Count_Waiting;
  326.  
  327.    ----------------------------
  328.    -- Select_Task_Entry_Call --
  329.    ----------------------------
  330.  
  331.    --  Select an entry for rendezvous. Selection depends on the queuing policy
  332.    --  being used.
  333.  
  334.    procedure Select_Task_Entry_Call
  335.      (Acceptor         : Task_ID;
  336.       Open_Accepts     : Accept_List_Access;
  337.       Call             : out Entry_Call_Link;
  338.       Selection        : out Select_Index;
  339.       Open_Alternative : out Boolean)
  340.    is
  341.       Entry_Call  : Entry_Call_Link;
  342.       Temp_Call   : Entry_Call_Link;
  343.       Entry_Index : Task_Entry_Index;
  344.       Temp_Entry  : Task_Entry_Index;
  345.  
  346.    begin
  347.       Open_Alternative := False;
  348.       Entry_Call := null;
  349.  
  350.       if Priority_Queuing then
  351.  
  352.       --  Priority Queuing
  353.  
  354.          for J in Open_Accepts'Range loop
  355.             Temp_Entry := Open_Accepts (J).S;
  356.  
  357.             if Temp_Entry /= Null_Task_Entry then
  358.                Open_Alternative := True;
  359.                Temp_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
  360.  
  361.                if Temp_Call /= null and then
  362.                  (Entry_Call = null or else
  363.                   Entry_Call.Prio < Temp_Call.Prio)
  364.  
  365.                then
  366.                   Entry_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
  367.                   Entry_Index := Temp_Entry;
  368.                   Selection := J;
  369.                end if;
  370.             end if;
  371.          end loop;
  372.  
  373.       else
  374.          --  FIFO Queuing
  375.  
  376.          for J in Open_Accepts'Range loop
  377.             Temp_Entry := Open_Accepts (J).S;
  378.  
  379.             if Temp_Entry /= Null_Task_Entry then
  380.                Open_Alternative := True;
  381.                Temp_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
  382.  
  383.                if Temp_Call /= null then
  384.                   Entry_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
  385.                   Entry_Index := Temp_Entry;
  386.                   Selection := J;
  387.                   exit;
  388.                end if;
  389.             end if;
  390.          end loop;
  391.       end if;
  392.  
  393.       if Entry_Call = null then
  394.          Selection := No_Rendezvous;
  395.  
  396.       else
  397.          Dequeue_Head (Acceptor.Entry_Queues (Entry_Index), Entry_Call);
  398.  
  399.          --  Guard is open
  400.       end if;
  401.  
  402.       Call := Entry_Call;
  403.    end Select_Task_Entry_Call;
  404.  
  405.    ---------------------------------
  406.    -- Select_Protected_Entry_Call --
  407.    ---------------------------------
  408.  
  409.    --  Select an entry of a protected object. Selection depends on the
  410.    --  queuing policy being used.
  411.  
  412.    procedure Select_Protected_Entry_Call
  413.      (Self_ID : Task_ID;
  414.       Object  : Protection_Entries_Access;
  415.       Call    : out Entry_Call_Link)
  416.    is
  417.       Entry_Call  : Entry_Call_Link;
  418.       Temp_Call   : Entry_Call_Link;
  419.       Entry_Index : Protected_Entry_Index;
  420.  
  421.    begin
  422.       Entry_Call := null;
  423.  
  424.       begin
  425.          if Priority_Queuing then
  426.  
  427.             --  Priority queuing
  428.  
  429.             for J in Object.Entry_Queues'Range loop
  430.                Temp_Call := Head (Object.Entry_Queues (J));
  431.  
  432.                if Temp_Call /= null and then
  433.                  Object.Entry_Bodies (
  434.                    Object.Find_Body_Index (Object.Compiler_Info, J)).
  435.                      Barrier (Object.Compiler_Info, J)
  436.                then
  437.                   if (Entry_Call = null or else
  438.                     Entry_Call.Prio < Temp_Call.Prio)
  439.                   then
  440.                      Entry_Call := Temp_Call;
  441.                      Entry_Index := J;
  442.                   end if;
  443.                end if;
  444.             end loop;
  445.  
  446.          else
  447.  
  448.             --  FIFO queuing
  449.  
  450.             for J in Object.Entry_Queues'Range loop
  451.                Temp_Call := Head (Object.Entry_Queues (J));
  452.  
  453.                if Temp_Call /= null and then
  454.                  Object.Entry_Bodies (
  455.                    Object.Find_Body_Index (Object.Compiler_Info, J)).
  456.                      Barrier (Object.Compiler_Info, J)
  457.                then
  458.                   Entry_Call := Temp_Call;
  459.                   Entry_Index := J;
  460.                   exit;
  461.                end if;
  462.             end loop;
  463.          end if;
  464.  
  465.       exception
  466.          when others =>
  467.             Broadcast_Program_Error (Self_ID, Object, null);
  468.       end;
  469.  
  470.       --  If a call was selected, dequeue it and return it for service.
  471.  
  472.       if Entry_Call /= null then
  473.          Dequeue_Head (Object.Entry_Queues (Entry_Index), Entry_Call);
  474.       end if;
  475.  
  476.       Call := Entry_Call;
  477.    end Select_Protected_Entry_Call;
  478.  
  479.    ------------------
  480.    -- Enqueue_Call --
  481.    ------------------
  482.  
  483.    procedure Enqueue_Call (Entry_Call : Entry_Call_Link) is
  484.       Called_PO : Protection_Entries_Access;
  485.  
  486.    begin
  487.       if Entry_Call.Called_Task /= null then
  488.          Enqueue
  489.            (Entry_Call.Called_Task.Entry_Queues
  490.               (Task_Entry_Index (Entry_Call.E)),
  491.            Entry_Call);
  492.  
  493.       else
  494.          Called_PO := To_Protection (Entry_Call.Called_PO);
  495.          Enqueue (Called_PO.Entry_Queues
  496.              (Protected_Entry_Index (Entry_Call.E)),
  497.            Entry_Call);
  498.       end if;
  499.    end Enqueue_Call;
  500.  
  501.    ------------------
  502.    -- Dequeue_Call --
  503.    ------------------
  504.  
  505.    procedure Dequeue_Call (Entry_Call : Entry_Call_Link) is
  506.       Called_PO : Protection_Entries_Access;
  507.  
  508.    begin
  509.       if Entry_Call.Called_Task /= null then
  510.          Dequeue
  511.            (Entry_Call.Called_Task.Entry_Queues
  512.              (Task_Entry_Index (Entry_Call.E)),
  513.            Entry_Call);
  514.  
  515.       else
  516.          Called_PO := To_Protection (Entry_Call.Called_PO);
  517.          Dequeue (Called_PO.Entry_Queues
  518.              (Protected_Entry_Index (Entry_Call.E)),
  519.            Entry_Call);
  520.       end if;
  521.    end Dequeue_Call;
  522.  
  523.    --------------------------------
  524.    -- Requeue_Call_With_New_Prio --
  525.    --------------------------------
  526.  
  527.    procedure Requeue_Call_With_New_Prio
  528.      (Entry_Call : Entry_Call_Link; Prio : System.Any_Priority)
  529.    is
  530.    begin
  531.       --  Perform a queue reordering only when the policy being used is the
  532.       --  Priority Queuing.
  533.  
  534.       if Priority_Queuing then
  535.          if Onqueue (Entry_Call) then
  536.             Dequeue_Call (Entry_Call);
  537.             Entry_Call.Prio := Prio;
  538.             Enqueue_Call (Entry_Call);
  539.          end if;
  540.       end if;
  541.    end Requeue_Call_With_New_Prio;
  542.  
  543. end System.Tasking.Queuing;
  544.