1 ------------------------------------------------------------------------------
3 -- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
5 -- S Y S T E M . T A S K I N G . Q U E U I N G --
11 -- Copyright (C) 1991-2001, Florida State University --
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. --
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. --
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). --
35 ------------------------------------------------------------------------------
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.
41 with System.Task_Primitives.Operations;
42 -- used for Write_Lock
45 with System.Tasking.Initialization;
46 -- used for Wakeup_Entry_Caller
48 package body System.Tasking.Queuing is
50 use System.Task_Primitives.Operations;
51 use System.Tasking.Protected_Objects;
52 use System.Tasking.Protected_Objects.Entries;
54 procedure Wakeup_Entry_Caller
56 Entry_Call : Entry_Call_Link;
57 New_State : Entry_Call_State)
58 renames Initialization.Wakeup_Entry_Caller;
60 -- Entry Queues implemented as doubly linked list.
62 Queuing_Policy : Character;
63 pragma Import (C, Queuing_Policy, "__gl_queuing_policy");
65 Priority_Queuing : constant Boolean := Queuing_Policy = 'P';
67 procedure Send_Program_Error
69 Entry_Call : Entry_Call_Link);
70 -- Raise Program_Error in the caller of the specified entry call
72 function Check_Queue (E : Entry_Queue) return Boolean;
73 -- Check the validity of E.
74 -- Return True if E is valid, raise Assert_Failure if assertions are
75 -- enabled and False otherwise.
77 -----------------------------
78 -- Broadcast_Program_Error --
79 -----------------------------
81 procedure Broadcast_Program_Error
83 Object : Protection_Entries_Access;
84 Pending_Call : Entry_Call_Link)
86 Entry_Call : Entry_Call_Link;
89 if Pending_Call /= null then
90 Send_Program_Error (Self_ID, Pending_Call);
93 for E in Object.Entry_Queues'Range loop
94 Dequeue_Head (Object.Entry_Queues (E), Entry_Call);
96 while Entry_Call /= null loop
97 pragma Assert (Entry_Call.Mode /= Conditional_Call);
99 Send_Program_Error (Self_ID, Entry_Call);
100 Dequeue_Head (Object.Entry_Queues (E), Entry_Call);
103 end Broadcast_Program_Error;
109 function Check_Queue (E : Entry_Queue) return Boolean is
110 Valid : Boolean := True;
111 C, Prev : Entry_Call_Link;
114 if E.Head = null then
115 if E.Tail /= null then
117 pragma Assert (Valid);
121 or else E.Tail.Next /= E.Head
124 pragma Assert (Valid);
135 pragma Assert (Valid);
139 if Prev /= C.Prev then
141 pragma Assert (Valid);
145 exit when C = E.Head;
148 if Prev /= E.Tail then
150 pragma Assert (Valid);
162 -- Return number of calls on the waiting queue of E
164 function Count_Waiting (E : in Entry_Queue) return Natural is
166 Temp : Entry_Call_Link;
169 pragma Assert (Check_Queue (E));
173 if E.Head /= null then
178 exit when E.Tail = Temp;
190 -- Dequeue call from entry_queue E
192 procedure Dequeue (E : in out Entry_Queue; Call : Entry_Call_Link) is
194 pragma Assert (Check_Queue (E));
195 pragma Assert (Call /= null);
197 -- If empty queue, simply return
199 if E.Head = null then
203 pragma Assert (Call.Prev /= null);
204 pragma Assert (Call.Next /= null);
206 Call.Prev.Next := Call.Next;
207 Call.Next.Prev := Call.Prev;
209 if E.Head = Call then
211 -- Case of one element
213 if E.Tail = Call then
217 -- More than one element
223 elsif E.Tail = Call then
227 -- Successfully dequeued
231 pragma Assert (Check_Queue (E));
238 procedure Dequeue_Call (Entry_Call : Entry_Call_Link) is
239 Called_PO : Protection_Entries_Access;
242 pragma Assert (Entry_Call /= null);
244 if Entry_Call.Called_Task /= null then
246 (Entry_Call.Called_Task.Entry_Queues
247 (Task_Entry_Index (Entry_Call.E)),
251 Called_PO := To_Protection (Entry_Call.Called_PO);
252 Dequeue (Called_PO.Entry_Queues
253 (Protected_Entry_Index (Entry_Call.E)),
262 -- Remove and return the head of entry_queue E
264 procedure Dequeue_Head
265 (E : in out Entry_Queue;
266 Call : out Entry_Call_Link)
268 Temp : Entry_Call_Link;
271 pragma Assert (Check_Queue (E));
272 -- If empty queue, return null pointer
274 if E.Head = null then
281 -- Case of one element
283 if E.Head = E.Tail then
287 -- More than one element
290 pragma Assert (Temp /= null);
291 pragma Assert (Temp.Next /= null);
292 pragma Assert (Temp.Prev /= null);
295 Temp.Prev.Next := Temp.Next;
296 Temp.Next.Prev := Temp.Prev;
299 -- Successfully dequeued
304 pragma Assert (Check_Queue (E));
311 -- Enqueue call at the end of entry_queue E, for FIFO queuing policy.
312 -- Enqueue call priority ordered, FIFO at same priority level, for
313 -- Priority queuing policy.
315 procedure Enqueue (E : in out Entry_Queue; Call : Entry_Call_Link) is
316 Temp : Entry_Call_Link := E.Head;
319 pragma Assert (Check_Queue (E));
320 pragma Assert (Call /= null);
324 if Priority_Queuing then
333 -- Find the entry that the new guy should precede
335 exit when Call.Prio > Temp.Prio;
338 if Temp = E.Head then
352 Call.Prev := Temp.Prev;
357 if Temp = E.Head then
362 pragma Assert (Call.Prev /= null);
363 pragma Assert (Call.Next /= null);
365 Call.Prev.Next := Call;
366 Call.Next.Prev := Call;
369 pragma Assert (Check_Queue (E));
375 if E.Head = null then
385 pragma Assert (Check_Queue (E));
392 procedure Enqueue_Call (Entry_Call : Entry_Call_Link) is
393 Called_PO : Protection_Entries_Access;
396 pragma Assert (Entry_Call /= null);
398 if Entry_Call.Called_Task /= null then
400 (Entry_Call.Called_Task.Entry_Queues
401 (Task_Entry_Index (Entry_Call.E)),
405 Called_PO := To_Protection (Entry_Call.Called_PO);
406 Enqueue (Called_PO.Entry_Queues
407 (Protected_Entry_Index (Entry_Call.E)),
416 -- Return the head of entry_queue E
418 function Head (E : in Entry_Queue) return Entry_Call_Link is
420 pragma Assert (Check_Queue (E));
428 -- Return True if Call is on any entry_queue at all
430 function Onqueue (Call : Entry_Call_Link) return Boolean is
432 pragma Assert (Call /= null);
434 -- Utilize the fact that every queue is circular, so if Call
435 -- is on any queue at all, Call.Next must NOT be null.
437 return Call.Next /= null;
440 --------------------------------
441 -- Requeue_Call_With_New_Prio --
442 --------------------------------
444 procedure Requeue_Call_With_New_Prio
445 (Entry_Call : Entry_Call_Link; Prio : System.Any_Priority) is
447 pragma Assert (Entry_Call /= null);
449 -- Perform a queue reordering only when the policy being used is the
452 if Priority_Queuing then
453 if Onqueue (Entry_Call) then
454 Dequeue_Call (Entry_Call);
455 Entry_Call.Prio := Prio;
456 Enqueue_Call (Entry_Call);
459 end Requeue_Call_With_New_Prio;
461 ---------------------------------
462 -- Select_Protected_Entry_Call --
463 ---------------------------------
465 -- Select an entry of a protected object. Selection depends on the
466 -- queuing policy being used.
468 procedure Select_Protected_Entry_Call
470 Object : Protection_Entries_Access;
471 Call : out Entry_Call_Link)
473 Entry_Call : Entry_Call_Link;
474 Temp_Call : Entry_Call_Link;
475 Entry_Index : Protected_Entry_Index;
481 if Priority_Queuing then
485 for J in Object.Entry_Queues'Range loop
486 Temp_Call := Head (Object.Entry_Queues (J));
488 if Temp_Call /= null and then
489 Object.Entry_Bodies (
490 Object.Find_Body_Index (Object.Compiler_Info, J)).
491 Barrier (Object.Compiler_Info, J)
493 if (Entry_Call = null or else
494 Entry_Call.Prio < Temp_Call.Prio)
496 Entry_Call := Temp_Call;
505 for J in Object.Entry_Queues'Range loop
506 Temp_Call := Head (Object.Entry_Queues (J));
508 if Temp_Call /= null and then
509 Object.Entry_Bodies (
510 Object.Find_Body_Index (Object.Compiler_Info, J)).
511 Barrier (Object.Compiler_Info, J)
513 Entry_Call := Temp_Call;
522 Broadcast_Program_Error (Self_ID, Object, null);
525 -- If a call was selected, dequeue it and return it for service.
527 if Entry_Call /= null then
528 Temp_Call := Entry_Call;
529 Dequeue_Head (Object.Entry_Queues (Entry_Index), Entry_Call);
530 pragma Assert (Temp_Call = Entry_Call);
534 end Select_Protected_Entry_Call;
536 ----------------------------
537 -- Select_Task_Entry_Call --
538 ----------------------------
540 -- Select an entry for rendezvous. Selection depends on the queuing policy
543 procedure Select_Task_Entry_Call
545 Open_Accepts : Accept_List_Access;
546 Call : out Entry_Call_Link;
547 Selection : out Select_Index;
548 Open_Alternative : out Boolean)
550 Entry_Call : Entry_Call_Link;
551 Temp_Call : Entry_Call_Link;
552 Entry_Index : Task_Entry_Index;
553 Temp_Entry : Task_Entry_Index;
556 Open_Alternative := False;
559 if Priority_Queuing then
563 for J in Open_Accepts'Range loop
564 Temp_Entry := Open_Accepts (J).S;
566 if Temp_Entry /= Null_Task_Entry then
567 Open_Alternative := True;
568 Temp_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
570 if Temp_Call /= null and then
571 (Entry_Call = null or else
572 Entry_Call.Prio < Temp_Call.Prio)
575 Entry_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
576 Entry_Index := Temp_Entry;
585 for J in Open_Accepts'Range loop
586 Temp_Entry := Open_Accepts (J).S;
588 if Temp_Entry /= Null_Task_Entry then
589 Open_Alternative := True;
590 Temp_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
592 if Temp_Call /= null then
593 Entry_Call := Head (Acceptor.Entry_Queues (Temp_Entry));
594 Entry_Index := Temp_Entry;
602 if Entry_Call = null then
603 Selection := No_Rendezvous;
606 Dequeue_Head (Acceptor.Entry_Queues (Entry_Index), Entry_Call);
612 end Select_Task_Entry_Call;
614 ------------------------
615 -- Send_Program_Error --
616 ------------------------
618 procedure Send_Program_Error
620 Entry_Call : Entry_Call_Link)
625 Caller := Entry_Call.Self;
626 Entry_Call.Exception_To_Raise := Program_Error'Identity;
628 Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
630 end Send_Program_Error;
632 end System.Tasking.Queuing;