2008-05-30 Vladimir Makarov <vmakarov@redhat.com>
[official-gcc.git] / gcc / ada / s-tpobop.adb
blob8ad468c7be82e9a756153da6a76df4aeb36be067
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- SYSTEM.TASKING.PROTECTED_OBJECTS.OPERATIONS --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1998-2008, 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 2, or (at your option) any later ver- --
14 -- sion. GNARL 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. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNARL; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
21 -- --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
28 -- --
29 -- GNARL was developed by the GNARL team at Florida State University. --
30 -- Extensive contributions were provided by Ada Core Technologies, Inc. --
31 -- --
32 ------------------------------------------------------------------------------
34 -- This package contains all extended primitives related to Protected_Objects
35 -- with entries.
37 -- The handling of protected objects with no entries is done in
38 -- System.Tasking.Protected_Objects, the simple routines for protected
39 -- objects with entries in System.Tasking.Protected_Objects.Entries.
41 -- The split between Entries and Operations is needed to break circular
42 -- dependencies inside the run time.
44 -- This package contains all primitives related to Protected_Objects.
45 -- Note: the compiler generates direct calls to this interface, via Rtsfind.
47 with System.Task_Primitives.Operations;
48 with System.Tasking.Entry_Calls;
49 with System.Tasking.Queuing;
50 with System.Tasking.Rendezvous;
51 with System.Tasking.Utilities;
52 with System.Tasking.Debug;
53 with System.Parameters;
54 with System.Traces.Tasking;
55 with System.Restrictions;
57 with System.Tasking.Initialization;
58 pragma Elaborate_All (System.Tasking.Initialization);
59 -- Insures that tasking is initialized if any protected objects are created
61 package body System.Tasking.Protected_Objects.Operations is
63 package STPO renames System.Task_Primitives.Operations;
65 use Parameters;
66 use Task_Primitives;
67 use Ada.Exceptions;
68 use Entries;
70 use System.Restrictions;
71 use System.Restrictions.Rident;
72 use System.Traces;
73 use System.Traces.Tasking;
75 -----------------------
76 -- Local Subprograms --
77 -----------------------
79 procedure Update_For_Queue_To_PO
80 (Entry_Call : Entry_Call_Link;
81 With_Abort : Boolean);
82 pragma Inline (Update_For_Queue_To_PO);
83 -- Update the state of an existing entry call to reflect the fact that it
84 -- is being enqueued, based on whether the current queuing action is with
85 -- or without abort. Call this only while holding the PO's lock. It returns
86 -- with the PO's lock still held.
88 procedure Requeue_Call
89 (Self_Id : Task_Id;
90 Object : Protection_Entries_Access;
91 Entry_Call : Entry_Call_Link);
92 -- Handle requeue of Entry_Call.
93 -- In particular, queue the call if needed, or service it immediately
94 -- if possible.
96 ---------------------------------
97 -- Cancel_Protected_Entry_Call --
98 ---------------------------------
100 -- Compiler interface only (do not call from within the RTS)
102 -- This should have analogous effect to Cancel_Task_Entry_Call, setting
103 -- the value of Block.Cancelled instead of returning the parameter value
104 -- Cancelled.
106 -- The effect should be idempotent, since the call may already have been
107 -- dequeued.
109 -- Source code:
111 -- select r.e;
112 -- ...A...
113 -- then abort
114 -- ...B...
115 -- end select;
117 -- Expanded code:
119 -- declare
120 -- X : protected_entry_index := 1;
121 -- B80b : communication_block;
122 -- communication_blockIP (B80b);
124 -- begin
125 -- begin
126 -- A79b : label
127 -- A79b : declare
128 -- procedure _clean is
129 -- begin
130 -- if enqueued (B80b) then
131 -- cancel_protected_entry_call (B80b);
132 -- end if;
133 -- return;
134 -- end _clean;
136 -- begin
137 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
138 -- null_address, asynchronous_call, B80b, objectF => 0);
139 -- if enqueued (B80b) then
140 -- ...B...
141 -- end if;
142 -- at end
143 -- _clean;
144 -- end A79b;
146 -- exception
147 -- when _abort_signal =>
148 -- abort_undefer.all;
149 -- null;
150 -- end;
152 -- if not cancelled (B80b) then
153 -- x := ...A...
154 -- end if;
155 -- end;
157 -- If the entry call completes after we get into the abortable part,
158 -- Abort_Signal should be raised and ATC will take us to the at-end
159 -- handler, which will call _clean.
161 -- If the entry call returns with the call already completed, we can skip
162 -- this, and use the "if enqueued()" to go past the at-end handler, but we
163 -- will still call _clean.
165 -- If the abortable part completes before the entry call is Done, it will
166 -- call _clean.
168 -- If the entry call or the abortable part raises an exception,
169 -- we will still call _clean, but the value of Cancelled should not matter.
171 -- Whoever calls _clean first gets to decide whether the call
172 -- has been "cancelled".
174 -- Enqueued should be true if there is any chance that the call is still on
175 -- a queue. It seems to be safe to make it True if the call was Onqueue at
176 -- some point before return from Protected_Entry_Call.
178 -- Cancelled should be true iff the abortable part completed
179 -- and succeeded in cancelling the entry call before it completed.
181 -- ?????
182 -- The need for Enqueued is less obvious. The "if enqueued ()" tests are
183 -- not necessary, since Cancel_Protected_Entry_Call/Protected_Entry_Call
184 -- must do the same test internally, with locking. The one that makes
185 -- cancellation conditional may be a useful heuristic since at least 1/2
186 -- the time the call should be off-queue by that point. The other one seems
187 -- totally useless, since Protected_Entry_Call must do the same check and
188 -- then possibly wait for the call to be abortable, internally.
190 -- We can check Call.State here without locking the caller's mutex,
191 -- since the call must be over after returning from Wait_For_Completion.
192 -- No other task can access the call record at this point.
194 procedure Cancel_Protected_Entry_Call
195 (Block : in out Communication_Block) is
196 begin
197 Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
198 end Cancel_Protected_Entry_Call;
200 ---------------
201 -- Cancelled --
202 ---------------
204 function Cancelled (Block : Communication_Block) return Boolean is
205 begin
206 return Block.Cancelled;
207 end Cancelled;
209 -------------------------
210 -- Complete_Entry_Body --
211 -------------------------
213 procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
214 begin
215 Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
216 end Complete_Entry_Body;
218 --------------
219 -- Enqueued --
220 --------------
222 function Enqueued (Block : Communication_Block) return Boolean is
223 begin
224 return Block.Enqueued;
225 end Enqueued;
227 -------------------------------------
228 -- Exceptional_Complete_Entry_Body --
229 -------------------------------------
231 procedure Exceptional_Complete_Entry_Body
232 (Object : Protection_Entries_Access;
233 Ex : Ada.Exceptions.Exception_Id)
235 procedure Transfer_Occurrence
236 (Target : Ada.Exceptions.Exception_Occurrence_Access;
237 Source : Ada.Exceptions.Exception_Occurrence);
238 pragma Import (C, Transfer_Occurrence, "__gnat_transfer_occurrence");
240 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
241 Self_Id : Task_Id;
243 begin
244 pragma Debug
245 (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
247 -- We must have abort deferred, since we are inside a protected
248 -- operation.
250 if Entry_Call /= null then
252 -- The call was not requeued
254 Entry_Call.Exception_To_Raise := Ex;
256 if Ex /= Ada.Exceptions.Null_Id then
258 -- An exception was raised and abort was deferred, so adjust
259 -- before propagating, otherwise the task will stay with deferral
260 -- enabled for its remaining life.
262 Self_Id := STPO.Self;
263 Initialization.Undefer_Abort_Nestable (Self_Id);
264 Transfer_Occurrence
265 (Entry_Call.Self.Common.Compiler_Data.Current_Excep'Access,
266 Self_Id.Common.Compiler_Data.Current_Excep);
267 end if;
269 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
270 -- PO_Service_Entries on return.
272 end if;
274 if Runtime_Traces then
275 Send_Trace_Info (PO_Done, Entry_Call.Self);
276 end if;
277 end Exceptional_Complete_Entry_Body;
279 --------------------
280 -- PO_Do_Or_Queue --
281 --------------------
283 procedure PO_Do_Or_Queue
284 (Self_ID : Task_Id;
285 Object : Protection_Entries_Access;
286 Entry_Call : Entry_Call_Link)
288 E : constant Protected_Entry_Index :=
289 Protected_Entry_Index (Entry_Call.E);
290 Barrier_Value : Boolean;
292 begin
293 -- When the Action procedure for an entry body returns, it is either
294 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
295 -- is queued, having executed a requeue statement.
297 Barrier_Value :=
298 Object.Entry_Bodies (
299 Object.Find_Body_Index (Object.Compiler_Info, E)).
300 Barrier (Object.Compiler_Info, E);
302 if Barrier_Value then
304 -- Not abortable while service is in progress
306 if Entry_Call.State = Now_Abortable then
307 Entry_Call.State := Was_Abortable;
308 end if;
310 Object.Call_In_Progress := Entry_Call;
312 pragma Debug
313 (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
314 Object.Entry_Bodies (
315 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
316 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
318 if Object.Call_In_Progress /= null then
320 -- Body of current entry served call to completion
322 Object.Call_In_Progress := null;
324 if Single_Lock then
325 STPO.Lock_RTS;
326 end if;
328 STPO.Write_Lock (Entry_Call.Self);
329 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
330 STPO.Unlock (Entry_Call.Self);
332 if Single_Lock then
333 STPO.Unlock_RTS;
334 end if;
336 else
337 Requeue_Call (Self_ID, Object, Entry_Call);
338 end if;
340 elsif Entry_Call.Mode /= Conditional_Call
341 or else not Entry_Call.With_Abort
342 then
344 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
345 and then
346 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
347 Queuing.Count_Waiting (Object.Entry_Queues (E))
348 then
349 -- This violates the Max_Entry_Queue_Length restriction,
350 -- raise Program_Error.
352 Entry_Call.Exception_To_Raise := Program_Error'Identity;
354 if Single_Lock then
355 STPO.Lock_RTS;
356 end if;
358 STPO.Write_Lock (Entry_Call.Self);
359 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
360 STPO.Unlock (Entry_Call.Self);
362 if Single_Lock then
363 STPO.Unlock_RTS;
364 end if;
365 else
366 Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
367 Update_For_Queue_To_PO (Entry_Call, Entry_Call.With_Abort);
368 end if;
369 else
370 -- Conditional_Call and With_Abort
372 if Single_Lock then
373 STPO.Lock_RTS;
374 end if;
376 STPO.Write_Lock (Entry_Call.Self);
377 pragma Assert (Entry_Call.State >= Was_Abortable);
378 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
379 STPO.Unlock (Entry_Call.Self);
381 if Single_Lock then
382 STPO.Unlock_RTS;
383 end if;
384 end if;
386 exception
387 when others =>
388 Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
389 end PO_Do_Or_Queue;
391 ------------------------
392 -- PO_Service_Entries --
393 ------------------------
395 procedure PO_Service_Entries
396 (Self_ID : Task_Id;
397 Object : Entries.Protection_Entries_Access;
398 Unlock_Object : Boolean := True)
400 E : Protected_Entry_Index;
401 Caller : Task_Id;
402 Entry_Call : Entry_Call_Link;
404 begin
405 loop
406 Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
408 exit when Entry_Call = null;
410 E := Protected_Entry_Index (Entry_Call.E);
412 -- Not abortable while service is in progress
414 if Entry_Call.State = Now_Abortable then
415 Entry_Call.State := Was_Abortable;
416 end if;
418 Object.Call_In_Progress := Entry_Call;
420 begin
421 if Runtime_Traces then
422 Send_Trace_Info (PO_Run, Self_ID,
423 Entry_Call.Self, Entry_Index (E));
424 end if;
426 pragma Debug
427 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
429 Object.Entry_Bodies
430 (Object.Find_Body_Index (Object.Compiler_Info, E)).Action
431 (Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
433 exception
434 when others =>
435 Queuing.Broadcast_Program_Error
436 (Self_ID, Object, Entry_Call);
437 end;
439 if Object.Call_In_Progress = null then
440 Requeue_Call (Self_ID, Object, Entry_Call);
441 exit when Entry_Call.State = Cancelled;
443 else
444 Object.Call_In_Progress := null;
445 Caller := Entry_Call.Self;
447 if Single_Lock then
448 STPO.Lock_RTS;
449 end if;
451 STPO.Write_Lock (Caller);
452 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
453 STPO.Unlock (Caller);
455 if Single_Lock then
456 STPO.Unlock_RTS;
457 end if;
458 end if;
459 end loop;
461 if Unlock_Object then
462 Unlock_Entries (Object);
463 end if;
464 end PO_Service_Entries;
466 ---------------------
467 -- Protected_Count --
468 ---------------------
470 function Protected_Count
471 (Object : Protection_Entries'Class;
472 E : Protected_Entry_Index) return Natural
474 begin
475 return Queuing.Count_Waiting (Object.Entry_Queues (E));
476 end Protected_Count;
478 --------------------------
479 -- Protected_Entry_Call --
480 --------------------------
482 -- Compiler interface only (do not call from within the RTS)
484 -- select r.e;
485 -- ...A...
486 -- else
487 -- ...B...
488 -- end select;
490 -- declare
491 -- X : protected_entry_index := 1;
492 -- B85b : communication_block;
493 -- communication_blockIP (B85b);
495 -- begin
496 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
497 -- null_address, conditional_call, B85b, objectF => 0);
499 -- if cancelled (B85b) then
500 -- ...B...
501 -- else
502 -- ...A...
503 -- end if;
504 -- end;
506 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
507 -- entry call.
509 -- The initial part of this procedure does not need to lock the calling
510 -- task's ATCB, up to the point where the call record first may be queued
511 -- (PO_Do_Or_Queue), since before that no other task will have access to
512 -- the record.
514 -- If this is a call made inside of an abort deferred region, the call
515 -- should be never abortable.
517 -- If the call was not queued abortably, we need to wait until it is before
518 -- proceeding with the abortable part.
520 -- There are some heuristics here, just to save time for frequently
521 -- occurring cases. For example, we check Initially_Abortable to try to
522 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
523 -- for async. entry calls is to be queued abortably.
525 -- Another heuristic uses the Block.Enqueued to try to avoid calling
526 -- Cancel_Protected_Entry_Call if the call can be served immediately.
528 procedure Protected_Entry_Call
529 (Object : Protection_Entries_Access;
530 E : Protected_Entry_Index;
531 Uninterpreted_Data : System.Address;
532 Mode : Call_Modes;
533 Block : out Communication_Block)
535 Self_ID : constant Task_Id := STPO.Self;
536 Entry_Call : Entry_Call_Link;
537 Initially_Abortable : Boolean;
538 Ceiling_Violation : Boolean;
540 begin
541 pragma Debug
542 (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
544 if Runtime_Traces then
545 Send_Trace_Info (PO_Call, Entry_Index (E));
546 end if;
548 if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
549 raise Storage_Error with "not enough ATC nesting levels";
550 end if;
552 -- If pragma Detect_Blocking is active then Program_Error must be
553 -- raised if this potentially blocking operation is called from a
554 -- protected action.
556 if Detect_Blocking
557 and then Self_ID.Common.Protected_Action_Nesting > 0
558 then
559 raise Program_Error with "potentially blocking operation";
560 end if;
562 -- Self_ID.Deferral_Level should be 0, except when called from Finalize,
563 -- where abort is already deferred.
565 Initialization.Defer_Abort_Nestable (Self_ID);
566 Lock_Entries (Object, Ceiling_Violation);
568 if Ceiling_Violation then
570 -- Failed ceiling check
572 Initialization.Undefer_Abort_Nestable (Self_ID);
573 raise Program_Error;
574 end if;
576 Block.Self := Self_ID;
577 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
578 pragma Debug
579 (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
580 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
581 Entry_Call :=
582 Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
583 Entry_Call.Next := null;
584 Entry_Call.Mode := Mode;
585 Entry_Call.Cancellation_Attempted := False;
587 if Self_ID.Deferral_Level > 1 then
588 Entry_Call.State := Never_Abortable;
589 else
590 Entry_Call.State := Now_Abortable;
591 end if;
593 Entry_Call.E := Entry_Index (E);
594 Entry_Call.Prio := STPO.Get_Priority (Self_ID);
595 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
596 Entry_Call.Called_PO := To_Address (Object);
597 Entry_Call.Called_Task := null;
598 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
599 Entry_Call.With_Abort := True;
601 PO_Do_Or_Queue (Self_ID, Object, Entry_Call);
602 Initially_Abortable := Entry_Call.State = Now_Abortable;
603 PO_Service_Entries (Self_ID, Object);
605 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
606 -- for completed or cancelled calls. (This is a heuristic, only.)
608 if Entry_Call.State >= Done then
610 -- Once State >= Done it will not change any more
612 if Single_Lock then
613 STPO.Lock_RTS;
614 end if;
616 STPO.Write_Lock (Self_ID);
617 Utilities.Exit_One_ATC_Level (Self_ID);
618 STPO.Unlock (Self_ID);
620 if Single_Lock then
621 STPO.Unlock_RTS;
622 end if;
624 Block.Enqueued := False;
625 Block.Cancelled := Entry_Call.State = Cancelled;
626 Initialization.Undefer_Abort_Nestable (Self_ID);
627 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
628 return;
630 else
631 -- In this case we cannot conclude anything, since State can change
632 -- concurrently.
634 null;
635 end if;
637 -- Now for the general case
639 if Mode = Asynchronous_Call then
641 -- Try to avoid an expensive call
643 if not Initially_Abortable then
644 if Single_Lock then
645 STPO.Lock_RTS;
646 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
647 STPO.Unlock_RTS;
648 else
649 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
650 end if;
651 end if;
653 elsif Mode < Asynchronous_Call then
655 -- Simple_Call or Conditional_Call
657 if Single_Lock then
658 STPO.Lock_RTS;
659 Entry_Calls.Wait_For_Completion (Entry_Call);
660 STPO.Unlock_RTS;
662 else
663 STPO.Write_Lock (Self_ID);
664 Entry_Calls.Wait_For_Completion (Entry_Call);
665 STPO.Unlock (Self_ID);
666 end if;
668 Block.Cancelled := Entry_Call.State = Cancelled;
670 else
671 pragma Assert (False);
672 null;
673 end if;
675 Initialization.Undefer_Abort_Nestable (Self_ID);
676 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
677 end Protected_Entry_Call;
679 ------------------
680 -- Requeue_Call --
681 ------------------
683 procedure Requeue_Call
684 (Self_Id : Task_Id;
685 Object : Protection_Entries_Access;
686 Entry_Call : Entry_Call_Link)
688 New_Object : Protection_Entries_Access;
689 Ceiling_Violation : Boolean;
690 Result : Boolean;
691 E : Protected_Entry_Index;
693 begin
694 New_Object := To_Protection (Entry_Call.Called_PO);
696 if New_Object = null then
698 -- Call is to be requeued to a task entry
700 if Single_Lock then
701 STPO.Lock_RTS;
702 end if;
704 Result := Rendezvous.Task_Do_Or_Queue (Self_Id, Entry_Call);
706 if not Result then
707 Queuing.Broadcast_Program_Error
708 (Self_Id, Object, Entry_Call, RTS_Locked => True);
709 end if;
711 if Single_Lock then
712 STPO.Unlock_RTS;
713 end if;
715 else
716 -- Call should be requeued to a PO
718 if Object /= New_Object then
720 -- Requeue is to different PO
722 Lock_Entries (New_Object, Ceiling_Violation);
724 if Ceiling_Violation then
725 Object.Call_In_Progress := null;
726 Queuing.Broadcast_Program_Error (Self_Id, Object, Entry_Call);
728 else
729 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call);
730 PO_Service_Entries (Self_Id, New_Object);
731 end if;
733 else
734 -- Requeue is to same protected object
736 -- ??? Try to compensate apparent failure of the scheduler on some
737 -- OS (e.g VxWorks) to give higher priority tasks a chance to run
738 -- (see CXD6002).
740 STPO.Yield (False);
742 if Entry_Call.With_Abort
743 and then Entry_Call.Cancellation_Attempted
744 then
745 -- If this is a requeue with abort and someone tried to cancel
746 -- this call, cancel it at this point.
748 Entry_Call.State := Cancelled;
749 return;
750 end if;
752 if not Entry_Call.With_Abort
753 or else Entry_Call.Mode /= Conditional_Call
754 then
755 E := Protected_Entry_Index (Entry_Call.E);
757 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
758 and then
759 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
760 Queuing.Count_Waiting (Object.Entry_Queues (E))
761 then
762 -- This violates the Max_Entry_Queue_Length restriction,
763 -- raise Program_Error.
765 Entry_Call.Exception_To_Raise := Program_Error'Identity;
767 if Single_Lock then
768 STPO.Lock_RTS;
769 end if;
771 STPO.Write_Lock (Entry_Call.Self);
772 Initialization.Wakeup_Entry_Caller
773 (Self_Id, Entry_Call, Done);
774 STPO.Unlock (Entry_Call.Self);
776 if Single_Lock then
777 STPO.Unlock_RTS;
778 end if;
780 else
781 Queuing.Enqueue
782 (New_Object.Entry_Queues (E), Entry_Call);
783 Update_For_Queue_To_PO (Entry_Call, Entry_Call.With_Abort);
784 end if;
786 else
787 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call);
788 end if;
789 end if;
790 end if;
791 end Requeue_Call;
793 ----------------------------
794 -- Protected_Entry_Caller --
795 ----------------------------
797 function Protected_Entry_Caller
798 (Object : Protection_Entries'Class) return Task_Id is
799 begin
800 return Object.Call_In_Progress.Self;
801 end Protected_Entry_Caller;
803 -----------------------------
804 -- Requeue_Protected_Entry --
805 -----------------------------
807 -- Compiler interface only (do not call from within the RTS)
809 -- entry e when b is
810 -- begin
811 -- b := false;
812 -- ...A...
813 -- requeue e2;
814 -- end e;
816 -- procedure rPT__E10b (O : address; P : address; E :
817 -- protected_entry_index) is
818 -- type rTVP is access rTV;
819 -- freeze rTVP []
820 -- _object : rTVP := rTVP!(O);
821 -- begin
822 -- declare
823 -- rR : protection renames _object._object;
824 -- vP : integer renames _object.v;
825 -- bP : boolean renames _object.b;
826 -- begin
827 -- b := false;
828 -- ...A...
829 -- requeue_protected_entry (rR'unchecked_access, rR'
830 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
831 -- 0);
832 -- return;
833 -- end;
834 -- complete_entry_body (_object._object'unchecked_access, objectF =>
835 -- 0);
836 -- return;
837 -- exception
838 -- when others =>
839 -- abort_undefer.all;
840 -- exceptional_complete_entry_body (_object._object'
841 -- unchecked_access, current_exception, objectF => 0);
842 -- return;
843 -- end rPT__E10b;
845 procedure Requeue_Protected_Entry
846 (Object : Protection_Entries_Access;
847 New_Object : Protection_Entries_Access;
848 E : Protected_Entry_Index;
849 With_Abort : Boolean)
851 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
853 begin
854 pragma Debug
855 (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
856 pragma Assert (STPO.Self.Deferral_Level > 0);
858 Entry_Call.E := Entry_Index (E);
859 Entry_Call.Called_PO := To_Address (New_Object);
860 Entry_Call.Called_Task := null;
861 Entry_Call.With_Abort := With_Abort;
862 Object.Call_In_Progress := null;
863 end Requeue_Protected_Entry;
865 -------------------------------------
866 -- Requeue_Task_To_Protected_Entry --
867 -------------------------------------
869 -- Compiler interface only (do not call from within the RTS)
871 -- accept e1 do
872 -- ...A...
873 -- requeue r.e2;
874 -- end e1;
876 -- A79b : address;
877 -- L78b : label
879 -- begin
880 -- accept_call (1, A79b);
881 -- ...A...
882 -- requeue_task_to_protected_entry (rTV!(r)._object'
883 -- unchecked_access, 2, false, new_objectF => 0);
884 -- goto L78b;
885 -- <<L78b>>
886 -- complete_rendezvous;
888 -- exception
889 -- when all others =>
890 -- exceptional_complete_rendezvous (get_gnat_exception);
891 -- end;
893 procedure Requeue_Task_To_Protected_Entry
894 (New_Object : Protection_Entries_Access;
895 E : Protected_Entry_Index;
896 With_Abort : Boolean)
898 Self_ID : constant Task_Id := STPO.Self;
899 Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
901 begin
902 Initialization.Defer_Abort (Self_ID);
904 -- We do not need to lock Self_ID here since the call is not abortable
905 -- at this point, and therefore, the caller cannot cancel the call.
907 Entry_Call.Needs_Requeue := True;
908 Entry_Call.With_Abort := With_Abort;
909 Entry_Call.Called_PO := To_Address (New_Object);
910 Entry_Call.Called_Task := null;
911 Entry_Call.E := Entry_Index (E);
912 Initialization.Undefer_Abort (Self_ID);
913 end Requeue_Task_To_Protected_Entry;
915 ---------------------
916 -- Service_Entries --
917 ---------------------
919 procedure Service_Entries (Object : Protection_Entries_Access) is
920 Self_ID : constant Task_Id := STPO.Self;
921 begin
922 PO_Service_Entries (Self_ID, Object);
923 end Service_Entries;
925 --------------------------------
926 -- Timed_Protected_Entry_Call --
927 --------------------------------
929 -- Compiler interface only (do not call from within the RTS)
931 procedure Timed_Protected_Entry_Call
932 (Object : Protection_Entries_Access;
933 E : Protected_Entry_Index;
934 Uninterpreted_Data : System.Address;
935 Timeout : Duration;
936 Mode : Delay_Modes;
937 Entry_Call_Successful : out Boolean)
939 Self_Id : constant Task_Id := STPO.Self;
940 Entry_Call : Entry_Call_Link;
941 Ceiling_Violation : Boolean;
943 Yielded : Boolean;
944 pragma Unreferenced (Yielded);
946 begin
947 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
948 raise Storage_Error with "not enough ATC nesting levels";
949 end if;
951 -- If pragma Detect_Blocking is active then Program_Error must be
952 -- raised if this potentially blocking operation is called from a
953 -- protected action.
955 if Detect_Blocking
956 and then Self_Id.Common.Protected_Action_Nesting > 0
957 then
958 raise Program_Error with "potentially blocking operation";
959 end if;
961 if Runtime_Traces then
962 Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
963 end if;
965 Initialization.Defer_Abort (Self_Id);
966 Lock_Entries (Object, Ceiling_Violation);
968 if Ceiling_Violation then
969 Initialization.Undefer_Abort (Self_Id);
970 raise Program_Error;
971 end if;
973 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
974 pragma Debug
975 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
976 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
977 Entry_Call :=
978 Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
979 Entry_Call.Next := null;
980 Entry_Call.Mode := Timed_Call;
981 Entry_Call.Cancellation_Attempted := False;
983 if Self_Id.Deferral_Level > 1 then
984 Entry_Call.State := Never_Abortable;
985 else
986 Entry_Call.State := Now_Abortable;
987 end if;
989 Entry_Call.E := Entry_Index (E);
990 Entry_Call.Prio := STPO.Get_Priority (Self_Id);
991 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
992 Entry_Call.Called_PO := To_Address (Object);
993 Entry_Call.Called_Task := null;
994 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
995 Entry_Call.With_Abort := True;
997 PO_Do_Or_Queue (Self_Id, Object, Entry_Call);
998 PO_Service_Entries (Self_Id, Object);
1000 if Single_Lock then
1001 STPO.Lock_RTS;
1002 else
1003 STPO.Write_Lock (Self_Id);
1004 end if;
1006 -- Try to avoid waiting for completed or cancelled calls
1008 if Entry_Call.State >= Done then
1009 Utilities.Exit_One_ATC_Level (Self_Id);
1011 if Single_Lock then
1012 STPO.Unlock_RTS;
1013 else
1014 STPO.Unlock (Self_Id);
1015 end if;
1017 Entry_Call_Successful := Entry_Call.State = Done;
1018 Initialization.Undefer_Abort (Self_Id);
1019 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1020 return;
1021 end if;
1023 Entry_Calls.Wait_For_Completion_With_Timeout
1024 (Entry_Call, Timeout, Mode, Yielded);
1026 if Single_Lock then
1027 STPO.Unlock_RTS;
1028 else
1029 STPO.Unlock (Self_Id);
1030 end if;
1032 -- ??? Do we need to yield in case Yielded is False
1034 Initialization.Undefer_Abort (Self_Id);
1035 Entry_Call_Successful := Entry_Call.State = Done;
1036 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1037 end Timed_Protected_Entry_Call;
1039 ----------------------------
1040 -- Update_For_Queue_To_PO --
1041 ----------------------------
1043 -- Update the state of an existing entry call, based on
1044 -- whether the current queuing action is with or without abort.
1045 -- Call this only while holding the server's lock.
1046 -- It returns with the server's lock released.
1048 New_State : constant array (Boolean, Entry_Call_State)
1049 of Entry_Call_State :=
1050 (True =>
1051 (Never_Abortable => Never_Abortable,
1052 Not_Yet_Abortable => Now_Abortable,
1053 Was_Abortable => Now_Abortable,
1054 Now_Abortable => Now_Abortable,
1055 Done => Done,
1056 Cancelled => Cancelled),
1057 False =>
1058 (Never_Abortable => Never_Abortable,
1059 Not_Yet_Abortable => Not_Yet_Abortable,
1060 Was_Abortable => Was_Abortable,
1061 Now_Abortable => Now_Abortable,
1062 Done => Done,
1063 Cancelled => Cancelled)
1066 procedure Update_For_Queue_To_PO
1067 (Entry_Call : Entry_Call_Link;
1068 With_Abort : Boolean)
1070 Old : constant Entry_Call_State := Entry_Call.State;
1072 begin
1073 pragma Assert (Old < Done);
1075 Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1077 if Entry_Call.Mode = Asynchronous_Call then
1078 if Old < Was_Abortable and then
1079 Entry_Call.State = Now_Abortable
1080 then
1081 if Single_Lock then
1082 STPO.Lock_RTS;
1083 end if;
1085 STPO.Write_Lock (Entry_Call.Self);
1087 if Entry_Call.Self.Common.State = Async_Select_Sleep then
1088 STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1089 end if;
1091 STPO.Unlock (Entry_Call.Self);
1093 if Single_Lock then
1094 STPO.Unlock_RTS;
1095 end if;
1097 end if;
1099 elsif Entry_Call.Mode = Conditional_Call then
1100 pragma Assert (Entry_Call.State < Was_Abortable);
1101 null;
1102 end if;
1103 end Update_For_Queue_To_PO;
1105 end System.Tasking.Protected_Objects.Operations;