1 ------------------------------------------------------------------------------
3 -- GNAT 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 --
9 -- Copyright (C) 1992-2024, Free Software Foundation, Inc. --
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. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
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/>. --
27 -- GNARL was developed by the GNARL team at Florida State University. --
28 -- Extensive contributions were provided by Ada Core Technologies, Inc. --
30 ------------------------------------------------------------------------------
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.
36 with System
.Task_Primitives
.Operations
;
37 with System
.Tasking
.Initialization
;
39 package body System
.Tasking
.Queuing
is
41 use Task_Primitives
.Operations
;
42 use Protected_Objects
;
43 use Protected_Objects
.Entries
;
45 -- Entry Queues implemented as doubly linked list
47 Queuing_Policy
: constant Character;
48 pragma Import
(C
, Queuing_Policy
, "__gl_queuing_policy");
50 Priority_Queuing
: constant Boolean := Queuing_Policy
= 'P';
52 procedure Send_Program_Error
54 Entry_Call
: Entry_Call_Link
);
55 -- Raise Program_Error in the caller of the specified entry call
57 function Check_Queue
(E
: Entry_Queue
) return Boolean;
58 -- Check the validity of E.
59 -- Return True if E is valid, raise Assert_Failure if assertions are
60 -- enabled and False otherwise.
62 -----------------------------
63 -- Broadcast_Program_Error --
64 -----------------------------
66 procedure Broadcast_Program_Error
68 Object
: Protection_Entries_Access
;
69 Pending_Call
: Entry_Call_Link
)
71 Entry_Call
: Entry_Call_Link
;
73 if Pending_Call
/= null then
74 Send_Program_Error
(Self_ID
, Pending_Call
);
77 for E
in Object
.Entry_Queues
'Range loop
78 Dequeue_Head
(Object
.Entry_Queues
(E
), Entry_Call
);
80 while Entry_Call
/= null loop
81 pragma Assert
(Entry_Call
.Mode
/= Conditional_Call
);
83 Send_Program_Error
(Self_ID
, Entry_Call
);
84 Dequeue_Head
(Object
.Entry_Queues
(E
), Entry_Call
);
87 end Broadcast_Program_Error
;
93 function Check_Queue
(E
: Entry_Queue
) return Boolean is
94 Valid
: Boolean := True;
95 C
, Prev
: Entry_Call_Link
;
99 if E
.Tail
/= null then
101 pragma Assert
(Valid
);
105 or else E
.Tail
.Next
/= E
.Head
108 pragma Assert
(Valid
);
119 pragma Assert
(Valid
);
123 if Prev
/= C
.Prev
then
125 pragma Assert
(Valid
);
129 exit when C
= E
.Head
;
132 if Prev
/= E
.Tail
then
134 pragma Assert
(Valid
);
146 -- Return number of calls on the waiting queue of E
148 function Count_Waiting
(E
: Entry_Queue
) return Natural is
150 Temp
: Entry_Call_Link
;
153 pragma Assert
(Check_Queue
(E
));
157 if E
.Head
/= null then
162 exit when E
.Tail
= Temp
;
174 -- Dequeue call from entry_queue E
176 procedure Dequeue
(E
: in out Entry_Queue
; Call
: Entry_Call_Link
) is
178 pragma Assert
(Check_Queue
(E
));
179 pragma Assert
(Call
/= null);
181 -- If empty queue, simply return
183 if E
.Head
= null then
187 pragma Assert
(Call
.Prev
/= null);
188 pragma Assert
(Call
.Next
/= null);
190 Call
.Prev
.Next
:= Call
.Next
;
191 Call
.Next
.Prev
:= Call
.Prev
;
193 if E
.Head
= Call
then
195 -- Case of one element
197 if E
.Tail
= Call
then
201 -- More than one element
207 elsif E
.Tail
= Call
then
211 -- Successfully dequeued
215 pragma Assert
(Check_Queue
(E
));
222 procedure Dequeue_Call
(Entry_Call
: Entry_Call_Link
) is
223 Called_PO
: Protection_Entries_Access
;
226 pragma Assert
(Entry_Call
/= null);
228 if Entry_Call
.Called_Task
/= null then
230 (Entry_Call
.Called_Task
.Entry_Queues
231 (Task_Entry_Index
(Entry_Call
.E
)),
235 Called_PO
:= To_Protection
(Entry_Call
.Called_PO
);
236 Dequeue
(Called_PO
.Entry_Queues
237 (Protected_Entry_Index
(Entry_Call
.E
)),
246 -- Remove and return the head of entry_queue E
248 procedure Dequeue_Head
249 (E
: in out Entry_Queue
;
250 Call
: out Entry_Call_Link
)
252 Temp
: Entry_Call_Link
;
255 pragma Assert
(Check_Queue
(E
));
256 -- If empty queue, return null pointer
258 if E
.Head
= null then
265 -- Case of one element
267 if E
.Head
= E
.Tail
then
271 -- More than one element
274 pragma Assert
(Temp
/= null);
275 pragma Assert
(Temp
.Next
/= null);
276 pragma Assert
(Temp
.Prev
/= null);
279 Temp
.Prev
.Next
:= Temp
.Next
;
280 Temp
.Next
.Prev
:= Temp
.Prev
;
283 -- Successfully dequeued
288 pragma Assert
(Check_Queue
(E
));
295 -- Enqueue call at the end of entry_queue E, for FIFO queuing policy.
296 -- Enqueue call priority ordered, FIFO at same priority level, for
297 -- Priority queuing policy.
299 procedure Enqueue
(E
: in out Entry_Queue
; Call
: Entry_Call_Link
) is
300 Temp
: Entry_Call_Link
:= E
.Head
;
303 pragma Assert
(Check_Queue
(E
));
304 pragma Assert
(Call
/= null);
308 if Priority_Queuing
then
317 -- Find the entry that the new guy should precede
319 exit when Call
.Prio
> Temp
.Prio
;
322 if Temp
= E
.Head
then
336 Call
.Prev
:= Temp
.Prev
;
341 if Temp
= E
.Head
then
346 pragma Assert
(Call
.Prev
/= null);
347 pragma Assert
(Call
.Next
/= null);
349 Call
.Prev
.Next
:= Call
;
350 Call
.Next
.Prev
:= Call
;
353 pragma Assert
(Check_Queue
(E
));
359 if E
.Head
= null then
369 pragma Assert
(Check_Queue
(E
));
376 procedure Enqueue_Call
(Entry_Call
: Entry_Call_Link
) is
377 Called_PO
: Protection_Entries_Access
;
380 pragma Assert
(Entry_Call
/= null);
382 if Entry_Call
.Called_Task
/= null then
384 (Entry_Call
.Called_Task
.Entry_Queues
385 (Task_Entry_Index
(Entry_Call
.E
)),
389 Called_PO
:= To_Protection
(Entry_Call
.Called_PO
);
390 Enqueue
(Called_PO
.Entry_Queues
391 (Protected_Entry_Index
(Entry_Call
.E
)),
400 -- Return the head of entry_queue E
402 function Head
(E
: Entry_Queue
) return Entry_Call_Link
is
404 pragma Assert
(Check_Queue
(E
));
412 -- Return True if Call is on any entry_queue at all
414 function Onqueue
(Call
: Entry_Call_Link
) return Boolean is
416 pragma Assert
(Call
/= null);
418 -- Utilize the fact that every queue is circular, so if Call
419 -- is on any queue at all, Call.Next must NOT be null.
421 return Call
.Next
/= null;
424 --------------------------------
425 -- Requeue_Call_With_New_Prio --
426 --------------------------------
428 procedure Requeue_Call_With_New_Prio
429 (Entry_Call
: Entry_Call_Link
; Prio
: System
.Any_Priority
) is
431 pragma Assert
(Entry_Call
/= null);
433 -- Perform a queue reordering only when the policy being used is the
436 if Priority_Queuing
then
437 if Onqueue
(Entry_Call
) then
438 Dequeue_Call
(Entry_Call
);
439 Entry_Call
.Prio
:= Prio
;
440 Enqueue_Call
(Entry_Call
);
443 end Requeue_Call_With_New_Prio
;
445 ---------------------------------
446 -- Select_Protected_Entry_Call --
447 ---------------------------------
449 -- Select an entry of a protected object. Selection depends on the
450 -- queuing policy being used.
452 procedure Select_Protected_Entry_Call
454 Object
: Protection_Entries_Access
;
455 Call
: out Entry_Call_Link
)
457 Entry_Call
: Entry_Call_Link
;
458 Temp_Call
: Entry_Call_Link
;
459 Entry_Index
: Protected_Entry_Index
:= Null_Entry
; -- stop warning
465 -- Priority queuing case
467 if Priority_Queuing
then
468 for J
in Object
.Entry_Queues
'Range loop
469 Temp_Call
:= Head
(Object
.Entry_Queues
(J
));
474 (Object
.Find_Body_Index
475 (Object
.Compiler_Info
, J
)).
476 Barrier
(Object
.Compiler_Info
, J
)
479 or else Entry_Call
.Prio
< Temp_Call
.Prio
481 Entry_Call
:= Temp_Call
;
487 -- FIFO queueing case
490 for J
in Object
.Entry_Queues
'Range loop
491 Temp_Call
:= Head
(Object
.Entry_Queues
(J
));
496 (Object
.Find_Body_Index
497 (Object
.Compiler_Info
, J
)).
498 Barrier
(Object
.Compiler_Info
, J
)
500 Entry_Call
:= Temp_Call
;
509 Broadcast_Program_Error
(Self_ID
, Object
, null);
512 -- If a call was selected, dequeue it and return it for service
514 if Entry_Call
/= null then
515 Temp_Call
:= Entry_Call
;
516 Dequeue_Head
(Object
.Entry_Queues
(Entry_Index
), Entry_Call
);
517 pragma Assert
(Temp_Call
= Entry_Call
);
521 end Select_Protected_Entry_Call
;
523 ----------------------------
524 -- Select_Task_Entry_Call --
525 ----------------------------
527 -- Select an entry for rendezvous. Selection depends on the queuing policy
530 procedure Select_Task_Entry_Call
532 Open_Accepts
: Accept_List_Access
;
533 Call
: out Entry_Call_Link
;
534 Selection
: out Select_Index
;
535 Open_Alternative
: out Boolean)
537 Entry_Call
: Entry_Call_Link
;
538 Temp_Call
: Entry_Call_Link
;
539 Entry_Index
: Task_Entry_Index
:= Task_Entry_Index
'First;
540 Temp_Entry
: Task_Entry_Index
;
543 Open_Alternative
:= False;
545 Selection
:= No_Rendezvous
;
547 if Priority_Queuing
then
548 -- Priority queueing case
550 for J
in Open_Accepts
'Range loop
551 Temp_Entry
:= Open_Accepts
(J
).S
;
553 if Temp_Entry
/= Null_Task_Entry
then
554 Open_Alternative
:= True;
555 Temp_Call
:= Head
(Acceptor
.Entry_Queues
(Temp_Entry
));
558 and then (Entry_Call
= null
559 or else Entry_Call
.Prio
< Temp_Call
.Prio
)
561 Entry_Call
:= Head
(Acceptor
.Entry_Queues
(Temp_Entry
));
562 Entry_Index
:= Temp_Entry
;
571 for J
in Open_Accepts
'Range loop
572 Temp_Entry
:= Open_Accepts
(J
).S
;
574 if Temp_Entry
/= Null_Task_Entry
then
575 Open_Alternative
:= True;
576 Temp_Call
:= Head
(Acceptor
.Entry_Queues
(Temp_Entry
));
578 if Temp_Call
/= null then
579 Entry_Call
:= Head
(Acceptor
.Entry_Queues
(Temp_Entry
));
580 Entry_Index
:= Temp_Entry
;
588 if Entry_Call
/= null then
589 Dequeue_Head
(Acceptor
.Entry_Queues
(Entry_Index
), Entry_Call
);
595 end Select_Task_Entry_Call
;
597 ------------------------
598 -- Send_Program_Error --
599 ------------------------
601 procedure Send_Program_Error
603 Entry_Call
: Entry_Call_Link
)
607 Caller
:= Entry_Call
.Self
;
608 Entry_Call
.Exception_To_Raise
:= Program_Error
'Identity;
610 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
612 end Send_Program_Error
;
614 end System
.Tasking
.Queuing
;