File : s-tasque.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                  GNAT 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 --         Copyright (C) 1992-2009, Free Software Foundation, Inc.          --
  10 --                                                                          --
  11 -- GNARL is free software; you can  redistribute it  and/or modify it under --
  12 -- terms of the  GNU General Public License as published  by the Free Soft- --
  13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
  14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  16 -- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
  17 --                                                                          --
  18 --                                                                          --
  19 --                                                                          --
  20 --                                                                          --
  21 --                                                                          --
  22 -- You should have received a copy of the GNU General Public License and    --
  23 -- a copy of the GCC Runtime Library Exception along with this program;     --
  24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
  25 -- <http://www.gnu.org/licenses/>.                                          --
  26 --                                                                          --
  27 -- GNARL was developed by the GNARL team at Florida State University.       --
  28 -- Extensive contributions were provided by Ada Core Technologies, Inc.     --
  29 --                                                                          --
  30 ------------------------------------------------------------------------------
  31 
  32 --  This version of the body implements queueing policy according to the policy
  33 --  specified by the pragma Queuing_Policy. When no such pragma is specified
  34 --  FIFO policy is used as default.
  35 
  36 with System.Task_Primitives.Operations;
  37 with System.Tasking.Initialization;
  38 with System.Parameters;
  39 
  40 package body System.Tasking.Queuing is
  41 
  42    use Parameters;
  43    use Task_Primitives.Operations;
  44    use Protected_Objects;
  45    use Protected_Objects.Entries;
  46 
  47    --  Entry Queues implemented as doubly linked list
  48 
  49    Queuing_Policy : Character;
  50    pragma Import (C, Queuing_Policy, "__gl_queuing_policy");
  51 
  52    Priority_Queuing : constant Boolean := Queuing_Policy = 'P';
  53 
  54    procedure Send_Program_Error
  55      (Self_ID    : Task_Id;
  56       Entry_Call : Entry_Call_Link);
  57    --  Raise Program_Error in the caller of the specified entry call
  58 
  59    function Check_Queue (E : Entry_Queue) return Boolean;
  60    --  Check the validity of E.
  61    --  Return True if E is valid, raise Assert_Failure if assertions are
  62    --  enabled and False otherwise.
  63 
  64    -----------------------------
  65    -- Broadcast_Program_Error --
  66    -----------------------------
  67 
  68    procedure Broadcast_Program_Error
  69      (Self_ID      : Task_Id;
  70       Object       : Protection_Entries_Access;
  71       Pending_Call : Entry_Call_Link;
  72       RTS_Locked   : Boolean := False)
  73    is
  74       Entry_Call : Entry_Call_Link;
  75    begin
  76       if Single_Lock and then not RTS_Locked then
  77          Lock_RTS;
  78       end if;
  79 
  80       if Pending_Call /= null then
  81          Send_Program_Error (Self_ID, Pending_Call);
  82       end if;
  83 
  84       for E in Object.Entry_Queues'Range loop
  85          Dequeue_Head (Object.Entry_Queues (E), Entry_Call);
  86 
  87          while Entry_Call /= null loop
  88             pragma Assert (Entry_Call.Mode /= Conditional_Call);
  89 
  90             Send_Program_Error (Self_ID, Entry_Call);
  91             Dequeue_Head (Object.Entry_Queues (E), Entry_Call);
  92          end loop;
  93       end loop;
  94 
  95       if Single_Lock and then not RTS_Locked then
  96          Unlock_RTS;
  97       end if;
  98    end Broadcast_Program_Error;
  99 
 100    -----------------
 101    -- Check_Queue --
 102    -----------------
 103 
 104    function Check_Queue (E : Entry_Queue) return Boolean is
 105       Valid   : Boolean := True;
 106       C, Prev : Entry_Call_Link;
 107 
 108    begin
 109       if E.Head = null then
 110          if E.Tail /= null then
 111             Valid := False;
 112             pragma Assert (Valid);
 113          end if;
 114       else
 115          if E.Tail = null
 116            or else E.Tail.Next /= E.Head
 117          then
 118             Valid := False;
 119             pragma Assert (Valid);
 120 
 121          else
 122             C := E.Head;
 123 
 124             loop
 125                Prev := C;
 126                C := C.Next;
 127 
 128                if C = null then
 129                   Valid := False;
 130                   pragma Assert (Valid);
 131                   exit;
 132                end if;
 133 
 134                if Prev /= C.Prev then
 135                   Valid := False;
 136                   pragma Assert (Valid);
 137                   exit;
 138                end if;
 139 
 140                exit when C = E.Head;
 141             end loop;
 142 
 143             if Prev /= E.Tail then
 144                Valid := False;
 145                pragma Assert (Valid);
 146             end if;
 147          end if;
 148       end if;
 149 
 150       return Valid;
 151    end Check_Queue;
 152 
 153    -------------------
 154    -- Count_Waiting --
 155    -------------------
 156 
 157    --  Return number of calls on the waiting queue of E
 158 
 159    function Count_Waiting (E : Entry_Queue) return Natural is
 160       Count   : Natural;
 161       Temp    : Entry_Call_Link;
 162 
 163    begin
 164       pragma Assert (Check_Queue (E));
 165 
 166       Count := 0;
 167 
 168       if E.Head /= null then
 169          Temp := E.Head;
 170 
 171          loop
 172             Count := Count + 1;
 173             exit when E.Tail = Temp;
 174             Temp := Temp.Next;
 175          end loop;
 176       end if;
 177 
 178       return Count;
 179    end Count_Waiting;
 180 
 181    -------------
 182    -- Dequeue --
 183    -------------
 184 
 185    --  Dequeue call from entry_queue E
 186 
 187    procedure Dequeue (E : in out Entry_Queue; Call : Entry_Call_Link) is
 188    begin
 189       pragma Assert (Check_Queue (E));
 190       pragma Assert (Call /= null);
 191 
 192       --  If empty queue, simply return
 193 
 194       if E.Head = null then
 195          return;
 196       end if;
 197 
 198       pragma Assert (Call.Prev /= null);
 199       pragma Assert (Call.Next /= null);
 200 
 201       Call.Prev.Next := Call.Next;
 202       Call.Next.Prev := Call.Prev;
 203 
 204       if E.Head = Call then
 205 
 206          --  Case of one element
 207 
 208          if E.Tail = Call then
 209             E.Head := null;
 210             E.Tail := null;
 211 
 212          --  More than one element
 213 
 214          else
 215             E.Head := Call.Next;
 216          end if;
 217 
 218       elsif E.Tail = Call then
 219          E.Tail := Call.Prev;
 220       end if;
 221 
 222       --  Successfully dequeued
 223 
 224       Call.Prev := null;
 225       Call.Next := null;
 226       pragma Assert (Check_Queue (E));
 227    end Dequeue;
 228 
 229    ------------------
 230    -- Dequeue_Call --
 231    ------------------
 232 
 233    procedure Dequeue_Call (Entry_Call : Entry_Call_Link) is
 234       Called_PO : Protection_Entries_Access;
 235 
 236    begin
 237       pragma Assert (Entry_Call /= null);
 238 
 239       if Entry_Call.Called_Task /= null then
 240          Dequeue
 241            (Entry_Call.Called_Task.Entry_Queues
 242              (Task_Entry_Index (Entry_Call.E)),
 243            Entry_Call);
 244 
 245       else
 246          Called_PO := To_Protection (Entry_Call.Called_PO);
 247          Dequeue (Called_PO.Entry_Queues
 248              (Protected_Entry_Index (Entry_Call.E)),
 249            Entry_Call);
 250       end if;
 251    end Dequeue_Call;
 252 
 253    ------------------
 254    -- Dequeue_Head --
 255    ------------------
 256 
 257    --  Remove and return the head of entry_queue E
 258 
 259    procedure Dequeue_Head
 260      (E    : in out Entry_Queue;
 261       Call : out Entry_Call_Link)
 262    is
 263       Temp : Entry_Call_Link;
 264 
 265    begin
 266       pragma Assert (Check_Queue (E));
 267       --  If empty queue, return null pointer
 268 
 269       if E.Head = null then
 270          Call := null;
 271          return;
 272       end if;
 273 
 274       Temp := E.Head;
 275 
 276       --  Case of one element
 277 
 278       if E.Head = E.Tail then
 279          E.Head := null;
 280          E.Tail := null;
 281 
 282       --  More than one element
 283 
 284       else
 285          pragma Assert (Temp /= null);
 286          pragma Assert (Temp.Next /= null);
 287          pragma Assert (Temp.Prev /= null);
 288 
 289          E.Head := Temp.Next;
 290          Temp.Prev.Next := Temp.Next;
 291          Temp.Next.Prev := Temp.Prev;
 292       end if;
 293 
 294       --  Successfully dequeued
 295 
 296       Temp.Prev := null;
 297       Temp.Next := null;
 298       Call := Temp;
 299       pragma Assert (Check_Queue (E));
 300    end Dequeue_Head;
 301 
 302    -------------
 303    -- Enqueue --
 304    -------------
 305 
 306    --  Enqueue call at the end of entry_queue E, for FIFO queuing policy.
 307    --  Enqueue call priority ordered, FIFO at same priority level, for
 308    --  Priority queuing policy.
 309 
 310    procedure Enqueue (E : in out Entry_Queue; Call : Entry_Call_Link) is
 311       Temp : Entry_Call_Link := E.Head;
 312 
 313    begin
 314       pragma Assert (Check_Queue (E));
 315       pragma Assert (Call /= null);
 316 
 317       --  Priority Queuing
 318 
 319       if Priority_Queuing then
 320          if Temp = null then
 321             Call.Prev := Call;
 322             Call.Next := Call;
 323             E.Head := Call;
 324             E.Tail := Call;
 325 
 326          else
 327             loop
 328                --  Find the entry that the new guy should precede
 329 
 330                exit when Call.Prio > Temp.Prio;
 331                Temp := Temp.Next;
 332 
 333                if Temp = E.Head then
 334                   Temp := null;
 335                   exit;
 336                end if;
 337             end loop;
 338 
 339             if Temp = null then
 340                --  Insert at tail
 341 
 342                Call.Prev := E.Tail;
 343                Call.Next := E.Head;
 344                E.Tail := Call;
 345 
 346             else
 347                Call.Prev := Temp.Prev;
 348                Call.Next := Temp;
 349 
 350                --  Insert at head
 351 
 352                if Temp = E.Head then
 353                   E.Head := Call;
 354                end if;
 355             end if;
 356 
 357             pragma Assert (Call.Prev /= null);
 358             pragma Assert (Call.Next /= null);
 359 
 360             Call.Prev.Next := Call;
 361             Call.Next.Prev := Call;
 362          end if;
 363 
 364          pragma Assert (Check_Queue (E));
 365          return;
 366       end if;
 367 
 368       --  FIFO Queuing
 369 
 370       if E.Head = null then
 371          E.Head := Call;
 372       else
 373          E.Tail.Next := Call;
 374          Call.Prev   := E.Tail;
 375       end if;
 376 
 377       E.Head.Prev := Call;
 378       E.Tail      := Call;
 379       Call.Next   := E.Head;
 380       pragma Assert (Check_Queue (E));
 381    end Enqueue;
 382 
 383    ------------------
 384    -- Enqueue_Call --
 385    ------------------
 386 
 387    procedure Enqueue_Call (Entry_Call : Entry_Call_Link) is
 388       Called_PO : Protection_Entries_Access;
 389 
 390    begin
 391       pragma Assert (Entry_Call /= null);
 392 
 393       if Entry_Call.Called_Task /= null then
 394          Enqueue
 395            (Entry_Call.Called_Task.Entry_Queues
 396               (Task_Entry_Index (Entry_Call.E)),
 397            Entry_Call);
 398 
 399       else
 400          Called_PO := To_Protection (Entry_Call.Called_PO);
 401          Enqueue (Called_PO.Entry_Queues
 402              (Protected_Entry_Index (Entry_Call.E)),
 403            Entry_Call);
 404       end if;
 405    end Enqueue_Call;
 406 
 407    ----------
 408    -- Head --
 409    ----------
 410 
 411    --  Return the head of entry_queue E
 412 
 413    function Head (E : Entry_Queue) return Entry_Call_Link is
 414    begin
 415       pragma Assert (Check_Queue (E));
 416       return E.Head;
 417    end Head;
 418 
 419    -------------
 420    -- Onqueue --
 421    -------------
 422 
 423    --  Return True if Call is on any entry_queue at all
 424 
 425    function Onqueue (Call : Entry_Call_Link) return Boolean is
 426    begin
 427       pragma Assert (Call /= null);
 428 
 429       --  Utilize the fact that every queue is circular, so if Call
 430       --  is on any queue at all, Call.Next must NOT be null.
 431 
 432       return Call.Next /= null;
 433    end Onqueue;
 434 
 435    --------------------------------
 436    -- Requeue_Call_With_New_Prio --
 437    --------------------------------
 438 
 439    procedure Requeue_Call_With_New_Prio
 440      (Entry_Call : Entry_Call_Link; Prio : System.Any_Priority) is
 441    begin
 442       pragma Assert (Entry_Call /= null);
 443 
 444       --  Perform a queue reordering only when the policy being used is the
 445       --  Priority Queuing.
 446 
 447       if Priority_Queuing then
 448          if Onqueue (Entry_Call) then
 449             Dequeue_Call (Entry_Call);
 450             Entry_Call.Prio := Prio;
 451             Enqueue_Call (Entry_Call);
 452          end if;
 453       end if;
 454    end Requeue_Call_With_New_Prio;
 455 
 456    ---------------------------------
 457    -- Select_Protected_Entry_Call --
 458    ---------------------------------
 459 
 460    --  Select an entry of a protected object. Selection depends on the
 461    --  queuing policy being used.
 462 
 463    procedure Select_Protected_Entry_Call
 464      (Self_ID : Task_Id;
 465       Object  : Protection_Entries_Access;
 466       Call    : out Entry_Call_Link)
 467    is
 468       Entry_Call  : Entry_Call_Link;
 469       Temp_Call   : Entry_Call_Link;
 470       Entry_Index : Protected_Entry_Index := Null_Entry; -- stop warning
 471 
 472    begin
 473       Entry_Call := null;
 474 
 475       begin
 476          --  Priority queuing case
 477 
 478          if Priority_Queuing then
 479             for J in Object.Entry_Queues'Range loop
 480                Temp_Call := Head (Object.Entry_Queues (J));
 481 
 482                if Temp_Call /= null
 483                  and then
 484                    Object.Entry_Bodies
 485                      (Object.Find_Body_Index
 486                        (Object.Compiler_Info, J)).
 487                           Barrier (Object.Compiler_Info, J)
 488                then
 489                   if Entry_Call = null
 490                     or else Entry_Call.Prio < Temp_Call.Prio
 491                   then
 492                      Entry_Call := Temp_Call;
 493                      Entry_Index := J;
 494                   end if;
 495                end if;
 496             end loop;
 497 
 498          --  FIFO queueing case
 499 
 500          else
 501             for J in Object.Entry_Queues'Range loop
 502                Temp_Call := Head (Object.Entry_Queues (J));
 503 
 504                if Temp_Call /= null
 505                  and then
 506                    Object.Entry_Bodies
 507                      (Object.Find_Body_Index
 508                        (Object.Compiler_Info, J)).
 509                           Barrier (Object.Compiler_Info, J)
 510                then
 511                   Entry_Call := Temp_Call;
 512                   Entry_Index := J;
 513                   exit;
 514                end if;
 515             end loop;
 516          end if;
 517 
 518       exception
 519          when others =>
 520             Broadcast_Program_Error (Self_ID, Object, null);
 521       end;
 522 
 523       --  If a call was selected, dequeue it and return it for service
 524 
 525       if Entry_Call /= null then
 526          Temp_Call := Entry_Call;
 527          Dequeue_Head (Object.Entry_Queues (Entry_Index), Entry_Call);
 528          pragma Assert (Temp_Call = Entry_Call);
 529       end if;
 530 
 531       Call := Entry_Call;
 532    end Select_Protected_Entry_Call;
 533 
 534    ----------------------------
 535    -- Select_Task_Entry_Call --
 536    ----------------------------
 537 
 538    --  Select an entry for rendezvous. Selection depends on the queuing policy
 539    --  being used.
 540 
 541    procedure Select_Task_Entry_Call
 542      (Acceptor         : Task_Id;
 543       Open_Accepts     : Accept_List_Access;
 544       Call             : out Entry_Call_Link;
 545       Selection        : out Select_Index;
 546       Open_Alternative : out Boolean)
 547    is
 548       Entry_Call  : Entry_Call_Link;
 549       Temp_Call   : Entry_Call_Link;
 550       Entry_Index : Task_Entry_Index := Task_Entry_Index'First;
 551       Temp_Entry  : Task_Entry_Index;
 552 
 553    begin
 554       Open_Alternative := False;
 555       Entry_Call       := null;
 556       Selection        := No_Rendezvous;
 557 
 558       if Priority_Queuing then
 559          --  Priority queueing case
 560 
 561          for J in Open_Accepts'Range loop
 562             Temp_Entry := Open_Accepts (J).S;
 563 
 564             if Temp_Entry /= Null_Task_Entry then
 565                Open_Alternative := True;
 566                Temp_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
 567 
 568                if Temp_Call /= null
 569                  and then (Entry_Call = null
 570                    or else Entry_Call.Prio < Temp_Call.Prio)
 571                then
 572                   Entry_Call  := Head (Acceptor.Entry_Queues (Temp_Entry));
 573                   Entry_Index := Temp_Entry;
 574                   Selection := J;
 575                end if;
 576             end if;
 577          end loop;
 578 
 579       else
 580          --  FIFO Queuing case
 581 
 582          for J in Open_Accepts'Range loop
 583             Temp_Entry := Open_Accepts (J).S;
 584 
 585             if Temp_Entry /= Null_Task_Entry then
 586                Open_Alternative := True;
 587                Temp_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
 588 
 589                if Temp_Call /= null then
 590                   Entry_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
 591                   Entry_Index := Temp_Entry;
 592                   Selection := J;
 593                   exit;
 594                end if;
 595             end if;
 596          end loop;
 597       end if;
 598 
 599       if Entry_Call /= null then
 600          Dequeue_Head (Acceptor.Entry_Queues (Entry_Index), Entry_Call);
 601 
 602          --  Guard is open
 603       end if;
 604 
 605       Call := Entry_Call;
 606    end Select_Task_Entry_Call;
 607 
 608    ------------------------
 609    -- Send_Program_Error --
 610    ------------------------
 611 
 612    procedure Send_Program_Error
 613      (Self_ID    : Task_Id;
 614       Entry_Call : Entry_Call_Link)
 615    is
 616       Caller : Task_Id;
 617    begin
 618       Caller := Entry_Call.Self;
 619       Entry_Call.Exception_To_Raise := Program_Error'Identity;
 620       Write_Lock (Caller);
 621       Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
 622       Unlock (Caller);
 623    end Send_Program_Error;
 624 
 625 end System.Tasking.Queuing;