1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
5 -- SYSTEM.TASKING.PROTECTED_OBJECTS.OPERATIONS --
9 -- Copyright (C) 1998-2016, Free Software Foundation, Inc. --
11 -- GNARL is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNARL was developed by the GNARL team at Florida State University. --
28 -- Extensive contributions were provided by Ada Core Technologies, Inc. --
30 ------------------------------------------------------------------------------
32 -- This package contains all extended primitives related to Protected_Objects
35 -- The handling of protected objects with no entries is done in
36 -- System.Tasking.Protected_Objects, the simple routines for protected
37 -- objects with entries in System.Tasking.Protected_Objects.Entries.
39 -- The split between Entries and Operations is needed to break circular
40 -- dependencies inside the run time.
42 -- This package contains all primitives related to Protected_Objects.
43 -- Note: the compiler generates direct calls to this interface, via Rtsfind.
45 with System
.Task_Primitives
.Operations
;
46 with System
.Tasking
.Entry_Calls
;
47 with System
.Tasking
.Queuing
;
48 with System
.Tasking
.Rendezvous
;
49 with System
.Tasking
.Utilities
;
50 with System
.Tasking
.Debug
;
51 with System
.Parameters
;
52 with System
.Traces
.Tasking
;
53 with System
.Restrictions
;
55 with System
.Tasking
.Initialization
;
56 pragma Elaborate_All
(System
.Tasking
.Initialization
);
57 -- Insures that tasking is initialized if any protected objects are created
59 package body System
.Tasking
.Protected_Objects
.Operations
is
61 package STPO
renames System
.Task_Primitives
.Operations
;
68 use System
.Restrictions
;
69 use System
.Restrictions
.Rident
;
71 use System
.Traces
.Tasking
;
73 -----------------------
74 -- Local Subprograms --
75 -----------------------
77 procedure Update_For_Queue_To_PO
78 (Entry_Call
: Entry_Call_Link
;
79 With_Abort
: Boolean);
80 pragma Inline
(Update_For_Queue_To_PO
);
81 -- Update the state of an existing entry call to reflect the fact that it
82 -- is being enqueued, based on whether the current queuing action is with
83 -- or without abort. Call this only while holding the PO's lock. It returns
84 -- with the PO's lock still held.
86 procedure Requeue_Call
88 Object
: Protection_Entries_Access
;
89 Entry_Call
: Entry_Call_Link
);
90 -- Handle requeue of Entry_Call.
91 -- In particular, queue the call if needed, or service it immediately
94 ---------------------------------
95 -- Cancel_Protected_Entry_Call --
96 ---------------------------------
98 -- Compiler interface only (do not call from within the RTS)
100 -- This should have analogous effect to Cancel_Task_Entry_Call, setting
101 -- the value of Block.Cancelled instead of returning the parameter value
104 -- The effect should be idempotent, since the call may already have been
118 -- X : protected_entry_index := 1;
119 -- B80b : communication_block;
120 -- communication_blockIP (B80b);
126 -- procedure _clean is
128 -- if enqueued (B80b) then
129 -- cancel_protected_entry_call (B80b);
135 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
136 -- null_address, asynchronous_call, B80b, objectF => 0);
137 -- if enqueued (B80b) then
145 -- when _abort_signal =>
146 -- abort_undefer.all;
150 -- if not cancelled (B80b) then
155 -- If the entry call completes after we get into the abortable part,
156 -- Abort_Signal should be raised and ATC will take us to the at-end
157 -- handler, which will call _clean.
159 -- If the entry call returns with the call already completed, we can skip
160 -- this, and use the "if enqueued()" to go past the at-end handler, but we
161 -- will still call _clean.
163 -- If the abortable part completes before the entry call is Done, it will
166 -- If the entry call or the abortable part raises an exception,
167 -- we will still call _clean, but the value of Cancelled should not matter.
169 -- Whoever calls _clean first gets to decide whether the call
170 -- has been "cancelled".
172 -- Enqueued should be true if there is any chance that the call is still on
173 -- a queue. It seems to be safe to make it True if the call was Onqueue at
174 -- some point before return from Protected_Entry_Call.
176 -- Cancelled should be true iff the abortable part completed
177 -- and succeeded in cancelling the entry call before it completed.
180 -- The need for Enqueued is less obvious. The "if enqueued ()" tests are
181 -- not necessary, since Cancel_Protected_Entry_Call/Protected_Entry_Call
182 -- must do the same test internally, with locking. The one that makes
183 -- cancellation conditional may be a useful heuristic since at least 1/2
184 -- the time the call should be off-queue by that point. The other one seems
185 -- totally useless, since Protected_Entry_Call must do the same check and
186 -- then possibly wait for the call to be abortable, internally.
188 -- We can check Call.State here without locking the caller's mutex,
189 -- since the call must be over after returning from Wait_For_Completion.
190 -- No other task can access the call record at this point.
192 procedure Cancel_Protected_Entry_Call
193 (Block
: in out Communication_Block
) is
195 Entry_Calls
.Try_To_Cancel_Entry_Call
(Block
.Cancelled
);
196 end Cancel_Protected_Entry_Call
;
202 function Cancelled
(Block
: Communication_Block
) return Boolean is
204 return Block
.Cancelled
;
207 -------------------------
208 -- Complete_Entry_Body --
209 -------------------------
211 procedure Complete_Entry_Body
(Object
: Protection_Entries_Access
) is
213 Exceptional_Complete_Entry_Body
(Object
, Ada
.Exceptions
.Null_Id
);
214 end Complete_Entry_Body
;
220 function Enqueued
(Block
: Communication_Block
) return Boolean is
222 return Block
.Enqueued
;
225 -------------------------------------
226 -- Exceptional_Complete_Entry_Body --
227 -------------------------------------
229 procedure Exceptional_Complete_Entry_Body
230 (Object
: Protection_Entries_Access
;
231 Ex
: Ada
.Exceptions
.Exception_Id
)
233 procedure Transfer_Occurrence
234 (Target
: Ada
.Exceptions
.Exception_Occurrence_Access
;
235 Source
: Ada
.Exceptions
.Exception_Occurrence
);
236 pragma Import
(C
, Transfer_Occurrence
, "__gnat_transfer_occurrence");
238 Entry_Call
: constant Entry_Call_Link
:= Object
.Call_In_Progress
;
243 (Debug
.Trace
(STPO
.Self
, "Exceptional_Complete_Entry_Body", 'P'));
245 -- We must have abort deferred, since we are inside a protected
248 if Entry_Call
/= null then
250 -- The call was not requeued
252 Entry_Call
.Exception_To_Raise
:= Ex
;
254 if Ex
/= Ada
.Exceptions
.Null_Id
then
256 -- An exception was raised and abort was deferred, so adjust
257 -- before propagating, otherwise the task will stay with deferral
258 -- enabled for its remaining life.
260 Self_Id
:= STPO
.Self
;
262 if not ZCX_By_Default
then
263 Initialization
.Undefer_Abort_Nestable
(Self_Id
);
267 (Entry_Call
.Self
.Common
.Compiler_Data
.Current_Excep
'Access,
268 Self_Id
.Common
.Compiler_Data
.Current_Excep
);
271 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
272 -- PO_Service_Entries on return.
276 if Runtime_Traces
then
278 -- ??? Entry_Call can be null
280 Send_Trace_Info
(PO_Done
, Entry_Call
.Self
);
282 end Exceptional_Complete_Entry_Body
;
288 procedure PO_Do_Or_Queue
290 Object
: Protection_Entries_Access
;
291 Entry_Call
: Entry_Call_Link
)
293 E
: constant Protected_Entry_Index
:=
294 Protected_Entry_Index
(Entry_Call
.E
);
295 Index
: constant Protected_Entry_Index
:=
296 Object
.Find_Body_Index
(Object
.Compiler_Info
, E
);
297 Barrier_Value
: Boolean;
298 Queue_Length
: Natural;
300 -- When the Action procedure for an entry body returns, it is either
301 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
302 -- is queued, having executed a requeue statement.
305 Object
.Entry_Bodies
(Index
).Barrier
(Object
.Compiler_Info
, E
);
307 if Barrier_Value
then
309 -- Not abortable while service is in progress
311 if Entry_Call
.State
= Now_Abortable
then
312 Entry_Call
.State
:= Was_Abortable
;
315 Object
.Call_In_Progress
:= Entry_Call
;
318 (Debug
.Trace
(Self_ID
, "PODOQ: start entry body", 'P'));
319 Object
.Entry_Bodies
(Index
).Action
(
320 Object
.Compiler_Info
, Entry_Call
.Uninterpreted_Data
, E
);
322 if Object
.Call_In_Progress
/= null then
324 -- Body of current entry served call to completion
326 Object
.Call_In_Progress
:= null;
332 STPO
.Write_Lock
(Entry_Call
.Self
);
333 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
334 STPO
.Unlock
(Entry_Call
.Self
);
341 Requeue_Call
(Self_ID
, Object
, Entry_Call
);
344 elsif Entry_Call
.Mode
/= Conditional_Call
345 or else not Entry_Call
.With_Abort
347 if Run_Time_Restrictions
.Set
(Max_Entry_Queue_Length
)
348 or else Object
.Entry_Queue_Maxes
/= null
350 -- Need to check the queue length. Computing the length is an
351 -- unusual case and is slow (need to walk the queue).
353 Queue_Length
:= Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
));
355 if (Run_Time_Restrictions
.Set
(Max_Entry_Queue_Length
)
356 and then Queue_Length
>=
357 Run_Time_Restrictions
.Value
(Max_Entry_Queue_Length
))
359 (Object
.Entry_Queue_Maxes
/= null
360 and then Object
.Entry_Queue_Maxes
(Index
) /= 0
361 and then Queue_Length
>= Object
.Entry_Queue_Maxes
(Index
))
363 -- This violates the Max_Entry_Queue_Length restriction or the
364 -- Max_Queue_Length bound, raise Program_Error.
366 Entry_Call
.Exception_To_Raise
:= Program_Error
'Identity;
372 STPO
.Write_Lock
(Entry_Call
.Self
);
373 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
374 STPO
.Unlock
(Entry_Call
.Self
);
384 -- Do the work: queue the call
386 Queuing
.Enqueue
(Object
.Entry_Queues
(E
), Entry_Call
);
387 Update_For_Queue_To_PO
(Entry_Call
, Entry_Call
.With_Abort
);
391 -- Conditional_Call and With_Abort
397 STPO
.Write_Lock
(Entry_Call
.Self
);
398 pragma Assert
(Entry_Call
.State
/= Not_Yet_Abortable
);
399 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Cancelled
);
400 STPO
.Unlock
(Entry_Call
.Self
);
409 Queuing
.Broadcast_Program_Error
(Self_ID
, Object
, Entry_Call
);
412 ------------------------
413 -- PO_Service_Entries --
414 ------------------------
416 procedure PO_Service_Entries
418 Object
: Entries
.Protection_Entries_Access
;
419 Unlock_Object
: Boolean := True)
421 E
: Protected_Entry_Index
;
423 Entry_Call
: Entry_Call_Link
;
427 Queuing
.Select_Protected_Entry_Call
(Self_ID
, Object
, Entry_Call
);
429 exit when Entry_Call
= null;
431 E
:= Protected_Entry_Index
(Entry_Call
.E
);
433 -- Not abortable while service is in progress
435 if Entry_Call
.State
= Now_Abortable
then
436 Entry_Call
.State
:= Was_Abortable
;
439 Object
.Call_In_Progress
:= Entry_Call
;
442 if Runtime_Traces
then
443 Send_Trace_Info
(PO_Run
, Self_ID
,
444 Entry_Call
.Self
, Entry_Index
(E
));
448 (Debug
.Trace
(Self_ID
, "POSE: start entry body", 'P'));
451 (Object
.Find_Body_Index
(Object
.Compiler_Info
, E
)).Action
452 (Object
.Compiler_Info
, Entry_Call
.Uninterpreted_Data
, E
);
456 Queuing
.Broadcast_Program_Error
457 (Self_ID
, Object
, Entry_Call
);
460 if Object
.Call_In_Progress
= null then
461 Requeue_Call
(Self_ID
, Object
, Entry_Call
);
462 exit when Entry_Call
.State
= Cancelled
;
465 Object
.Call_In_Progress
:= null;
466 Caller
:= Entry_Call
.Self
;
472 STPO
.Write_Lock
(Caller
);
473 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
474 STPO
.Unlock
(Caller
);
482 if Unlock_Object
then
483 Unlock_Entries
(Object
);
485 end PO_Service_Entries
;
487 ---------------------
488 -- Protected_Count --
489 ---------------------
491 function Protected_Count
492 (Object
: Protection_Entries
'Class;
493 E
: Protected_Entry_Index
) return Natural
496 return Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
));
499 --------------------------
500 -- Protected_Entry_Call --
501 --------------------------
503 -- Compiler interface only (do not call from within the RTS)
512 -- X : protected_entry_index := 1;
513 -- B85b : communication_block;
514 -- communication_blockIP (B85b);
517 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
518 -- null_address, conditional_call, B85b, objectF => 0);
520 -- if cancelled (B85b) then
527 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
530 -- The initial part of this procedure does not need to lock the calling
531 -- task's ATCB, up to the point where the call record first may be queued
532 -- (PO_Do_Or_Queue), since before that no other task will have access to
535 -- If this is a call made inside of an abort deferred region, the call
536 -- should be never abortable.
538 -- If the call was not queued abortably, we need to wait until it is before
539 -- proceeding with the abortable part.
541 -- There are some heuristics here, just to save time for frequently
542 -- occurring cases. For example, we check Initially_Abortable to try to
543 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
544 -- for async. entry calls is to be queued abortably.
546 -- Another heuristic uses the Block.Enqueued to try to avoid calling
547 -- Cancel_Protected_Entry_Call if the call can be served immediately.
549 procedure Protected_Entry_Call
550 (Object
: Protection_Entries_Access
;
551 E
: Protected_Entry_Index
;
552 Uninterpreted_Data
: System
.Address
;
554 Block
: out Communication_Block
)
556 Self_ID
: constant Task_Id
:= STPO
.Self
;
557 Entry_Call
: Entry_Call_Link
;
558 Initially_Abortable
: Boolean;
559 Ceiling_Violation
: Boolean;
563 (Debug
.Trace
(Self_ID
, "Protected_Entry_Call", 'P'));
565 if Runtime_Traces
then
566 Send_Trace_Info
(PO_Call
, Entry_Index
(E
));
569 if Self_ID
.ATC_Nesting_Level
= ATC_Level
'Last then
570 raise Storage_Error
with "not enough ATC nesting levels";
573 -- If pragma Detect_Blocking is active then Program_Error must be
574 -- raised if this potentially blocking operation is called from a
578 and then Self_ID
.Common
.Protected_Action_Nesting
> 0
580 raise Program_Error
with "potentially blocking operation";
583 -- Self_ID.Deferral_Level should be 0, except when called from Finalize,
584 -- where abort is already deferred.
586 Initialization
.Defer_Abort_Nestable
(Self_ID
);
587 Lock_Entries_With_Status
(Object
, Ceiling_Violation
);
589 if Ceiling_Violation
then
591 -- Failed ceiling check
593 Initialization
.Undefer_Abort_Nestable
(Self_ID
);
597 Block
.Self
:= Self_ID
;
598 Self_ID
.ATC_Nesting_Level
:= Self_ID
.ATC_Nesting_Level
+ 1;
600 (Debug
.Trace
(Self_ID
, "PEC: entered ATC level: " &
601 ATC_Level
'Image (Self_ID
.ATC_Nesting_Level
), 'A'));
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;
609 (if Self_ID
.Deferral_Level
> 1
610 then Never_Abortable
else Now_Abortable
);
612 Entry_Call
.E
:= Entry_Index
(E
);
613 Entry_Call
.Prio
:= STPO
.Get_Priority
(Self_ID
);
614 Entry_Call
.Uninterpreted_Data
:= Uninterpreted_Data
;
615 Entry_Call
.Called_PO
:= To_Address
(Object
);
616 Entry_Call
.Called_Task
:= null;
617 Entry_Call
.Exception_To_Raise
:= Ada
.Exceptions
.Null_Id
;
618 Entry_Call
.With_Abort
:= True;
620 PO_Do_Or_Queue
(Self_ID
, Object
, Entry_Call
);
621 Initially_Abortable
:= Entry_Call
.State
= Now_Abortable
;
622 PO_Service_Entries
(Self_ID
, Object
);
624 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
625 -- for completed or cancelled calls. (This is a heuristic, only.)
627 if Entry_Call
.State
>= Done
then
629 -- Once State >= Done it will not change any more
635 STPO
.Write_Lock
(Self_ID
);
636 Utilities
.Exit_One_ATC_Level
(Self_ID
);
637 STPO
.Unlock
(Self_ID
);
643 Block
.Enqueued
:= False;
644 Block
.Cancelled
:= Entry_Call
.State
= Cancelled
;
645 Initialization
.Undefer_Abort_Nestable
(Self_ID
);
646 Entry_Calls
.Check_Exception
(Self_ID
, Entry_Call
);
650 -- In this case we cannot conclude anything, since State can change
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
665 Entry_Calls
.Wait_Until_Abortable
(Self_ID
, Entry_Call
);
668 Entry_Calls
.Wait_Until_Abortable
(Self_ID
, Entry_Call
);
674 when Simple_Call | Conditional_Call
=>
677 Entry_Calls
.Wait_For_Completion
(Entry_Call
);
681 STPO
.Write_Lock
(Self_ID
);
682 Entry_Calls
.Wait_For_Completion
(Entry_Call
);
683 STPO
.Unlock
(Self_ID
);
686 Block
.Cancelled
:= Entry_Call
.State
= Cancelled
;
688 when Asynchronous_Call | Timed_Call
=>
689 pragma Assert
(False);
694 Initialization
.Undefer_Abort_Nestable
(Self_ID
);
695 Entry_Calls
.Check_Exception
(Self_ID
, Entry_Call
);
696 end Protected_Entry_Call
;
702 procedure Requeue_Call
704 Object
: Protection_Entries_Access
;
705 Entry_Call
: Entry_Call_Link
)
707 New_Object
: Protection_Entries_Access
;
708 Ceiling_Violation
: Boolean;
710 E
: Protected_Entry_Index
;
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
723 Result
:= Rendezvous
.Task_Do_Or_Queue
(Self_Id
, Entry_Call
);
726 Queuing
.Broadcast_Program_Error
727 (Self_Id
, Object
, Entry_Call
, RTS_Locked
=> True);
735 -- Call should be requeued to a PO
737 if Object
/= New_Object
then
739 -- Requeue is to different PO
741 Lock_Entries_With_Status
(New_Object
, Ceiling_Violation
);
743 if Ceiling_Violation
then
744 Object
.Call_In_Progress
:= null;
745 Queuing
.Broadcast_Program_Error
(Self_Id
, Object
, Entry_Call
);
748 PO_Do_Or_Queue
(Self_Id
, New_Object
, Entry_Call
);
749 PO_Service_Entries
(Self_Id
, New_Object
);
753 -- Requeue is to same protected object
755 -- ??? Try to compensate apparent failure of the scheduler on some
756 -- OS (e.g VxWorks) to give higher priority tasks a chance to run
759 STPO
.Yield
(Do_Yield
=> False);
761 if Entry_Call
.With_Abort
762 and then Entry_Call
.Cancellation_Attempted
764 -- If this is a requeue with abort and someone tried to cancel
765 -- this call, cancel it at this point.
767 Entry_Call
.State
:= Cancelled
;
771 if not Entry_Call
.With_Abort
772 or else Entry_Call
.Mode
/= Conditional_Call
774 E
:= Protected_Entry_Index
(Entry_Call
.E
);
776 if Run_Time_Restrictions
.Set
(Max_Entry_Queue_Length
)
778 Run_Time_Restrictions
.Value
(Max_Entry_Queue_Length
) <=
779 Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
))
781 -- This violates the Max_Entry_Queue_Length restriction,
782 -- raise Program_Error.
784 Entry_Call
.Exception_To_Raise
:= Program_Error
'Identity;
790 STPO
.Write_Lock
(Entry_Call
.Self
);
791 Initialization
.Wakeup_Entry_Caller
792 (Self_Id
, Entry_Call
, Done
);
793 STPO
.Unlock
(Entry_Call
.Self
);
801 (New_Object
.Entry_Queues
(E
), Entry_Call
);
802 Update_For_Queue_To_PO
(Entry_Call
, Entry_Call
.With_Abort
);
806 PO_Do_Or_Queue
(Self_Id
, New_Object
, Entry_Call
);
812 ----------------------------
813 -- Protected_Entry_Caller --
814 ----------------------------
816 function Protected_Entry_Caller
817 (Object
: Protection_Entries
'Class) return Task_Id
is
819 return Object
.Call_In_Progress
.Self
;
820 end Protected_Entry_Caller
;
822 -----------------------------
823 -- Requeue_Protected_Entry --
824 -----------------------------
826 -- Compiler interface only (do not call from within the RTS)
835 -- procedure rPT__E10b (O : address; P : address; E :
836 -- protected_entry_index) is
837 -- type rTVP is access rTV;
839 -- _object : rTVP := rTVP!(O);
842 -- rR : protection renames _object._object;
843 -- vP : integer renames _object.v;
844 -- bP : boolean renames _object.b;
848 -- requeue_protected_entry (rR'unchecked_access, rR'
849 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
853 -- complete_entry_body (_object._object'unchecked_access, objectF =>
858 -- abort_undefer.all;
859 -- exceptional_complete_entry_body (_object._object'
860 -- unchecked_access, current_exception, objectF => 0);
864 procedure Requeue_Protected_Entry
865 (Object
: Protection_Entries_Access
;
866 New_Object
: Protection_Entries_Access
;
867 E
: Protected_Entry_Index
;
868 With_Abort
: Boolean)
870 Entry_Call
: constant Entry_Call_Link
:= Object
.Call_In_Progress
;
874 (Debug
.Trace
(STPO
.Self
, "Requeue_Protected_Entry", 'P'));
875 pragma Assert
(STPO
.Self
.Deferral_Level
> 0);
877 Entry_Call
.E
:= Entry_Index
(E
);
878 Entry_Call
.Called_PO
:= To_Address
(New_Object
);
879 Entry_Call
.Called_Task
:= null;
880 Entry_Call
.With_Abort
:= With_Abort
;
881 Object
.Call_In_Progress
:= null;
882 end Requeue_Protected_Entry
;
884 -------------------------------------
885 -- Requeue_Task_To_Protected_Entry --
886 -------------------------------------
888 -- Compiler interface only (do not call from within the RTS)
899 -- accept_call (1, A79b);
901 -- requeue_task_to_protected_entry (rTV!(r)._object'
902 -- unchecked_access, 2, false, new_objectF => 0);
905 -- complete_rendezvous;
908 -- when all others =>
909 -- exceptional_complete_rendezvous (get_gnat_exception);
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
;
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
.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
;
941 PO_Service_Entries
(Self_ID
, Object
);
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
;
956 Entry_Call_Successful
: out Boolean)
958 Self_Id
: constant Task_Id
:= STPO
.Self
;
959 Entry_Call
: Entry_Call_Link
;
960 Ceiling_Violation
: Boolean;
963 pragma Unreferenced
(Yielded
);
966 if Self_Id
.ATC_Nesting_Level
= ATC_Level
'Last then
967 raise Storage_Error
with "not enough ATC nesting levels";
970 -- If pragma Detect_Blocking is active then Program_Error must be
971 -- raised if this potentially blocking operation is called from a
975 and then Self_Id
.Common
.Protected_Action_Nesting
> 0
977 raise Program_Error
with "potentially blocking operation";
980 if Runtime_Traces
then
981 Send_Trace_Info
(POT_Call
, Entry_Index
(E
), Timeout
);
984 Initialization
.Defer_Abort_Nestable
(Self_Id
);
985 Lock_Entries_With_Status
(Object
, Ceiling_Violation
);
987 if Ceiling_Violation
then
988 Initialization
.Undefer_Abort
(Self_Id
);
992 Self_Id
.ATC_Nesting_Level
:= Self_Id
.ATC_Nesting_Level
+ 1;
994 (Debug
.Trace
(Self_Id
, "TPEC: exited to ATC level: " &
995 ATC_Level
'Image (Self_Id
.ATC_Nesting_Level
), 'A'));
996 Entry_Call
:= Self_Id
.Entry_Calls
(Self_Id
.ATC_Nesting_Level
)'Access;
997 Entry_Call
.Next
:= null;
998 Entry_Call
.Mode
:= Timed_Call
;
999 Entry_Call
.Cancellation_Attempted
:= False;
1002 (if Self_Id
.Deferral_Level
> 1
1003 then Never_Abortable
1004 else Now_Abortable
);
1006 Entry_Call
.E
:= Entry_Index
(E
);
1007 Entry_Call
.Prio
:= STPO
.Get_Priority
(Self_Id
);
1008 Entry_Call
.Uninterpreted_Data
:= Uninterpreted_Data
;
1009 Entry_Call
.Called_PO
:= To_Address
(Object
);
1010 Entry_Call
.Called_Task
:= null;
1011 Entry_Call
.Exception_To_Raise
:= Ada
.Exceptions
.Null_Id
;
1012 Entry_Call
.With_Abort
:= True;
1014 PO_Do_Or_Queue
(Self_Id
, Object
, Entry_Call
);
1015 PO_Service_Entries
(Self_Id
, Object
);
1020 STPO
.Write_Lock
(Self_Id
);
1023 -- Try to avoid waiting for completed or cancelled calls
1025 if Entry_Call
.State
>= Done
then
1026 Utilities
.Exit_One_ATC_Level
(Self_Id
);
1031 STPO
.Unlock
(Self_Id
);
1034 Entry_Call_Successful
:= Entry_Call
.State
= Done
;
1035 Initialization
.Undefer_Abort_Nestable
(Self_Id
);
1036 Entry_Calls
.Check_Exception
(Self_Id
, Entry_Call
);
1040 Entry_Calls
.Wait_For_Completion_With_Timeout
1041 (Entry_Call
, Timeout
, Mode
, Yielded
);
1046 STPO
.Unlock
(Self_Id
);
1049 -- ??? Do we need to yield in case Yielded is False
1051 Initialization
.Undefer_Abort_Nestable
(Self_Id
);
1052 Entry_Call_Successful
:= Entry_Call
.State
= Done
;
1053 Entry_Calls
.Check_Exception
(Self_Id
, Entry_Call
);
1054 end Timed_Protected_Entry_Call
;
1056 ----------------------------
1057 -- Update_For_Queue_To_PO --
1058 ----------------------------
1060 -- Update the state of an existing entry call, based on
1061 -- whether the current queuing action is with or without abort.
1062 -- Call this only while holding the server's lock.
1063 -- It returns with the server's lock released.
1065 New_State
: constant array (Boolean, Entry_Call_State
)
1066 of Entry_Call_State
:=
1068 (Never_Abortable
=> Never_Abortable
,
1069 Not_Yet_Abortable
=> Now_Abortable
,
1070 Was_Abortable
=> Now_Abortable
,
1071 Now_Abortable
=> Now_Abortable
,
1073 Cancelled
=> Cancelled
),
1075 (Never_Abortable
=> Never_Abortable
,
1076 Not_Yet_Abortable
=> Not_Yet_Abortable
,
1077 Was_Abortable
=> Was_Abortable
,
1078 Now_Abortable
=> Now_Abortable
,
1080 Cancelled
=> Cancelled
)
1083 procedure Update_For_Queue_To_PO
1084 (Entry_Call
: Entry_Call_Link
;
1085 With_Abort
: Boolean)
1087 Old
: constant Entry_Call_State
:= Entry_Call
.State
;
1090 pragma Assert
(Old
< Done
);
1092 Entry_Call
.State
:= New_State
(With_Abort
, Entry_Call
.State
);
1094 if Entry_Call
.Mode
= Asynchronous_Call
then
1095 if Old
< Was_Abortable
and then
1096 Entry_Call
.State
= Now_Abortable
1102 STPO
.Write_Lock
(Entry_Call
.Self
);
1104 if Entry_Call
.Self
.Common
.State
= Async_Select_Sleep
then
1105 STPO
.Wakeup
(Entry_Call
.Self
, Async_Select_Sleep
);
1108 STPO
.Unlock
(Entry_Call
.Self
);
1116 elsif Entry_Call
.Mode
= Conditional_Call
then
1117 pragma Assert
(Entry_Call
.State
< Was_Abortable
);
1120 end Update_For_Queue_To_PO
;
1122 end System
.Tasking
.Protected_Objects
.Operations
;