1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
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 --
10 -- Copyright (C) 1998-2007, Free Software Foundation, Inc. --
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. --
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. --
30 -- GNARL was developed by the GNARL team at Florida State University. --
31 -- Extensive contributions were provided by Ada Core Technologies, Inc. --
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
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,
63 -- Change_Base_Priority
65 pragma Elaborate_All (System.Tasking.Initialization);
66 -- This insures that tasking is initialized if any protected objects are
69 with System.Tasking.Queuing;
71 -- Broadcast_Program_Error
72 -- Select_Protected_Entry_Call
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;
85 with System.Parameters;
86 -- used for Single_Lock
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;
104 use System.Restrictions;
105 use System.Restrictions.Rident;
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
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
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.
155 -- X : protected_entry_index := 1;
156 -- B80b : communication_block;
157 -- communication_blockIP (B80b);
162 -- procedure _clean is
164 -- if enqueued (B80b) then
165 -- cancel_protected_entry_call (B80b);
170 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
171 -- null_address, asynchronous_call, B80b, objectF => 0);
172 -- if enqueued (B80b) then
179 -- when _abort_signal =>
180 -- abort_undefer.all;
183 -- if not cancelled (B80b) then
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.
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
231 Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
232 end Cancel_Protected_Entry_Call;
238 function Cancelled (Block : Communication_Block) return Boolean is
240 return Block.Cancelled;
243 -------------------------
244 -- Complete_Entry_Body --
245 -------------------------
247 procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
249 Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
250 end Complete_Entry_Body;
256 function Enqueued (Block : Communication_Block) return Boolean is
258 return Block.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;
279 (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
281 -- We must have abort deferred, since we are inside
282 -- a protected operation.
284 if Entry_Call /= null then
285 -- The call was not requeued.
287 Entry_Call.Exception_To_Raise := Ex;
289 if Ex /= Ada.Exceptions.Null_Id then
290 -- An exception was raised and abort was deferred, so adjust
291 -- before propagating, otherwise the task will stay with deferral
292 -- enabled for its remaining life.
294 Self_Id := STPO.Self;
295 Initialization.Undefer_Abort_Nestable (Self_Id);
297 (Entry_Call.Self.Common.Compiler_Data.Current_Excep'Access,
298 Self_Id.Common.Compiler_Data.Current_Excep);
301 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
302 -- PO_Service_Entries on return.
305 if Runtime_Traces then
306 Send_Trace_Info (PO_Done, Entry_Call.Self);
308 end Exceptional_Complete_Entry_Body;
314 procedure PO_Do_Or_Queue
316 Object : Protection_Entries_Access;
317 Entry_Call : Entry_Call_Link;
318 With_Abort : Boolean)
320 E : constant Protected_Entry_Index :=
321 Protected_Entry_Index (Entry_Call.E);
322 Barrier_Value : Boolean;
325 -- When the Action procedure for an entry body returns, it is either
326 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
327 -- is queued, having executed a requeue statement.
330 Object.Entry_Bodies (
331 Object.Find_Body_Index (Object.Compiler_Info, E)).
332 Barrier (Object.Compiler_Info, E);
334 if Barrier_Value then
336 -- Not abortable while service is in progress.
338 if Entry_Call.State = Now_Abortable then
339 Entry_Call.State := Was_Abortable;
342 Object.Call_In_Progress := Entry_Call;
345 (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
346 Object.Entry_Bodies (
347 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
348 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
350 if Object.Call_In_Progress /= null then
352 -- Body of current entry served call to completion
354 Object.Call_In_Progress := null;
360 STPO.Write_Lock (Entry_Call.Self);
361 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
362 STPO.Unlock (Entry_Call.Self);
369 Requeue_Call (Self_ID, Object, Entry_Call, With_Abort);
372 elsif Entry_Call.Mode /= Conditional_Call
373 or else not With_Abort
376 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
378 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
379 Queuing.Count_Waiting (Object.Entry_Queues (E))
381 -- This violates the Max_Entry_Queue_Length restriction,
382 -- raise Program_Error.
384 Entry_Call.Exception_To_Raise := Program_Error'Identity;
390 STPO.Write_Lock (Entry_Call.Self);
391 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
392 STPO.Unlock (Entry_Call.Self);
398 Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
399 Update_For_Queue_To_PO (Entry_Call, With_Abort);
402 -- Conditional_Call and With_Abort
408 STPO.Write_Lock (Entry_Call.Self);
409 pragma Assert (Entry_Call.State >= Was_Abortable);
410 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
411 STPO.Unlock (Entry_Call.Self);
420 Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
423 ------------------------
424 -- PO_Service_Entries --
425 ------------------------
427 procedure PO_Service_Entries
429 Object : Entries.Protection_Entries_Access;
430 Unlock_Object : Boolean := True)
432 E : Protected_Entry_Index;
434 Entry_Call : Entry_Call_Link;
438 Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
440 exit when Entry_Call = null;
442 E := Protected_Entry_Index (Entry_Call.E);
444 -- Not abortable while service is in progress.
446 if Entry_Call.State = Now_Abortable then
447 Entry_Call.State := Was_Abortable;
450 Object.Call_In_Progress := Entry_Call;
453 if Runtime_Traces then
454 Send_Trace_Info (PO_Run, Self_ID,
455 Entry_Call.Self, Entry_Index (E));
459 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
460 Object.Entry_Bodies (
461 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
462 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
465 Queuing.Broadcast_Program_Error
466 (Self_ID, Object, Entry_Call);
469 if Object.Call_In_Progress = null then
471 (Self_ID, Object, Entry_Call, Entry_Call.Requeue_With_Abort);
472 exit when Entry_Call.State = Cancelled;
475 Object.Call_In_Progress := null;
476 Caller := Entry_Call.Self;
482 STPO.Write_Lock (Caller);
483 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
484 STPO.Unlock (Caller);
492 if Unlock_Object then
493 Unlock_Entries (Object);
495 end PO_Service_Entries;
497 ---------------------
498 -- Protected_Count --
499 ---------------------
501 function Protected_Count
502 (Object : Protection_Entries'Class;
503 E : Protected_Entry_Index)
507 return Queuing.Count_Waiting (Object.Entry_Queues (E));
510 --------------------------
511 -- Protected_Entry_Call --
512 --------------------------
514 -- Compiler interface only. Do not call from within the RTS.
523 -- X : protected_entry_index := 1;
524 -- B85b : communication_block;
525 -- communication_blockIP (B85b);
527 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
528 -- null_address, conditional_call, B85b, objectF => 0);
529 -- if cancelled (B85b) then
536 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
539 -- The initial part of this procedure does not need to lock the the calling
540 -- task's ATCB, up to the point where the call record first may be queued
541 -- (PO_Do_Or_Queue), since before that no other task will have access to
544 -- If this is a call made inside of an abort deferred region, the call
545 -- should be never abortable.
547 -- If the call was not queued abortably, we need to wait until it is before
548 -- proceeding with the abortable part.
550 -- There are some heuristics here, just to save time for frequently
551 -- occurring cases. For example, we check Initially_Abortable to try to
552 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
553 -- for async. entry calls is to be queued abortably.
555 -- Another heuristic uses the Block.Enqueued to try to avoid calling
556 -- Cancel_Protected_Entry_Call if the call can be served immediately.
558 procedure Protected_Entry_Call
559 (Object : Protection_Entries_Access;
560 E : Protected_Entry_Index;
561 Uninterpreted_Data : System.Address;
563 Block : out Communication_Block)
565 Self_ID : constant Task_Id := STPO.Self;
566 Entry_Call : Entry_Call_Link;
567 Initially_Abortable : Boolean;
568 Ceiling_Violation : Boolean;
572 (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
574 if Runtime_Traces then
575 Send_Trace_Info (PO_Call, Entry_Index (E));
578 if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
580 (Storage_Error'Identity, "not enough ATC nesting levels");
583 -- If pragma Detect_Blocking is active then Program_Error must be
584 -- raised if this potentially blocking operation is called from a
588 and then Self_ID.Common.Protected_Action_Nesting > 0
590 Ada.Exceptions.Raise_Exception
591 (Program_Error'Identity, "potentially blocking operation");
594 -- Self_ID.Deferral_Level should be 0, except when called from Finalize,
595 -- where abort is already deferred.
597 Initialization.Defer_Abort_Nestable (Self_ID);
598 Lock_Entries (Object, Ceiling_Violation);
600 if Ceiling_Violation then
602 -- Failed ceiling check
604 Initialization.Undefer_Abort_Nestable (Self_ID);
608 Block.Self := Self_ID;
609 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
611 (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
612 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
614 Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
615 Entry_Call.Next := null;
616 Entry_Call.Mode := Mode;
617 Entry_Call.Cancellation_Attempted := False;
619 if Self_ID.Deferral_Level > 1 then
620 Entry_Call.State := Never_Abortable;
622 Entry_Call.State := Now_Abortable;
625 Entry_Call.E := Entry_Index (E);
626 Entry_Call.Prio := STPO.Get_Priority (Self_ID);
627 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
628 Entry_Call.Called_PO := To_Address (Object);
629 Entry_Call.Called_Task := null;
630 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
632 PO_Do_Or_Queue (Self_ID, Object, Entry_Call, With_Abort => True);
633 Initially_Abortable := Entry_Call.State = Now_Abortable;
634 PO_Service_Entries (Self_ID, Object);
636 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
637 -- for completed or cancelled calls. (This is a heuristic, only.)
639 if Entry_Call.State >= Done then
641 -- Once State >= Done it will not change any more.
647 STPO.Write_Lock (Self_ID);
648 Utilities.Exit_One_ATC_Level (Self_ID);
649 STPO.Unlock (Self_ID);
655 Block.Enqueued := False;
656 Block.Cancelled := Entry_Call.State = Cancelled;
657 Initialization.Undefer_Abort_Nestable (Self_ID);
658 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
662 -- In this case we cannot conclude anything,
663 -- since State can change concurrently.
667 -- Now for the general case.
669 if Mode = Asynchronous_Call then
671 -- Try to avoid an expensive call.
673 if not Initially_Abortable then
676 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
679 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
683 elsif Mode < Asynchronous_Call then
685 -- Simple_Call or Conditional_Call
689 Entry_Calls.Wait_For_Completion (Entry_Call);
692 STPO.Write_Lock (Self_ID);
693 Entry_Calls.Wait_For_Completion (Entry_Call);
694 STPO.Unlock (Self_ID);
697 Block.Cancelled := Entry_Call.State = Cancelled;
700 pragma Assert (False);
704 Initialization.Undefer_Abort_Nestable (Self_ID);
705 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
706 end Protected_Entry_Call;
712 procedure Requeue_Call
714 Object : Protection_Entries_Access;
715 Entry_Call : Entry_Call_Link;
716 With_Abort : Boolean)
718 New_Object : Protection_Entries_Access;
719 Ceiling_Violation : Boolean;
721 E : Protected_Entry_Index;
724 New_Object := To_Protection (Entry_Call.Called_PO);
726 if New_Object = null then
728 -- Call is to be requeued to a task entry
734 Result := Rendezvous.Task_Do_Or_Queue
735 (Self_Id, Entry_Call,
736 With_Abort => Entry_Call.Requeue_With_Abort);
739 Queuing.Broadcast_Program_Error
740 (Self_Id, Object, Entry_Call, RTS_Locked => True);
748 -- Call should be requeued to a PO
750 if Object /= New_Object then
752 -- Requeue is to different PO
754 Lock_Entries (New_Object, Ceiling_Violation);
756 if Ceiling_Violation then
757 Object.Call_In_Progress := null;
758 Queuing.Broadcast_Program_Error
759 (Self_Id, Object, Entry_Call);
762 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call, With_Abort);
763 PO_Service_Entries (Self_Id, New_Object);
767 -- Requeue is to same protected object
769 -- ??? Try to compensate apparent failure of the
770 -- scheduler on some OS (e.g VxWorks) to give higher
771 -- priority tasks a chance to run (see CXD6002).
775 if Entry_Call.Requeue_With_Abort
776 and then Entry_Call.Cancellation_Attempted
778 -- If this is a requeue with abort and someone tried
779 -- to cancel this call, cancel it at this point.
781 Entry_Call.State := Cancelled;
786 or else Entry_Call.Mode /= Conditional_Call
788 E := Protected_Entry_Index (Entry_Call.E);
790 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
792 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
793 Queuing.Count_Waiting (Object.Entry_Queues (E))
795 -- This violates the Max_Entry_Queue_Length restriction,
796 -- raise Program_Error.
798 Entry_Call.Exception_To_Raise := Program_Error'Identity;
804 STPO.Write_Lock (Entry_Call.Self);
805 Initialization.Wakeup_Entry_Caller
806 (Self_Id, Entry_Call, Done);
807 STPO.Unlock (Entry_Call.Self);
814 (New_Object.Entry_Queues (E), Entry_Call);
815 Update_For_Queue_To_PO (Entry_Call, With_Abort);
819 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call, With_Abort);
825 ----------------------------
826 -- Protected_Entry_Caller --
827 ----------------------------
829 function Protected_Entry_Caller
830 (Object : Protection_Entries'Class) return Task_Id is
832 return Object.Call_In_Progress.Self;
833 end Protected_Entry_Caller;
835 -----------------------------
836 -- Requeue_Protected_Entry --
837 -----------------------------
839 -- Compiler interface only. Do not call from within the RTS.
848 -- procedure rPT__E10b (O : address; P : address; E :
849 -- protected_entry_index) is
850 -- type rTVP is access rTV;
852 -- _object : rTVP := rTVP!(O);
855 -- rR : protection renames _object._object;
856 -- vP : integer renames _object.v;
857 -- bP : boolean renames _object.b;
861 -- requeue_protected_entry (rR'unchecked_access, rR'
862 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
866 -- complete_entry_body (_object._object'unchecked_access, objectF =>
871 -- abort_undefer.all;
872 -- exceptional_complete_entry_body (_object._object'
873 -- unchecked_access, current_exception, objectF => 0);
877 procedure Requeue_Protected_Entry
878 (Object : Protection_Entries_Access;
879 New_Object : Protection_Entries_Access;
880 E : Protected_Entry_Index;
881 With_Abort : Boolean)
883 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
887 (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
888 pragma Assert (STPO.Self.Deferral_Level > 0);
890 Entry_Call.E := Entry_Index (E);
891 Entry_Call.Called_PO := To_Address (New_Object);
892 Entry_Call.Called_Task := null;
893 Entry_Call.Requeue_With_Abort := With_Abort;
894 Object.Call_In_Progress := null;
895 end Requeue_Protected_Entry;
897 -------------------------------------
898 -- Requeue_Task_To_Protected_Entry --
899 -------------------------------------
901 -- Compiler interface only.
911 -- accept_call (1, A79b);
913 -- requeue_task_to_protected_entry (rTV!(r)._object'
914 -- unchecked_access, 2, false, new_objectF => 0);
917 -- complete_rendezvous;
919 -- when all others =>
920 -- exceptional_complete_rendezvous (get_gnat_exception);
923 procedure Requeue_Task_To_Protected_Entry
924 (New_Object : Protection_Entries_Access;
925 E : Protected_Entry_Index;
926 With_Abort : Boolean)
928 Self_ID : constant Task_Id := STPO.Self;
929 Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
932 Initialization.Defer_Abort (Self_ID);
934 -- We do not need to lock Self_ID here since the call is not abortable
935 -- at this point, and therefore, the caller cannot cancel the call.
937 Entry_Call.Needs_Requeue := True;
938 Entry_Call.Requeue_With_Abort := With_Abort;
939 Entry_Call.Called_PO := To_Address (New_Object);
940 Entry_Call.Called_Task := null;
941 Entry_Call.E := Entry_Index (E);
942 Initialization.Undefer_Abort (Self_ID);
943 end Requeue_Task_To_Protected_Entry;
945 ---------------------
946 -- Service_Entries --
947 ---------------------
949 procedure Service_Entries (Object : Protection_Entries_Access) is
950 Self_ID : constant Task_Id := STPO.Self;
952 PO_Service_Entries (Self_ID, Object);
955 --------------------------------
956 -- Timed_Protected_Entry_Call --
957 --------------------------------
959 -- Compiler interface only. Do not call from within the RTS.
961 procedure Timed_Protected_Entry_Call
962 (Object : Protection_Entries_Access;
963 E : Protected_Entry_Index;
964 Uninterpreted_Data : System.Address;
967 Entry_Call_Successful : out Boolean)
969 Self_Id : constant Task_Id := STPO.Self;
970 Entry_Call : Entry_Call_Link;
971 Ceiling_Violation : Boolean;
975 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
976 Raise_Exception (Storage_Error'Identity,
977 "not enough ATC nesting levels");
980 -- If pragma Detect_Blocking is active then Program_Error must be
981 -- raised if this potentially blocking operation is called from a
985 and then Self_Id.Common.Protected_Action_Nesting > 0
987 Ada.Exceptions.Raise_Exception
988 (Program_Error'Identity, "potentially blocking operation");
991 if Runtime_Traces then
992 Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
995 Initialization.Defer_Abort (Self_Id);
996 Lock_Entries (Object, Ceiling_Violation);
998 if Ceiling_Violation then
999 Initialization.Undefer_Abort (Self_Id);
1000 raise Program_Error;
1003 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
1005 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
1006 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
1008 Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
1009 Entry_Call.Next := null;
1010 Entry_Call.Mode := Timed_Call;
1011 Entry_Call.Cancellation_Attempted := False;
1013 if Self_Id.Deferral_Level > 1 then
1014 Entry_Call.State := Never_Abortable;
1016 Entry_Call.State := Now_Abortable;
1019 Entry_Call.E := Entry_Index (E);
1020 Entry_Call.Prio := STPO.Get_Priority (Self_Id);
1021 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
1022 Entry_Call.Called_PO := To_Address (Object);
1023 Entry_Call.Called_Task := null;
1024 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
1026 PO_Do_Or_Queue (Self_Id, Object, Entry_Call, With_Abort => True);
1027 PO_Service_Entries (Self_Id, Object);
1032 STPO.Write_Lock (Self_Id);
1035 -- Try to avoid waiting for completed or cancelled calls.
1037 if Entry_Call.State >= Done then
1038 Utilities.Exit_One_ATC_Level (Self_Id);
1043 STPO.Unlock (Self_Id);
1046 Entry_Call_Successful := Entry_Call.State = Done;
1047 Initialization.Undefer_Abort (Self_Id);
1048 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1052 Entry_Calls.Wait_For_Completion_With_Timeout
1053 (Entry_Call, Timeout, Mode, Yielded);
1058 STPO.Unlock (Self_Id);
1061 -- ??? Do we need to yield in case Yielded is False
1063 Initialization.Undefer_Abort (Self_Id);
1064 Entry_Call_Successful := Entry_Call.State = Done;
1065 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1066 end Timed_Protected_Entry_Call;
1068 ----------------------------
1069 -- Update_For_Queue_To_PO --
1070 ----------------------------
1072 -- Update the state of an existing entry call, based on
1073 -- whether the current queuing action is with or without abort.
1074 -- Call this only while holding the server's lock.
1075 -- It returns with the server's lock released.
1077 New_State : constant array (Boolean, Entry_Call_State)
1078 of Entry_Call_State :=
1080 (Never_Abortable => Never_Abortable,
1081 Not_Yet_Abortable => Now_Abortable,
1082 Was_Abortable => Now_Abortable,
1083 Now_Abortable => Now_Abortable,
1085 Cancelled => Cancelled),
1087 (Never_Abortable => Never_Abortable,
1088 Not_Yet_Abortable => Not_Yet_Abortable,
1089 Was_Abortable => Was_Abortable,
1090 Now_Abortable => Now_Abortable,
1092 Cancelled => Cancelled)
1095 procedure Update_For_Queue_To_PO
1096 (Entry_Call : Entry_Call_Link;
1097 With_Abort : Boolean)
1099 Old : constant Entry_Call_State := Entry_Call.State;
1102 pragma Assert (Old < Done);
1104 Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1106 if Entry_Call.Mode = Asynchronous_Call then
1107 if Old < Was_Abortable and then
1108 Entry_Call.State = Now_Abortable
1114 STPO.Write_Lock (Entry_Call.Self);
1116 if Entry_Call.Self.Common.State = Async_Select_Sleep then
1117 STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1120 STPO.Unlock (Entry_Call.Self);
1128 elsif Entry_Call.Mode = Conditional_Call then
1129 pragma Assert (Entry_Call.State < Was_Abortable);
1132 end Update_For_Queue_To_PO;
1134 end System.Tasking.Protected_Objects.Operations;