PR c++/31187
[official-gcc.git] / gcc / ada / s-tpobop.adb
blobb8bfc9a3ef98b040e3bb3bff77de7a6c02adbdfd
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- S Y S T E M . T A S K I N G . P R O T E C T E D _ O B J E C T S . --
6 -- O P E R A T I O N S --
7 -- --
8 -- B o d y --
9 -- --
10 -- Copyright (C) 1998-2006, Free Software Foundation, Inc. --
11 -- --
12 -- GNARL is free software; you can redistribute it and/or modify it under --
13 -- terms of the GNU General Public License as published by the Free Soft- --
14 -- ware Foundation; either version 2, or (at your option) any later ver- --
15 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
18 -- for more details. You should have received a copy of the GNU General --
19 -- Public License distributed with GNARL; see file COPYING. If not, write --
20 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
21 -- Boston, MA 02110-1301, USA. --
22 -- --
23 -- As a special exception, if other files instantiate generics from this --
24 -- unit, or you link this unit with other files to produce an executable, --
25 -- this unit does not by itself cause the resulting executable to be --
26 -- covered by the GNU General Public License. This exception does not --
27 -- however invalidate any other reasons why the executable file might be --
28 -- covered by the GNU Public License. --
29 -- --
30 -- GNARL was developed by the GNARL team at Florida State University. --
31 -- Extensive contributions were provided by Ada Core Technologies, Inc. --
32 -- --
33 ------------------------------------------------------------------------------
35 -- This package contains all the extended primitives related to
36 -- Protected_Objects with entries.
38 -- The handling of protected objects with no entries is done in
39 -- System.Tasking.Protected_Objects, the simple routines for protected
40 -- objects with entries in System.Tasking.Protected_Objects.Entries.
42 -- The split between Entries and Operations is needed to break circular
43 -- dependencies inside the run time.
45 -- This package contains all primitives related to Protected_Objects.
46 -- Note: the compiler generates direct calls to this interface, via Rtsfind.
48 with System.Task_Primitives.Operations;
49 -- used for Initialize_Lock
50 -- Write_Lock
51 -- Unlock
52 -- Get_Priority
53 -- Wakeup
55 with System.Tasking.Entry_Calls;
56 -- used for Wait_For_Completion
57 -- Wait_Until_Abortable
58 -- Wait_For_Completion_With_Timeout
60 with System.Tasking.Initialization;
61 -- Used for Defer_Abort,
62 -- Undefer_Abort,
63 -- Change_Base_Priority
65 pragma Elaborate_All (System.Tasking.Initialization);
66 -- This insures that tasking is initialized if any protected objects are
67 -- created.
69 with System.Tasking.Queuing;
70 -- used for Enqueue
71 -- Broadcast_Program_Error
72 -- Select_Protected_Entry_Call
73 -- Onqueue
74 -- Count_Waiting
76 with System.Tasking.Rendezvous;
77 -- used for Task_Do_Or_Queue
79 with System.Tasking.Utilities;
80 -- used for Exit_One_ATC_Level
82 with System.Tasking.Debug;
83 -- used for Trace
85 with System.Parameters;
86 -- used for Single_Lock
87 -- Runtime_Traces
89 with System.Traces.Tasking;
90 -- used for Send_Trace_Info
92 with System.Restrictions;
93 -- used for Run_Time_Restrictions
95 package body System.Tasking.Protected_Objects.Operations is
97 package STPO renames System.Task_Primitives.Operations;
99 use Parameters;
100 use Task_Primitives;
101 use Ada.Exceptions;
102 use Entries;
104 use System.Restrictions;
105 use System.Restrictions.Rident;
106 use System.Traces;
107 use System.Traces.Tasking;
109 -----------------------
110 -- Local Subprograms --
111 -----------------------
113 procedure Update_For_Queue_To_PO
114 (Entry_Call : Entry_Call_Link;
115 With_Abort : Boolean);
116 pragma Inline (Update_For_Queue_To_PO);
117 -- Update the state of an existing entry call to reflect
118 -- the fact that it is being enqueued, based on
119 -- whether the current queuing action is with or without abort.
120 -- Call this only while holding the PO's lock.
121 -- It returns with the PO's lock still held.
123 procedure Requeue_Call
124 (Self_Id : Task_Id;
125 Object : Protection_Entries_Access;
126 Entry_Call : Entry_Call_Link;
127 With_Abort : Boolean);
128 -- Handle requeue of Entry_Call.
129 -- In particular, queue the call if needed, or service it immediately
130 -- if possible.
132 ---------------------------------
133 -- Cancel_Protected_Entry_Call --
134 ---------------------------------
136 -- Compiler interface only. Do not call from within the RTS.
137 -- This should have analogous effect to Cancel_Task_Entry_Call,
138 -- setting the value of Block.Cancelled instead of returning
139 -- the parameter value Cancelled.
141 -- The effect should be idempotent, since the call may already
142 -- have been dequeued.
144 -- source code:
146 -- select r.e;
147 -- ...A...
148 -- then abort
149 -- ...B...
150 -- end select;
152 -- expanded code:
154 -- declare
155 -- X : protected_entry_index := 1;
156 -- B80b : communication_block;
157 -- communication_blockIP (B80b);
158 -- begin
159 -- begin
160 -- A79b : label
161 -- A79b : declare
162 -- procedure _clean is
163 -- begin
164 -- if enqueued (B80b) then
165 -- cancel_protected_entry_call (B80b);
166 -- end if;
167 -- return;
168 -- end _clean;
169 -- begin
170 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
171 -- null_address, asynchronous_call, B80b, objectF => 0);
172 -- if enqueued (B80b) then
173 -- ...B...
174 -- end if;
175 -- at end
176 -- _clean;
177 -- end A79b;
178 -- exception
179 -- when _abort_signal =>
180 -- abort_undefer.all;
181 -- null;
182 -- end;
183 -- if not cancelled (B80b) then
184 -- x := ...A...
185 -- end if;
186 -- end;
188 -- If the entry call completes after we get into the abortable part,
189 -- Abort_Signal should be raised and ATC will take us to the at-end
190 -- handler, which will call _clean.
192 -- If the entry call returns with the call already completed,
193 -- we can skip this, and use the "if enqueued()" to go past
194 -- the at-end handler, but we will still call _clean.
196 -- If the abortable part completes before the entry call is Done,
197 -- it will call _clean.
199 -- If the entry call or the abortable part raises an exception,
200 -- we will still call _clean, but the value of Cancelled should not matter.
202 -- Whoever calls _clean first gets to decide whether the call
203 -- has been "cancelled".
205 -- Enqueued should be true if there is any chance that the call
206 -- is still on a queue. It seems to be safe to make it True if
207 -- the call was Onqueue at some point before return from
208 -- Protected_Entry_Call.
210 -- Cancelled should be true iff the abortable part completed
211 -- and succeeded in cancelling the entry call before it completed.
213 -- ?????
214 -- The need for Enqueued is less obvious.
215 -- The "if enqueued ()" tests are not necessary, since both
216 -- Cancel_Protected_Entry_Call and Protected_Entry_Call must
217 -- do the same test internally, with locking. The one that
218 -- makes cancellation conditional may be a useful heuristic
219 -- since at least 1/2 the time the call should be off-queue
220 -- by that point. The other one seems totally useless, since
221 -- Protected_Entry_Call must do the same check and then
222 -- possibly wait for the call to be abortable, internally.
224 -- We can check Call.State here without locking the caller's mutex,
225 -- since the call must be over after returning from Wait_For_Completion.
226 -- No other task can access the call record at this point.
228 procedure Cancel_Protected_Entry_Call
229 (Block : in out Communication_Block) is
230 begin
231 Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
232 end Cancel_Protected_Entry_Call;
234 ---------------
235 -- Cancelled --
236 ---------------
238 function Cancelled (Block : Communication_Block) return Boolean is
239 begin
240 return Block.Cancelled;
241 end Cancelled;
243 -------------------------
244 -- Complete_Entry_Body --
245 -------------------------
247 procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
248 begin
249 Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
250 end Complete_Entry_Body;
252 --------------
253 -- Enqueued --
254 --------------
256 function Enqueued (Block : Communication_Block) return Boolean is
257 begin
258 return Block.Enqueued;
259 end Enqueued;
261 -------------------------------------
262 -- Exceptional_Complete_Entry_Body --
263 -------------------------------------
265 procedure Exceptional_Complete_Entry_Body
266 (Object : Protection_Entries_Access;
267 Ex : Ada.Exceptions.Exception_Id)
269 procedure Transfer_Occurrence
270 (Target : Ada.Exceptions.Exception_Occurrence_Access;
271 Source : Ada.Exceptions.Exception_Occurrence);
272 pragma Import (C, Transfer_Occurrence, "__gnat_transfer_occurrence");
274 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
275 begin
276 pragma Debug
277 (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
279 -- We must have abort deferred, since we are inside
280 -- a protected operation.
282 if Entry_Call /= null then
283 -- The call was not requeued.
285 Entry_Call.Exception_To_Raise := Ex;
287 if Ex /= Ada.Exceptions.Null_Id then
288 Transfer_Occurrence
289 (Entry_Call.Self.Common.Compiler_Data.Current_Excep'Access,
290 STPO.Self.Common.Compiler_Data.Current_Excep);
291 end if;
293 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
294 -- PO_Service_Entries on return.
295 end if;
297 if Runtime_Traces then
298 Send_Trace_Info (PO_Done, Entry_Call.Self);
299 end if;
300 end Exceptional_Complete_Entry_Body;
302 --------------------
303 -- PO_Do_Or_Queue --
304 --------------------
306 procedure PO_Do_Or_Queue
307 (Self_ID : Task_Id;
308 Object : Protection_Entries_Access;
309 Entry_Call : Entry_Call_Link;
310 With_Abort : Boolean)
312 E : constant Protected_Entry_Index :=
313 Protected_Entry_Index (Entry_Call.E);
314 Barrier_Value : Boolean;
316 begin
317 -- When the Action procedure for an entry body returns, it is either
318 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
319 -- is queued, having executed a requeue statement.
321 Barrier_Value :=
322 Object.Entry_Bodies (
323 Object.Find_Body_Index (Object.Compiler_Info, E)).
324 Barrier (Object.Compiler_Info, E);
326 if Barrier_Value then
328 -- Not abortable while service is in progress.
330 if Entry_Call.State = Now_Abortable then
331 Entry_Call.State := Was_Abortable;
332 end if;
334 Object.Call_In_Progress := Entry_Call;
336 pragma Debug
337 (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
338 Object.Entry_Bodies (
339 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
340 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
342 if Object.Call_In_Progress /= null then
344 -- Body of current entry served call to completion
346 Object.Call_In_Progress := null;
348 if Single_Lock then
349 STPO.Lock_RTS;
350 end if;
352 STPO.Write_Lock (Entry_Call.Self);
353 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
354 STPO.Unlock (Entry_Call.Self);
356 if Single_Lock then
357 STPO.Unlock_RTS;
358 end if;
360 else
361 Requeue_Call (Self_ID, Object, Entry_Call, With_Abort);
362 end if;
364 elsif Entry_Call.Mode /= Conditional_Call
365 or else not With_Abort
366 then
368 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
369 and then
370 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
371 Queuing.Count_Waiting (Object.Entry_Queues (E))
372 then
373 -- This violates the Max_Entry_Queue_Length restriction,
374 -- raise Program_Error.
376 Entry_Call.Exception_To_Raise := Program_Error'Identity;
378 if Single_Lock then
379 STPO.Lock_RTS;
380 end if;
382 STPO.Write_Lock (Entry_Call.Self);
383 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
384 STPO.Unlock (Entry_Call.Self);
386 if Single_Lock then
387 STPO.Unlock_RTS;
388 end if;
389 else
390 Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
391 Update_For_Queue_To_PO (Entry_Call, With_Abort);
392 end if;
393 else
394 -- Conditional_Call and With_Abort
396 if Single_Lock then
397 STPO.Lock_RTS;
398 end if;
400 STPO.Write_Lock (Entry_Call.Self);
401 pragma Assert (Entry_Call.State >= Was_Abortable);
402 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
403 STPO.Unlock (Entry_Call.Self);
405 if Single_Lock then
406 STPO.Unlock_RTS;
407 end if;
408 end if;
410 exception
411 when others =>
412 Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
413 end PO_Do_Or_Queue;
415 ------------------------
416 -- PO_Service_Entries --
417 ------------------------
419 procedure PO_Service_Entries
420 (Self_ID : Task_Id;
421 Object : Entries.Protection_Entries_Access;
422 Unlock_Object : Boolean := True)
424 E : Protected_Entry_Index;
425 Caller : Task_Id;
426 Entry_Call : Entry_Call_Link;
428 begin
429 loop
430 Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
432 exit when Entry_Call = null;
434 E := Protected_Entry_Index (Entry_Call.E);
436 -- Not abortable while service is in progress.
438 if Entry_Call.State = Now_Abortable then
439 Entry_Call.State := Was_Abortable;
440 end if;
442 Object.Call_In_Progress := Entry_Call;
444 begin
445 if Runtime_Traces then
446 Send_Trace_Info (PO_Run, Self_ID,
447 Entry_Call.Self, Entry_Index (E));
448 end if;
450 pragma Debug
451 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
452 Object.Entry_Bodies (
453 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
454 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
455 exception
456 when others =>
457 Queuing.Broadcast_Program_Error
458 (Self_ID, Object, Entry_Call);
459 end;
461 if Object.Call_In_Progress = null then
462 Requeue_Call
463 (Self_ID, Object, Entry_Call, Entry_Call.Requeue_With_Abort);
464 exit when Entry_Call.State = Cancelled;
466 else
467 Object.Call_In_Progress := null;
468 Caller := Entry_Call.Self;
470 if Single_Lock then
471 STPO.Lock_RTS;
472 end if;
474 STPO.Write_Lock (Caller);
475 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
476 STPO.Unlock (Caller);
478 if Single_Lock then
479 STPO.Unlock_RTS;
480 end if;
481 end if;
482 end loop;
484 if Unlock_Object then
485 Unlock_Entries (Object);
486 end if;
487 end PO_Service_Entries;
489 ---------------------
490 -- Protected_Count --
491 ---------------------
493 function Protected_Count
494 (Object : Protection_Entries'Class;
495 E : Protected_Entry_Index)
496 return Natural
498 begin
499 return Queuing.Count_Waiting (Object.Entry_Queues (E));
500 end Protected_Count;
502 --------------------------
503 -- Protected_Entry_Call --
504 --------------------------
506 -- Compiler interface only. Do not call from within the RTS.
508 -- select r.e;
509 -- ...A...
510 -- else
511 -- ...B...
512 -- end select;
514 -- declare
515 -- X : protected_entry_index := 1;
516 -- B85b : communication_block;
517 -- communication_blockIP (B85b);
518 -- begin
519 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
520 -- null_address, conditional_call, B85b, objectF => 0);
521 -- if cancelled (B85b) then
522 -- ...B...
523 -- else
524 -- ...A...
525 -- end if;
526 -- end;
528 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
529 -- entry call.
531 -- The initial part of this procedure does not need to lock the the calling
532 -- task's ATCB, up to the point where the call record first may be queued
533 -- (PO_Do_Or_Queue), since before that no other task will have access to
534 -- the record.
536 -- If this is a call made inside of an abort deferred region, the call
537 -- should be never abortable.
539 -- If the call was not queued abortably, we need to wait until it is before
540 -- proceeding with the abortable part.
542 -- There are some heuristics here, just to save time for frequently
543 -- occurring cases. For example, we check Initially_Abortable to try to
544 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
545 -- for async. entry calls is to be queued abortably.
547 -- Another heuristic uses the Block.Enqueued to try to avoid calling
548 -- Cancel_Protected_Entry_Call if the call can be served immediately.
550 procedure Protected_Entry_Call
551 (Object : Protection_Entries_Access;
552 E : Protected_Entry_Index;
553 Uninterpreted_Data : System.Address;
554 Mode : Call_Modes;
555 Block : out Communication_Block)
557 Self_ID : constant Task_Id := STPO.Self;
558 Entry_Call : Entry_Call_Link;
559 Initially_Abortable : Boolean;
560 Ceiling_Violation : Boolean;
562 begin
563 pragma Debug
564 (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
566 if Runtime_Traces then
567 Send_Trace_Info (PO_Call, Entry_Index (E));
568 end if;
570 if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
571 Raise_Exception
572 (Storage_Error'Identity, "not enough ATC nesting levels");
573 end if;
575 -- If pragma Detect_Blocking is active then Program_Error must be
576 -- raised if this potentially blocking operation is called from a
577 -- protected action.
579 if Detect_Blocking
580 and then Self_ID.Common.Protected_Action_Nesting > 0
581 then
582 Ada.Exceptions.Raise_Exception
583 (Program_Error'Identity, "potentially blocking operation");
584 end if;
586 Initialization.Defer_Abort (Self_ID);
587 Lock_Entries (Object, Ceiling_Violation);
589 if Ceiling_Violation then
591 -- Failed ceiling check
593 Initialization.Undefer_Abort (Self_ID);
594 raise Program_Error;
595 end if;
597 Block.Self := Self_ID;
598 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
599 pragma Debug
600 (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
601 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
602 Entry_Call :=
603 Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
604 Entry_Call.Next := null;
605 Entry_Call.Mode := Mode;
606 Entry_Call.Cancellation_Attempted := False;
608 if Self_ID.Deferral_Level > 1 then
609 Entry_Call.State := Never_Abortable;
610 else
611 Entry_Call.State := Now_Abortable;
612 end if;
614 Entry_Call.E := Entry_Index (E);
615 Entry_Call.Prio := STPO.Get_Priority (Self_ID);
616 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
617 Entry_Call.Called_PO := To_Address (Object);
618 Entry_Call.Called_Task := null;
619 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
621 PO_Do_Or_Queue (Self_ID, Object, Entry_Call, With_Abort => True);
622 Initially_Abortable := Entry_Call.State = Now_Abortable;
623 PO_Service_Entries (Self_ID, Object);
625 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
626 -- for completed or cancelled calls. (This is a heuristic, only.)
628 if Entry_Call.State >= Done then
630 -- Once State >= Done it will not change any more.
632 if Single_Lock then
633 STPO.Lock_RTS;
634 end if;
636 STPO.Write_Lock (Self_ID);
637 Utilities.Exit_One_ATC_Level (Self_ID);
638 STPO.Unlock (Self_ID);
640 if Single_Lock then
641 STPO.Unlock_RTS;
642 end if;
644 Block.Enqueued := False;
645 Block.Cancelled := Entry_Call.State = Cancelled;
646 Initialization.Undefer_Abort (Self_ID);
647 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
648 return;
650 else
651 -- In this case we cannot conclude anything,
652 -- since State can change concurrently.
653 null;
654 end if;
656 -- Now for the general case.
658 if Mode = Asynchronous_Call then
660 -- Try to avoid an expensive call.
662 if not Initially_Abortable then
663 if Single_Lock then
664 STPO.Lock_RTS;
665 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
666 STPO.Unlock_RTS;
667 else
668 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
669 end if;
670 end if;
672 elsif Mode < Asynchronous_Call then
674 -- Simple_Call or Conditional_Call
676 if Single_Lock then
677 STPO.Lock_RTS;
678 Entry_Calls.Wait_For_Completion (Entry_Call);
679 STPO.Unlock_RTS;
680 else
681 STPO.Write_Lock (Self_ID);
682 Entry_Calls.Wait_For_Completion (Entry_Call);
683 STPO.Unlock (Self_ID);
684 end if;
686 Block.Cancelled := Entry_Call.State = Cancelled;
688 else
689 pragma Assert (False);
690 null;
691 end if;
693 Initialization.Undefer_Abort (Self_ID);
694 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
695 end Protected_Entry_Call;
697 ------------------
698 -- Requeue_Call --
699 ------------------
701 procedure Requeue_Call
702 (Self_Id : Task_Id;
703 Object : Protection_Entries_Access;
704 Entry_Call : Entry_Call_Link;
705 With_Abort : Boolean)
707 New_Object : Protection_Entries_Access;
708 Ceiling_Violation : Boolean;
709 Result : Boolean;
710 E : Protected_Entry_Index;
712 begin
713 New_Object := To_Protection (Entry_Call.Called_PO);
715 if New_Object = null then
717 -- Call is to be requeued to a task entry
719 if Single_Lock then
720 STPO.Lock_RTS;
721 end if;
723 Result := Rendezvous.Task_Do_Or_Queue
724 (Self_Id, Entry_Call,
725 With_Abort => Entry_Call.Requeue_With_Abort);
727 if not Result then
728 Queuing.Broadcast_Program_Error
729 (Self_Id, Object, Entry_Call, RTS_Locked => True);
730 end if;
732 if Single_Lock then
733 STPO.Unlock_RTS;
734 end if;
736 else
737 -- Call should be requeued to a PO
739 if Object /= New_Object then
741 -- Requeue is to different PO
743 Lock_Entries (New_Object, Ceiling_Violation);
745 if Ceiling_Violation then
746 Object.Call_In_Progress := null;
747 Queuing.Broadcast_Program_Error
748 (Self_Id, Object, Entry_Call);
750 else
751 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call, With_Abort);
752 PO_Service_Entries (Self_Id, New_Object);
753 end if;
755 else
756 -- Requeue is to same protected object
758 -- ??? Try to compensate apparent failure of the
759 -- scheduler on some OS (e.g VxWorks) to give higher
760 -- priority tasks a chance to run (see CXD6002).
762 STPO.Yield (False);
764 if Entry_Call.Requeue_With_Abort
765 and then Entry_Call.Cancellation_Attempted
766 then
767 -- If this is a requeue with abort and someone tried
768 -- to cancel this call, cancel it at this point.
770 Entry_Call.State := Cancelled;
771 return;
772 end if;
774 if not With_Abort
775 or else Entry_Call.Mode /= Conditional_Call
776 then
777 E := Protected_Entry_Index (Entry_Call.E);
779 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
780 and then
781 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
782 Queuing.Count_Waiting (Object.Entry_Queues (E))
783 then
784 -- This violates the Max_Entry_Queue_Length restriction,
785 -- raise Program_Error.
787 Entry_Call.Exception_To_Raise := Program_Error'Identity;
789 if Single_Lock then
790 STPO.Lock_RTS;
791 end if;
793 STPO.Write_Lock (Entry_Call.Self);
794 Initialization.Wakeup_Entry_Caller
795 (Self_Id, Entry_Call, Done);
796 STPO.Unlock (Entry_Call.Self);
798 if Single_Lock then
799 STPO.Unlock_RTS;
800 end if;
801 else
802 Queuing.Enqueue
803 (New_Object.Entry_Queues (E), Entry_Call);
804 Update_For_Queue_To_PO (Entry_Call, With_Abort);
805 end if;
807 else
808 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call, With_Abort);
809 end if;
810 end if;
811 end if;
812 end Requeue_Call;
814 ----------------------------
815 -- Protected_Entry_Caller --
816 ----------------------------
818 function Protected_Entry_Caller
819 (Object : Protection_Entries'Class) return Task_Id is
820 begin
821 return Object.Call_In_Progress.Self;
822 end Protected_Entry_Caller;
824 -----------------------------
825 -- Requeue_Protected_Entry --
826 -----------------------------
828 -- Compiler interface only. Do not call from within the RTS.
830 -- entry e when b is
831 -- begin
832 -- b := false;
833 -- ...A...
834 -- requeue e2;
835 -- end e;
837 -- procedure rPT__E10b (O : address; P : address; E :
838 -- protected_entry_index) is
839 -- type rTVP is access rTV;
840 -- freeze rTVP []
841 -- _object : rTVP := rTVP!(O);
842 -- begin
843 -- declare
844 -- rR : protection renames _object._object;
845 -- vP : integer renames _object.v;
846 -- bP : boolean renames _object.b;
847 -- begin
848 -- b := false;
849 -- ...A...
850 -- requeue_protected_entry (rR'unchecked_access, rR'
851 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
852 -- 0);
853 -- return;
854 -- end;
855 -- complete_entry_body (_object._object'unchecked_access, objectF =>
856 -- 0);
857 -- return;
858 -- exception
859 -- when others =>
860 -- abort_undefer.all;
861 -- exceptional_complete_entry_body (_object._object'
862 -- unchecked_access, current_exception, objectF => 0);
863 -- return;
864 -- end rPT__E10b;
866 procedure Requeue_Protected_Entry
867 (Object : Protection_Entries_Access;
868 New_Object : Protection_Entries_Access;
869 E : Protected_Entry_Index;
870 With_Abort : Boolean)
872 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
874 begin
875 pragma Debug
876 (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
877 pragma Assert (STPO.Self.Deferral_Level > 0);
879 Entry_Call.E := Entry_Index (E);
880 Entry_Call.Called_PO := To_Address (New_Object);
881 Entry_Call.Called_Task := null;
882 Entry_Call.Requeue_With_Abort := With_Abort;
883 Object.Call_In_Progress := null;
884 end Requeue_Protected_Entry;
886 -------------------------------------
887 -- Requeue_Task_To_Protected_Entry --
888 -------------------------------------
890 -- Compiler interface only.
892 -- accept e1 do
893 -- ...A...
894 -- requeue r.e2;
895 -- end e1;
897 -- A79b : address;
898 -- L78b : label
899 -- begin
900 -- accept_call (1, A79b);
901 -- ...A...
902 -- requeue_task_to_protected_entry (rTV!(r)._object'
903 -- unchecked_access, 2, false, new_objectF => 0);
904 -- goto L78b;
905 -- <<L78b>>
906 -- complete_rendezvous;
907 -- exception
908 -- when all others =>
909 -- exceptional_complete_rendezvous (get_gnat_exception);
910 -- end;
912 procedure Requeue_Task_To_Protected_Entry
913 (New_Object : Protection_Entries_Access;
914 E : Protected_Entry_Index;
915 With_Abort : Boolean)
917 Self_ID : constant Task_Id := STPO.Self;
918 Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
920 begin
921 Initialization.Defer_Abort (Self_ID);
923 -- We do not need to lock Self_ID here since the call is not abortable
924 -- at this point, and therefore, the caller cannot cancel the call.
926 Entry_Call.Needs_Requeue := True;
927 Entry_Call.Requeue_With_Abort := With_Abort;
928 Entry_Call.Called_PO := To_Address (New_Object);
929 Entry_Call.Called_Task := null;
930 Entry_Call.E := Entry_Index (E);
931 Initialization.Undefer_Abort (Self_ID);
932 end Requeue_Task_To_Protected_Entry;
934 ---------------------
935 -- Service_Entries --
936 ---------------------
938 procedure Service_Entries (Object : Protection_Entries_Access) is
939 Self_ID : constant Task_Id := STPO.Self;
940 begin
941 PO_Service_Entries (Self_ID, Object);
942 end Service_Entries;
944 --------------------------------
945 -- Timed_Protected_Entry_Call --
946 --------------------------------
948 -- Compiler interface only. Do not call from within the RTS.
950 procedure Timed_Protected_Entry_Call
951 (Object : Protection_Entries_Access;
952 E : Protected_Entry_Index;
953 Uninterpreted_Data : System.Address;
954 Timeout : Duration;
955 Mode : Delay_Modes;
956 Entry_Call_Successful : out Boolean)
958 Self_Id : constant Task_Id := STPO.Self;
959 Entry_Call : Entry_Call_Link;
960 Ceiling_Violation : Boolean;
961 Yielded : Boolean;
963 begin
964 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
965 Raise_Exception (Storage_Error'Identity,
966 "not enough ATC nesting levels");
967 end if;
969 -- If pragma Detect_Blocking is active then Program_Error must be
970 -- raised if this potentially blocking operation is called from a
971 -- protected action.
973 if Detect_Blocking
974 and then Self_Id.Common.Protected_Action_Nesting > 0
975 then
976 Ada.Exceptions.Raise_Exception
977 (Program_Error'Identity, "potentially blocking operation");
978 end if;
980 if Runtime_Traces then
981 Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
982 end if;
984 Initialization.Defer_Abort (Self_Id);
985 Lock_Entries (Object, Ceiling_Violation);
987 if Ceiling_Violation then
988 Initialization.Undefer_Abort (Self_Id);
989 raise Program_Error;
990 end if;
992 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
993 pragma Debug
994 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
995 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
996 Entry_Call :=
997 Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
998 Entry_Call.Next := null;
999 Entry_Call.Mode := Timed_Call;
1000 Entry_Call.Cancellation_Attempted := False;
1002 if Self_Id.Deferral_Level > 1 then
1003 Entry_Call.State := Never_Abortable;
1004 else
1005 Entry_Call.State := Now_Abortable;
1006 end if;
1008 Entry_Call.E := Entry_Index (E);
1009 Entry_Call.Prio := STPO.Get_Priority (Self_Id);
1010 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
1011 Entry_Call.Called_PO := To_Address (Object);
1012 Entry_Call.Called_Task := null;
1013 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
1015 PO_Do_Or_Queue (Self_Id, Object, Entry_Call, With_Abort => True);
1016 PO_Service_Entries (Self_Id, Object);
1018 if Single_Lock then
1019 STPO.Lock_RTS;
1020 else
1021 STPO.Write_Lock (Self_Id);
1022 end if;
1024 -- Try to avoid waiting for completed or cancelled calls.
1026 if Entry_Call.State >= Done then
1027 Utilities.Exit_One_ATC_Level (Self_Id);
1029 if Single_Lock then
1030 STPO.Unlock_RTS;
1031 else
1032 STPO.Unlock (Self_Id);
1033 end if;
1035 Entry_Call_Successful := Entry_Call.State = Done;
1036 Initialization.Undefer_Abort (Self_Id);
1037 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1038 return;
1039 end if;
1041 Entry_Calls.Wait_For_Completion_With_Timeout
1042 (Entry_Call, Timeout, Mode, Yielded);
1044 if Single_Lock then
1045 STPO.Unlock_RTS;
1046 else
1047 STPO.Unlock (Self_Id);
1048 end if;
1050 -- ??? Do we need to yield in case Yielded is False
1052 Initialization.Undefer_Abort (Self_Id);
1053 Entry_Call_Successful := Entry_Call.State = Done;
1054 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1055 end Timed_Protected_Entry_Call;
1057 ----------------------------
1058 -- Update_For_Queue_To_PO --
1059 ----------------------------
1061 -- Update the state of an existing entry call, based on
1062 -- whether the current queuing action is with or without abort.
1063 -- Call this only while holding the server's lock.
1064 -- It returns with the server's lock released.
1066 New_State : constant array (Boolean, Entry_Call_State)
1067 of Entry_Call_State :=
1068 (True =>
1069 (Never_Abortable => Never_Abortable,
1070 Not_Yet_Abortable => Now_Abortable,
1071 Was_Abortable => Now_Abortable,
1072 Now_Abortable => Now_Abortable,
1073 Done => Done,
1074 Cancelled => Cancelled),
1075 False =>
1076 (Never_Abortable => Never_Abortable,
1077 Not_Yet_Abortable => Not_Yet_Abortable,
1078 Was_Abortable => Was_Abortable,
1079 Now_Abortable => Now_Abortable,
1080 Done => Done,
1081 Cancelled => Cancelled)
1084 procedure Update_For_Queue_To_PO
1085 (Entry_Call : Entry_Call_Link;
1086 With_Abort : Boolean)
1088 Old : constant Entry_Call_State := Entry_Call.State;
1090 begin
1091 pragma Assert (Old < Done);
1093 Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1095 if Entry_Call.Mode = Asynchronous_Call then
1096 if Old < Was_Abortable and then
1097 Entry_Call.State = Now_Abortable
1098 then
1099 if Single_Lock then
1100 STPO.Lock_RTS;
1101 end if;
1103 STPO.Write_Lock (Entry_Call.Self);
1105 if Entry_Call.Self.Common.State = Async_Select_Sleep then
1106 STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1107 end if;
1109 STPO.Unlock (Entry_Call.Self);
1111 if Single_Lock then
1112 STPO.Unlock_RTS;
1113 end if;
1115 end if;
1117 elsif Entry_Call.Mode = Conditional_Call then
1118 pragma Assert (Entry_Call.State < Was_Abortable);
1119 null;
1120 end if;
1121 end Update_For_Queue_To_PO;
1123 end System.Tasking.Protected_Objects.Operations;