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 the fact that it
118 -- is being enqueued, based on whether the current queuing action is with
119 -- or without abort. Call this only while holding the PO's lock. It returns
120 -- with the PO's lock still held.
122 procedure Requeue_Call
124 Object : Protection_Entries_Access;
125 Entry_Call : Entry_Call_Link);
126 -- Handle requeue of Entry_Call.
127 -- In particular, queue the call if needed, or service it immediately
130 ---------------------------------
131 -- Cancel_Protected_Entry_Call --
132 ---------------------------------
134 -- Compiler interface only (do not call from within the RTS)
136 -- This should have analogous effect to Cancel_Task_Entry_Call, setting
137 -- the value of Block.Cancelled instead of returning the parameter value
140 -- The effect should be idempotent, since the call may already have been
154 -- X : protected_entry_index := 1;
155 -- B80b : communication_block;
156 -- communication_blockIP (B80b);
162 -- procedure _clean is
164 -- if enqueued (B80b) then
165 -- cancel_protected_entry_call (B80b);
171 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
172 -- null_address, asynchronous_call, B80b, objectF => 0);
173 -- if enqueued (B80b) then
181 -- when _abort_signal =>
182 -- abort_undefer.all;
186 -- if not cancelled (B80b) then
191 -- If the entry call completes after we get into the abortable part,
192 -- Abort_Signal should be raised and ATC will take us to the at-end
193 -- handler, which will call _clean.
195 -- If the entry call returns with the call already completed, we can skip
196 -- this, and use the "if enqueued()" to go past the at-end handler, but we
197 -- will still call _clean.
199 -- If the abortable part completes before the entry call is Done, it will
202 -- If the entry call or the abortable part raises an exception,
203 -- we will still call _clean, but the value of Cancelled should not matter.
205 -- Whoever calls _clean first gets to decide whether the call
206 -- has been "cancelled".
208 -- Enqueued should be true if there is any chance that the call is still on
209 -- a queue. It seems to be safe to make it True if the call was Onqueue at
210 -- some point before return from Protected_Entry_Call.
212 -- Cancelled should be true iff the abortable part completed
213 -- and succeeded in cancelling the entry call before it completed.
216 -- The need for Enqueued is less obvious. The "if enqueued ()" tests are
217 -- not necessary, since Cancel_Protected_Entry_Call/Protected_Entry_Call
218 -- must do the same test internally, with locking. The one that makes
219 -- cancellation conditional may be a useful heuristic since at least 1/2
220 -- the time the call should be off-queue by that point. The other one seems
221 -- totally useless, since Protected_Entry_Call must do the same check and
222 -- then 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 a protected
284 if Entry_Call /= null then
286 -- The call was not requeued
288 Entry_Call.Exception_To_Raise := Ex;
290 if Ex /= Ada.Exceptions.Null_Id then
292 -- An exception was raised and abort was deferred, so adjust
293 -- before propagating, otherwise the task will stay with deferral
294 -- enabled for its remaining life.
296 Self_Id := STPO.Self;
297 Initialization.Undefer_Abort_Nestable (Self_Id);
299 (Entry_Call.Self.Common.Compiler_Data.Current_Excep'Access,
300 Self_Id.Common.Compiler_Data.Current_Excep);
303 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
304 -- PO_Service_Entries on return.
308 if Runtime_Traces then
309 Send_Trace_Info (PO_Done, Entry_Call.Self);
311 end Exceptional_Complete_Entry_Body;
317 procedure PO_Do_Or_Queue
319 Object : Protection_Entries_Access;
320 Entry_Call : Entry_Call_Link)
322 E : constant Protected_Entry_Index :=
323 Protected_Entry_Index (Entry_Call.E);
324 Barrier_Value : Boolean;
327 -- When the Action procedure for an entry body returns, it is either
328 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
329 -- is queued, having executed a requeue statement.
332 Object.Entry_Bodies (
333 Object.Find_Body_Index (Object.Compiler_Info, E)).
334 Barrier (Object.Compiler_Info, E);
336 if Barrier_Value then
338 -- Not abortable while service is in progress
340 if Entry_Call.State = Now_Abortable then
341 Entry_Call.State := Was_Abortable;
344 Object.Call_In_Progress := Entry_Call;
347 (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
348 Object.Entry_Bodies (
349 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
350 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
352 if Object.Call_In_Progress /= null then
354 -- Body of current entry served call to completion
356 Object.Call_In_Progress := null;
362 STPO.Write_Lock (Entry_Call.Self);
363 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
364 STPO.Unlock (Entry_Call.Self);
371 Requeue_Call (Self_ID, Object, Entry_Call);
374 elsif Entry_Call.Mode /= Conditional_Call
375 or else not Entry_Call.With_Abort
378 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
380 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
381 Queuing.Count_Waiting (Object.Entry_Queues (E))
383 -- This violates the Max_Entry_Queue_Length restriction,
384 -- raise Program_Error.
386 Entry_Call.Exception_To_Raise := Program_Error'Identity;
392 STPO.Write_Lock (Entry_Call.Self);
393 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
394 STPO.Unlock (Entry_Call.Self);
400 Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
401 Update_For_Queue_To_PO (Entry_Call, Entry_Call.With_Abort);
404 -- Conditional_Call and With_Abort
410 STPO.Write_Lock (Entry_Call.Self);
411 pragma Assert (Entry_Call.State >= Was_Abortable);
412 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
413 STPO.Unlock (Entry_Call.Self);
422 Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
425 ------------------------
426 -- PO_Service_Entries --
427 ------------------------
429 procedure PO_Service_Entries
431 Object : Entries.Protection_Entries_Access;
432 Unlock_Object : Boolean := True)
434 E : Protected_Entry_Index;
436 Entry_Call : Entry_Call_Link;
440 Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
442 exit when Entry_Call = null;
444 E := Protected_Entry_Index (Entry_Call.E);
446 -- Not abortable while service is in progress
448 if Entry_Call.State = Now_Abortable then
449 Entry_Call.State := Was_Abortable;
452 Object.Call_In_Progress := Entry_Call;
455 if Runtime_Traces then
456 Send_Trace_Info (PO_Run, Self_ID,
457 Entry_Call.Self, Entry_Index (E));
461 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
464 (Object.Find_Body_Index (Object.Compiler_Info, E)).Action
465 (Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
469 Queuing.Broadcast_Program_Error
470 (Self_ID, Object, Entry_Call);
473 if Object.Call_In_Progress = null then
474 Requeue_Call (Self_ID, Object, Entry_Call);
475 exit when Entry_Call.State = Cancelled;
478 Object.Call_In_Progress := null;
479 Caller := Entry_Call.Self;
485 STPO.Write_Lock (Caller);
486 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
487 STPO.Unlock (Caller);
495 if Unlock_Object then
496 Unlock_Entries (Object);
498 end PO_Service_Entries;
500 ---------------------
501 -- Protected_Count --
502 ---------------------
504 function Protected_Count
505 (Object : Protection_Entries'Class;
506 E : Protected_Entry_Index) return Natural
509 return Queuing.Count_Waiting (Object.Entry_Queues (E));
512 --------------------------
513 -- Protected_Entry_Call --
514 --------------------------
516 -- Compiler interface only (do not call from within the RTS)
525 -- X : protected_entry_index := 1;
526 -- B85b : communication_block;
527 -- communication_blockIP (B85b);
530 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
531 -- null_address, conditional_call, B85b, objectF => 0);
533 -- if cancelled (B85b) then
540 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
543 -- The initial part of this procedure does not need to lock the the calling
544 -- task's ATCB, up to the point where the call record first may be queued
545 -- (PO_Do_Or_Queue), since before that no other task will have access to
548 -- If this is a call made inside of an abort deferred region, the call
549 -- should be never abortable.
551 -- If the call was not queued abortably, we need to wait until it is before
552 -- proceeding with the abortable part.
554 -- There are some heuristics here, just to save time for frequently
555 -- occurring cases. For example, we check Initially_Abortable to try to
556 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
557 -- for async. entry calls is to be queued abortably.
559 -- Another heuristic uses the Block.Enqueued to try to avoid calling
560 -- Cancel_Protected_Entry_Call if the call can be served immediately.
562 procedure Protected_Entry_Call
563 (Object : Protection_Entries_Access;
564 E : Protected_Entry_Index;
565 Uninterpreted_Data : System.Address;
567 Block : out Communication_Block)
569 Self_ID : constant Task_Id := STPO.Self;
570 Entry_Call : Entry_Call_Link;
571 Initially_Abortable : Boolean;
572 Ceiling_Violation : Boolean;
576 (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
578 if Runtime_Traces then
579 Send_Trace_Info (PO_Call, Entry_Index (E));
582 if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
584 (Storage_Error'Identity, "not enough ATC nesting levels");
587 -- If pragma Detect_Blocking is active then Program_Error must be
588 -- raised if this potentially blocking operation is called from a
592 and then Self_ID.Common.Protected_Action_Nesting > 0
594 Ada.Exceptions.Raise_Exception
595 (Program_Error'Identity, "potentially blocking operation");
598 -- Self_ID.Deferral_Level should be 0, except when called from Finalize,
599 -- where abort is already deferred.
601 Initialization.Defer_Abort_Nestable (Self_ID);
602 Lock_Entries (Object, Ceiling_Violation);
604 if Ceiling_Violation then
606 -- Failed ceiling check
608 Initialization.Undefer_Abort_Nestable (Self_ID);
612 Block.Self := Self_ID;
613 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
615 (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
616 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
618 Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
619 Entry_Call.Next := null;
620 Entry_Call.Mode := Mode;
621 Entry_Call.Cancellation_Attempted := False;
623 if Self_ID.Deferral_Level > 1 then
624 Entry_Call.State := Never_Abortable;
626 Entry_Call.State := Now_Abortable;
629 Entry_Call.E := Entry_Index (E);
630 Entry_Call.Prio := STPO.Get_Priority (Self_ID);
631 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
632 Entry_Call.Called_PO := To_Address (Object);
633 Entry_Call.Called_Task := null;
634 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
635 Entry_Call.With_Abort := True;
637 PO_Do_Or_Queue (Self_ID, Object, Entry_Call);
638 Initially_Abortable := Entry_Call.State = Now_Abortable;
639 PO_Service_Entries (Self_ID, Object);
641 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
642 -- for completed or cancelled calls. (This is a heuristic, only.)
644 if Entry_Call.State >= Done then
646 -- Once State >= Done it will not change any more
652 STPO.Write_Lock (Self_ID);
653 Utilities.Exit_One_ATC_Level (Self_ID);
654 STPO.Unlock (Self_ID);
660 Block.Enqueued := False;
661 Block.Cancelled := Entry_Call.State = Cancelled;
662 Initialization.Undefer_Abort_Nestable (Self_ID);
663 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
667 -- In this case we cannot conclude anything, since State can change
673 -- Now for the general case
675 if Mode = Asynchronous_Call then
677 -- Try to avoid an expensive call
679 if not Initially_Abortable then
682 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
685 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
689 elsif Mode < Asynchronous_Call then
691 -- Simple_Call or Conditional_Call
695 Entry_Calls.Wait_For_Completion (Entry_Call);
699 STPO.Write_Lock (Self_ID);
700 Entry_Calls.Wait_For_Completion (Entry_Call);
701 STPO.Unlock (Self_ID);
704 Block.Cancelled := Entry_Call.State = Cancelled;
707 pragma Assert (False);
711 Initialization.Undefer_Abort_Nestable (Self_ID);
712 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
713 end Protected_Entry_Call;
719 procedure Requeue_Call
721 Object : Protection_Entries_Access;
722 Entry_Call : Entry_Call_Link)
724 New_Object : Protection_Entries_Access;
725 Ceiling_Violation : Boolean;
727 E : Protected_Entry_Index;
730 New_Object := To_Protection (Entry_Call.Called_PO);
732 if New_Object = null then
734 -- Call is to be requeued to a task entry
740 Result := Rendezvous.Task_Do_Or_Queue (Self_Id, Entry_Call);
743 Queuing.Broadcast_Program_Error
744 (Self_Id, Object, Entry_Call, RTS_Locked => True);
752 -- Call should be requeued to a PO
754 if Object /= New_Object then
756 -- Requeue is to different PO
758 Lock_Entries (New_Object, Ceiling_Violation);
760 if Ceiling_Violation then
761 Object.Call_In_Progress := null;
762 Queuing.Broadcast_Program_Error (Self_Id, Object, Entry_Call);
765 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call);
766 PO_Service_Entries (Self_Id, New_Object);
770 -- Requeue is to same protected object
772 -- ??? Try to compensate apparent failure of the scheduler on some
773 -- OS (e.g VxWorks) to give higher priority tasks a chance to run
778 if Entry_Call.With_Abort
779 and then Entry_Call.Cancellation_Attempted
781 -- If this is a requeue with abort and someone tried to cancel
782 -- this call, cancel it at this point.
784 Entry_Call.State := Cancelled;
788 if not Entry_Call.With_Abort
789 or else Entry_Call.Mode /= Conditional_Call
791 E := Protected_Entry_Index (Entry_Call.E);
793 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
795 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
796 Queuing.Count_Waiting (Object.Entry_Queues (E))
798 -- This violates the Max_Entry_Queue_Length restriction,
799 -- raise Program_Error.
801 Entry_Call.Exception_To_Raise := Program_Error'Identity;
807 STPO.Write_Lock (Entry_Call.Self);
808 Initialization.Wakeup_Entry_Caller
809 (Self_Id, Entry_Call, Done);
810 STPO.Unlock (Entry_Call.Self);
818 (New_Object.Entry_Queues (E), Entry_Call);
819 Update_For_Queue_To_PO (Entry_Call, Entry_Call.With_Abort);
823 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call);
829 ----------------------------
830 -- Protected_Entry_Caller --
831 ----------------------------
833 function Protected_Entry_Caller
834 (Object : Protection_Entries'Class) return Task_Id is
836 return Object.Call_In_Progress.Self;
837 end Protected_Entry_Caller;
839 -----------------------------
840 -- Requeue_Protected_Entry --
841 -----------------------------
843 -- Compiler interface only (do not call from within the RTS)
852 -- procedure rPT__E10b (O : address; P : address; E :
853 -- protected_entry_index) is
854 -- type rTVP is access rTV;
856 -- _object : rTVP := rTVP!(O);
859 -- rR : protection renames _object._object;
860 -- vP : integer renames _object.v;
861 -- bP : boolean renames _object.b;
865 -- requeue_protected_entry (rR'unchecked_access, rR'
866 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
870 -- complete_entry_body (_object._object'unchecked_access, objectF =>
875 -- abort_undefer.all;
876 -- exceptional_complete_entry_body (_object._object'
877 -- unchecked_access, current_exception, objectF => 0);
881 procedure Requeue_Protected_Entry
882 (Object : Protection_Entries_Access;
883 New_Object : Protection_Entries_Access;
884 E : Protected_Entry_Index;
885 With_Abort : Boolean)
887 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
891 (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
892 pragma Assert (STPO.Self.Deferral_Level > 0);
894 Entry_Call.E := Entry_Index (E);
895 Entry_Call.Called_PO := To_Address (New_Object);
896 Entry_Call.Called_Task := null;
897 Entry_Call.With_Abort := With_Abort;
898 Object.Call_In_Progress := null;
899 end Requeue_Protected_Entry;
901 -------------------------------------
902 -- Requeue_Task_To_Protected_Entry --
903 -------------------------------------
905 -- Compiler interface only (do not call from within the RTS)
916 -- accept_call (1, A79b);
918 -- requeue_task_to_protected_entry (rTV!(r)._object'
919 -- unchecked_access, 2, false, new_objectF => 0);
922 -- complete_rendezvous;
925 -- when all others =>
926 -- exceptional_complete_rendezvous (get_gnat_exception);
929 procedure Requeue_Task_To_Protected_Entry
930 (New_Object : Protection_Entries_Access;
931 E : Protected_Entry_Index;
932 With_Abort : Boolean)
934 Self_ID : constant Task_Id := STPO.Self;
935 Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
938 Initialization.Defer_Abort (Self_ID);
940 -- We do not need to lock Self_ID here since the call is not abortable
941 -- at this point, and therefore, the caller cannot cancel the call.
943 Entry_Call.Needs_Requeue := True;
944 Entry_Call.With_Abort := With_Abort;
945 Entry_Call.Called_PO := To_Address (New_Object);
946 Entry_Call.Called_Task := null;
947 Entry_Call.E := Entry_Index (E);
948 Initialization.Undefer_Abort (Self_ID);
949 end Requeue_Task_To_Protected_Entry;
951 ---------------------
952 -- Service_Entries --
953 ---------------------
955 procedure Service_Entries (Object : Protection_Entries_Access) is
956 Self_ID : constant Task_Id := STPO.Self;
958 PO_Service_Entries (Self_ID, Object);
961 --------------------------------
962 -- Timed_Protected_Entry_Call --
963 --------------------------------
965 -- Compiler interface only (do not call from within the RTS)
967 procedure Timed_Protected_Entry_Call
968 (Object : Protection_Entries_Access;
969 E : Protected_Entry_Index;
970 Uninterpreted_Data : System.Address;
973 Entry_Call_Successful : out Boolean)
975 Self_Id : constant Task_Id := STPO.Self;
976 Entry_Call : Entry_Call_Link;
977 Ceiling_Violation : Boolean;
980 pragma Unreferenced (Yielded);
983 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
984 Raise_Exception (Storage_Error'Identity,
985 "not enough ATC nesting levels");
988 -- If pragma Detect_Blocking is active then Program_Error must be
989 -- raised if this potentially blocking operation is called from a
993 and then Self_Id.Common.Protected_Action_Nesting > 0
995 Ada.Exceptions.Raise_Exception
996 (Program_Error'Identity, "potentially blocking operation");
999 if Runtime_Traces then
1000 Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
1003 Initialization.Defer_Abort (Self_Id);
1004 Lock_Entries (Object, Ceiling_Violation);
1006 if Ceiling_Violation then
1007 Initialization.Undefer_Abort (Self_Id);
1008 raise Program_Error;
1011 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
1013 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
1014 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
1016 Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
1017 Entry_Call.Next := null;
1018 Entry_Call.Mode := Timed_Call;
1019 Entry_Call.Cancellation_Attempted := False;
1021 if Self_Id.Deferral_Level > 1 then
1022 Entry_Call.State := Never_Abortable;
1024 Entry_Call.State := Now_Abortable;
1027 Entry_Call.E := Entry_Index (E);
1028 Entry_Call.Prio := STPO.Get_Priority (Self_Id);
1029 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
1030 Entry_Call.Called_PO := To_Address (Object);
1031 Entry_Call.Called_Task := null;
1032 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
1033 Entry_Call.With_Abort := True;
1035 PO_Do_Or_Queue (Self_Id, Object, Entry_Call);
1036 PO_Service_Entries (Self_Id, Object);
1041 STPO.Write_Lock (Self_Id);
1044 -- Try to avoid waiting for completed or cancelled calls
1046 if Entry_Call.State >= Done then
1047 Utilities.Exit_One_ATC_Level (Self_Id);
1052 STPO.Unlock (Self_Id);
1055 Entry_Call_Successful := Entry_Call.State = Done;
1056 Initialization.Undefer_Abort (Self_Id);
1057 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1061 Entry_Calls.Wait_For_Completion_With_Timeout
1062 (Entry_Call, Timeout, Mode, Yielded);
1067 STPO.Unlock (Self_Id);
1070 -- ??? Do we need to yield in case Yielded is False
1072 Initialization.Undefer_Abort (Self_Id);
1073 Entry_Call_Successful := Entry_Call.State = Done;
1074 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1075 end Timed_Protected_Entry_Call;
1077 ----------------------------
1078 -- Update_For_Queue_To_PO --
1079 ----------------------------
1081 -- Update the state of an existing entry call, based on
1082 -- whether the current queuing action is with or without abort.
1083 -- Call this only while holding the server's lock.
1084 -- It returns with the server's lock released.
1086 New_State : constant array (Boolean, Entry_Call_State)
1087 of Entry_Call_State :=
1089 (Never_Abortable => Never_Abortable,
1090 Not_Yet_Abortable => Now_Abortable,
1091 Was_Abortable => Now_Abortable,
1092 Now_Abortable => Now_Abortable,
1094 Cancelled => Cancelled),
1096 (Never_Abortable => Never_Abortable,
1097 Not_Yet_Abortable => Not_Yet_Abortable,
1098 Was_Abortable => Was_Abortable,
1099 Now_Abortable => Now_Abortable,
1101 Cancelled => Cancelled)
1104 procedure Update_For_Queue_To_PO
1105 (Entry_Call : Entry_Call_Link;
1106 With_Abort : Boolean)
1108 Old : constant Entry_Call_State := Entry_Call.State;
1111 pragma Assert (Old < Done);
1113 Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1115 if Entry_Call.Mode = Asynchronous_Call then
1116 if Old < Was_Abortable and then
1117 Entry_Call.State = Now_Abortable
1123 STPO.Write_Lock (Entry_Call.Self);
1125 if Entry_Call.Self.Common.State = Async_Select_Sleep then
1126 STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1129 STPO.Unlock (Entry_Call.Self);
1137 elsif Entry_Call.Mode = Conditional_Call then
1138 pragma Assert (Entry_Call.State < Was_Abortable);
1141 end Update_For_Queue_To_PO;
1143 end System.Tasking.Protected_Objects.Operations;