1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
5 -- SYSTEM.TASKING.PROTECTED_OBJECTS.OPERATIONS --
9 -- Copyright (C) 1998-2017, 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
.Restrictions
;
54 with System
.Tasking
.Initialization
;
55 pragma Elaborate_All
(System
.Tasking
.Initialization
);
56 -- Insures that tasking is initialized if any protected objects are created
58 package body System
.Tasking
.Protected_Objects
.Operations
is
60 package STPO
renames System
.Task_Primitives
.Operations
;
66 use System
.Restrictions
;
67 use System
.Restrictions
.Rident
;
69 -----------------------
70 -- Local Subprograms --
71 -----------------------
73 procedure Update_For_Queue_To_PO
74 (Entry_Call
: Entry_Call_Link
;
75 With_Abort
: Boolean);
76 pragma Inline
(Update_For_Queue_To_PO
);
77 -- Update the state of an existing entry call to reflect the fact that it
78 -- is being enqueued, based on whether the current queuing action is with
79 -- or without abort. Call this only while holding the PO's lock. It returns
80 -- with the PO's lock still held.
82 procedure Requeue_Call
84 Object
: Protection_Entries_Access
;
85 Entry_Call
: Entry_Call_Link
);
86 -- Handle requeue of Entry_Call.
87 -- In particular, queue the call if needed, or service it immediately
90 ---------------------------------
91 -- Cancel_Protected_Entry_Call --
92 ---------------------------------
94 -- Compiler interface only (do not call from within the RTS)
96 -- This should have analogous effect to Cancel_Task_Entry_Call, setting
97 -- the value of Block.Cancelled instead of returning the parameter value
100 -- The effect should be idempotent, since the call may already have been
114 -- X : protected_entry_index := 1;
115 -- B80b : communication_block;
116 -- communication_blockIP (B80b);
122 -- procedure _clean is
124 -- if enqueued (B80b) then
125 -- cancel_protected_entry_call (B80b);
131 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
132 -- null_address, asynchronous_call, B80b, objectF => 0);
133 -- if enqueued (B80b) then
141 -- when _abort_signal =>
142 -- abort_undefer.all;
146 -- if not cancelled (B80b) then
151 -- If the entry call completes after we get into the abortable part,
152 -- Abort_Signal should be raised and ATC will take us to the at-end
153 -- handler, which will call _clean.
155 -- If the entry call returns with the call already completed, we can skip
156 -- this, and use the "if enqueued()" to go past the at-end handler, but we
157 -- will still call _clean.
159 -- If the abortable part completes before the entry call is Done, it will
162 -- If the entry call or the abortable part raises an exception,
163 -- we will still call _clean, but the value of Cancelled should not matter.
165 -- Whoever calls _clean first gets to decide whether the call
166 -- has been "cancelled".
168 -- Enqueued should be true if there is any chance that the call is still on
169 -- a queue. It seems to be safe to make it True if the call was Onqueue at
170 -- some point before return from Protected_Entry_Call.
172 -- Cancelled should be true iff the abortable part completed
173 -- and succeeded in cancelling the entry call before it completed.
176 -- The need for Enqueued is less obvious. The "if enqueued ()" tests are
177 -- not necessary, since Cancel_Protected_Entry_Call/Protected_Entry_Call
178 -- must do the same test internally, with locking. The one that makes
179 -- cancellation conditional may be a useful heuristic since at least 1/2
180 -- the time the call should be off-queue by that point. The other one seems
181 -- totally useless, since Protected_Entry_Call must do the same check and
182 -- then possibly wait for the call to be abortable, internally.
184 -- We can check Call.State here without locking the caller's mutex,
185 -- since the call must be over after returning from Wait_For_Completion.
186 -- No other task can access the call record at this point.
188 procedure Cancel_Protected_Entry_Call
189 (Block
: in out Communication_Block
) is
191 Entry_Calls
.Try_To_Cancel_Entry_Call
(Block
.Cancelled
);
192 end Cancel_Protected_Entry_Call
;
198 function Cancelled
(Block
: Communication_Block
) return Boolean is
200 return Block
.Cancelled
;
203 -------------------------
204 -- Complete_Entry_Body --
205 -------------------------
207 procedure Complete_Entry_Body
(Object
: Protection_Entries_Access
) is
209 Exceptional_Complete_Entry_Body
(Object
, Ada
.Exceptions
.Null_Id
);
210 end Complete_Entry_Body
;
216 function Enqueued
(Block
: Communication_Block
) return Boolean is
218 return Block
.Enqueued
;
221 -------------------------------------
222 -- Exceptional_Complete_Entry_Body --
223 -------------------------------------
225 procedure Exceptional_Complete_Entry_Body
226 (Object
: Protection_Entries_Access
;
227 Ex
: Ada
.Exceptions
.Exception_Id
)
229 procedure Transfer_Occurrence
230 (Target
: Ada
.Exceptions
.Exception_Occurrence_Access
;
231 Source
: Ada
.Exceptions
.Exception_Occurrence
);
232 pragma Import
(C
, Transfer_Occurrence
, "__gnat_transfer_occurrence");
234 Entry_Call
: constant Entry_Call_Link
:= Object
.Call_In_Progress
;
239 (Debug
.Trace
(STPO
.Self
, "Exceptional_Complete_Entry_Body", 'P'));
241 -- We must have abort deferred, since we are inside a protected
244 if Entry_Call
/= null then
246 -- The call was not requeued
248 Entry_Call
.Exception_To_Raise
:= Ex
;
250 if Ex
/= Ada
.Exceptions
.Null_Id
then
252 -- An exception was raised and abort was deferred, so adjust
253 -- before propagating, otherwise the task will stay with deferral
254 -- enabled for its remaining life.
256 Self_Id
:= STPO
.Self
;
258 if not ZCX_By_Default
then
259 Initialization
.Undefer_Abort_Nestable
(Self_Id
);
263 (Entry_Call
.Self
.Common
.Compiler_Data
.Current_Excep
'Access,
264 Self_Id
.Common
.Compiler_Data
.Current_Excep
);
267 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
268 -- PO_Service_Entries on return.
271 end Exceptional_Complete_Entry_Body
;
277 procedure PO_Do_Or_Queue
279 Object
: Protection_Entries_Access
;
280 Entry_Call
: Entry_Call_Link
)
282 E
: constant Protected_Entry_Index
:=
283 Protected_Entry_Index
(Entry_Call
.E
);
284 Index
: constant Protected_Entry_Index
:=
285 Object
.Find_Body_Index
(Object
.Compiler_Info
, E
);
286 Barrier_Value
: Boolean;
287 Queue_Length
: Natural;
289 -- When the Action procedure for an entry body returns, it is either
290 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
291 -- is queued, having executed a requeue statement.
294 Object
.Entry_Bodies
(Index
).Barrier
(Object
.Compiler_Info
, E
);
296 if Barrier_Value
then
298 -- Not abortable while service is in progress
300 if Entry_Call
.State
= Now_Abortable
then
301 Entry_Call
.State
:= Was_Abortable
;
304 Object
.Call_In_Progress
:= Entry_Call
;
307 (Debug
.Trace
(Self_ID
, "PODOQ: start entry body", 'P'));
308 Object
.Entry_Bodies
(Index
).Action
(
309 Object
.Compiler_Info
, Entry_Call
.Uninterpreted_Data
, E
);
311 if Object
.Call_In_Progress
/= null then
313 -- Body of current entry served call to completion
315 Object
.Call_In_Progress
:= null;
321 STPO
.Write_Lock
(Entry_Call
.Self
);
322 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
323 STPO
.Unlock
(Entry_Call
.Self
);
330 Requeue_Call
(Self_ID
, Object
, Entry_Call
);
333 elsif Entry_Call
.Mode
/= Conditional_Call
334 or else not Entry_Call
.With_Abort
336 if Run_Time_Restrictions
.Set
(Max_Entry_Queue_Length
)
337 or else Object
.Entry_Queue_Maxes
/= null
339 -- Need to check the queue length. Computing the length is an
340 -- unusual case and is slow (need to walk the queue).
342 Queue_Length
:= Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
));
344 if (Run_Time_Restrictions
.Set
(Max_Entry_Queue_Length
)
345 and then Queue_Length
>=
346 Run_Time_Restrictions
.Value
(Max_Entry_Queue_Length
))
348 (Object
.Entry_Queue_Maxes
/= null
349 and then Object
.Entry_Queue_Maxes
(Index
) /= 0
350 and then Queue_Length
>= Object
.Entry_Queue_Maxes
(Index
))
352 -- This violates the Max_Entry_Queue_Length restriction or the
353 -- Max_Queue_Length bound, raise Program_Error.
355 Entry_Call
.Exception_To_Raise
:= Program_Error
'Identity;
361 STPO
.Write_Lock
(Entry_Call
.Self
);
362 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
363 STPO
.Unlock
(Entry_Call
.Self
);
373 -- Do the work: queue the call
375 Queuing
.Enqueue
(Object
.Entry_Queues
(E
), Entry_Call
);
376 Update_For_Queue_To_PO
(Entry_Call
, Entry_Call
.With_Abort
);
380 -- Conditional_Call and With_Abort
386 STPO
.Write_Lock
(Entry_Call
.Self
);
387 pragma Assert
(Entry_Call
.State
/= Not_Yet_Abortable
);
388 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Cancelled
);
389 STPO
.Unlock
(Entry_Call
.Self
);
398 Queuing
.Broadcast_Program_Error
(Self_ID
, Object
, Entry_Call
);
401 ------------------------
402 -- PO_Service_Entries --
403 ------------------------
405 procedure PO_Service_Entries
407 Object
: Entries
.Protection_Entries_Access
;
408 Unlock_Object
: Boolean := True)
410 E
: Protected_Entry_Index
;
412 Entry_Call
: Entry_Call_Link
;
416 Queuing
.Select_Protected_Entry_Call
(Self_ID
, Object
, Entry_Call
);
418 exit when Entry_Call
= null;
420 E
:= Protected_Entry_Index
(Entry_Call
.E
);
422 -- Not abortable while service is in progress
424 if Entry_Call
.State
= Now_Abortable
then
425 Entry_Call
.State
:= Was_Abortable
;
428 Object
.Call_In_Progress
:= Entry_Call
;
432 (Debug
.Trace
(Self_ID
, "POSE: start entry body", 'P'));
435 (Object
.Find_Body_Index
(Object
.Compiler_Info
, E
)).Action
436 (Object
.Compiler_Info
, Entry_Call
.Uninterpreted_Data
, E
);
440 Queuing
.Broadcast_Program_Error
441 (Self_ID
, Object
, Entry_Call
);
444 if Object
.Call_In_Progress
= null then
445 Requeue_Call
(Self_ID
, Object
, Entry_Call
);
446 exit when Entry_Call
.State
= Cancelled
;
449 Object
.Call_In_Progress
:= null;
450 Caller
:= Entry_Call
.Self
;
456 STPO
.Write_Lock
(Caller
);
457 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
458 STPO
.Unlock
(Caller
);
466 if Unlock_Object
then
467 Unlock_Entries
(Object
);
469 end PO_Service_Entries
;
471 ---------------------
472 -- Protected_Count --
473 ---------------------
475 function Protected_Count
476 (Object
: Protection_Entries
'Class;
477 E
: Protected_Entry_Index
) return Natural
480 return Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
));
483 --------------------------
484 -- Protected_Entry_Call --
485 --------------------------
487 -- Compiler interface only (do not call from within the RTS)
496 -- X : protected_entry_index := 1;
497 -- B85b : communication_block;
498 -- communication_blockIP (B85b);
501 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
502 -- null_address, conditional_call, B85b, objectF => 0);
504 -- if cancelled (B85b) then
511 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
514 -- The initial part of this procedure does not need to lock the calling
515 -- task's ATCB, up to the point where the call record first may be queued
516 -- (PO_Do_Or_Queue), since before that no other task will have access to
519 -- If this is a call made inside of an abort deferred region, the call
520 -- should be never abortable.
522 -- If the call was not queued abortably, we need to wait until it is before
523 -- proceeding with the abortable part.
525 -- There are some heuristics here, just to save time for frequently
526 -- occurring cases. For example, we check Initially_Abortable to try to
527 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
528 -- for async. entry calls is to be queued abortably.
530 -- Another heuristic uses the Block.Enqueued to try to avoid calling
531 -- Cancel_Protected_Entry_Call if the call can be served immediately.
533 procedure Protected_Entry_Call
534 (Object
: Protection_Entries_Access
;
535 E
: Protected_Entry_Index
;
536 Uninterpreted_Data
: System
.Address
;
538 Block
: out Communication_Block
)
540 Self_ID
: constant Task_Id
:= STPO
.Self
;
541 Entry_Call
: Entry_Call_Link
;
542 Initially_Abortable
: Boolean;
543 Ceiling_Violation
: Boolean;
547 (Debug
.Trace
(Self_ID
, "Protected_Entry_Call", 'P'));
549 if Self_ID
.ATC_Nesting_Level
= ATC_Level
'Last then
550 raise Storage_Error
with "not enough ATC nesting levels";
553 -- If pragma Detect_Blocking is active then Program_Error must be
554 -- raised if this potentially blocking operation is called from a
558 and then Self_ID
.Common
.Protected_Action_Nesting
> 0
560 raise Program_Error
with "potentially blocking operation";
563 -- Self_ID.Deferral_Level should be 0, except when called from Finalize,
564 -- where abort is already deferred.
566 Initialization
.Defer_Abort_Nestable
(Self_ID
);
567 Lock_Entries_With_Status
(Object
, Ceiling_Violation
);
569 if Ceiling_Violation
then
571 -- Failed ceiling check
573 Initialization
.Undefer_Abort_Nestable
(Self_ID
);
577 Block
.Self
:= Self_ID
;
578 Self_ID
.ATC_Nesting_Level
:= Self_ID
.ATC_Nesting_Level
+ 1;
580 (Debug
.Trace
(Self_ID
, "PEC: entered ATC level: " &
581 ATC_Level
'Image (Self_ID
.ATC_Nesting_Level
), 'A'));
583 Self_ID
.Entry_Calls
(Self_ID
.ATC_Nesting_Level
)'Access;
584 Entry_Call
.Next
:= null;
585 Entry_Call
.Mode
:= Mode
;
586 Entry_Call
.Cancellation_Attempted
:= False;
589 (if Self_ID
.Deferral_Level
> 1
590 then Never_Abortable
else Now_Abortable
);
592 Entry_Call
.E
:= Entry_Index
(E
);
593 Entry_Call
.Prio
:= STPO
.Get_Priority
(Self_ID
);
594 Entry_Call
.Uninterpreted_Data
:= Uninterpreted_Data
;
595 Entry_Call
.Called_PO
:= To_Address
(Object
);
596 Entry_Call
.Called_Task
:= null;
597 Entry_Call
.Exception_To_Raise
:= Ada
.Exceptions
.Null_Id
;
598 Entry_Call
.With_Abort
:= True;
600 PO_Do_Or_Queue
(Self_ID
, Object
, Entry_Call
);
601 Initially_Abortable
:= Entry_Call
.State
= Now_Abortable
;
602 PO_Service_Entries
(Self_ID
, Object
);
604 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
605 -- for completed or cancelled calls. (This is a heuristic, only.)
607 if Entry_Call
.State
>= Done
then
609 -- Once State >= Done it will not change any more
615 STPO
.Write_Lock
(Self_ID
);
616 Utilities
.Exit_One_ATC_Level
(Self_ID
);
617 STPO
.Unlock
(Self_ID
);
623 Block
.Enqueued
:= False;
624 Block
.Cancelled
:= Entry_Call
.State
= Cancelled
;
625 Initialization
.Undefer_Abort_Nestable
(Self_ID
);
626 Entry_Calls
.Check_Exception
(Self_ID
, Entry_Call
);
630 -- In this case we cannot conclude anything, since State can change
636 -- Now for the general case
638 if Mode
= Asynchronous_Call
then
640 -- Try to avoid an expensive call
642 if not Initially_Abortable
then
645 Entry_Calls
.Wait_Until_Abortable
(Self_ID
, Entry_Call
);
648 Entry_Calls
.Wait_Until_Abortable
(Self_ID
, Entry_Call
);
654 when Conditional_Call
659 Entry_Calls
.Wait_For_Completion
(Entry_Call
);
663 STPO
.Write_Lock
(Self_ID
);
664 Entry_Calls
.Wait_For_Completion
(Entry_Call
);
665 STPO
.Unlock
(Self_ID
);
668 Block
.Cancelled
:= Entry_Call
.State
= Cancelled
;
670 when Asynchronous_Call
673 pragma Assert
(False);
678 Initialization
.Undefer_Abort_Nestable
(Self_ID
);
679 Entry_Calls
.Check_Exception
(Self_ID
, Entry_Call
);
680 end Protected_Entry_Call
;
686 procedure Requeue_Call
688 Object
: Protection_Entries_Access
;
689 Entry_Call
: Entry_Call_Link
)
691 New_Object
: Protection_Entries_Access
;
692 Ceiling_Violation
: Boolean;
694 E
: Protected_Entry_Index
;
697 New_Object
:= To_Protection
(Entry_Call
.Called_PO
);
699 if New_Object
= null then
701 -- Call is to be requeued to a task entry
707 Result
:= Rendezvous
.Task_Do_Or_Queue
(Self_Id
, Entry_Call
);
710 Queuing
.Broadcast_Program_Error
711 (Self_Id
, Object
, Entry_Call
, RTS_Locked
=> True);
719 -- Call should be requeued to a PO
721 if Object
/= New_Object
then
723 -- Requeue is to different PO
725 Lock_Entries_With_Status
(New_Object
, Ceiling_Violation
);
727 if Ceiling_Violation
then
728 Object
.Call_In_Progress
:= null;
729 Queuing
.Broadcast_Program_Error
(Self_Id
, Object
, Entry_Call
);
732 PO_Do_Or_Queue
(Self_Id
, New_Object
, Entry_Call
);
733 PO_Service_Entries
(Self_Id
, New_Object
);
737 -- Requeue is to same protected object
739 -- ??? Try to compensate apparent failure of the scheduler on some
740 -- OS (e.g VxWorks) to give higher priority tasks a chance to run
743 STPO
.Yield
(Do_Yield
=> False);
745 if Entry_Call
.With_Abort
746 and then Entry_Call
.Cancellation_Attempted
748 -- If this is a requeue with abort and someone tried to cancel
749 -- this call, cancel it at this point.
751 Entry_Call
.State
:= Cancelled
;
755 if not Entry_Call
.With_Abort
756 or else Entry_Call
.Mode
/= Conditional_Call
758 E
:= Protected_Entry_Index
(Entry_Call
.E
);
760 if Run_Time_Restrictions
.Set
(Max_Entry_Queue_Length
)
762 Run_Time_Restrictions
.Value
(Max_Entry_Queue_Length
) <=
763 Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
))
765 -- This violates the Max_Entry_Queue_Length restriction,
766 -- raise Program_Error.
768 Entry_Call
.Exception_To_Raise
:= Program_Error
'Identity;
774 STPO
.Write_Lock
(Entry_Call
.Self
);
775 Initialization
.Wakeup_Entry_Caller
776 (Self_Id
, Entry_Call
, Done
);
777 STPO
.Unlock
(Entry_Call
.Self
);
785 (New_Object
.Entry_Queues
(E
), Entry_Call
);
786 Update_For_Queue_To_PO
(Entry_Call
, Entry_Call
.With_Abort
);
790 PO_Do_Or_Queue
(Self_Id
, New_Object
, Entry_Call
);
796 ----------------------------
797 -- Protected_Entry_Caller --
798 ----------------------------
800 function Protected_Entry_Caller
801 (Object
: Protection_Entries
'Class) return Task_Id
is
803 return Object
.Call_In_Progress
.Self
;
804 end Protected_Entry_Caller
;
806 -----------------------------
807 -- Requeue_Protected_Entry --
808 -----------------------------
810 -- Compiler interface only (do not call from within the RTS)
819 -- procedure rPT__E10b (O : address; P : address; E :
820 -- protected_entry_index) is
821 -- type rTVP is access rTV;
823 -- _object : rTVP := rTVP!(O);
826 -- rR : protection renames _object._object;
827 -- vP : integer renames _object.v;
828 -- bP : boolean renames _object.b;
832 -- requeue_protected_entry (rR'unchecked_access, rR'
833 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
837 -- complete_entry_body (_object._object'unchecked_access, objectF =>
842 -- abort_undefer.all;
843 -- exceptional_complete_entry_body (_object._object'
844 -- unchecked_access, current_exception, objectF => 0);
848 procedure Requeue_Protected_Entry
849 (Object
: Protection_Entries_Access
;
850 New_Object
: Protection_Entries_Access
;
851 E
: Protected_Entry_Index
;
852 With_Abort
: Boolean)
854 Entry_Call
: constant Entry_Call_Link
:= Object
.Call_In_Progress
;
858 (Debug
.Trace
(STPO
.Self
, "Requeue_Protected_Entry", 'P'));
859 pragma Assert
(STPO
.Self
.Deferral_Level
> 0);
861 Entry_Call
.E
:= Entry_Index
(E
);
862 Entry_Call
.Called_PO
:= To_Address
(New_Object
);
863 Entry_Call
.Called_Task
:= null;
864 Entry_Call
.With_Abort
:= With_Abort
;
865 Object
.Call_In_Progress
:= null;
866 end Requeue_Protected_Entry
;
868 -------------------------------------
869 -- Requeue_Task_To_Protected_Entry --
870 -------------------------------------
872 -- Compiler interface only (do not call from within the RTS)
883 -- accept_call (1, A79b);
885 -- requeue_task_to_protected_entry (rTV!(r)._object'
886 -- unchecked_access, 2, false, new_objectF => 0);
889 -- complete_rendezvous;
892 -- when all others =>
893 -- exceptional_complete_rendezvous (get_gnat_exception);
896 procedure Requeue_Task_To_Protected_Entry
897 (New_Object
: Protection_Entries_Access
;
898 E
: Protected_Entry_Index
;
899 With_Abort
: Boolean)
901 Self_ID
: constant Task_Id
:= STPO
.Self
;
902 Entry_Call
: constant Entry_Call_Link
:= Self_ID
.Common
.Call
;
905 Initialization
.Defer_Abort
(Self_ID
);
907 -- We do not need to lock Self_ID here since the call is not abortable
908 -- at this point, and therefore, the caller cannot cancel the call.
910 Entry_Call
.Needs_Requeue
:= True;
911 Entry_Call
.With_Abort
:= With_Abort
;
912 Entry_Call
.Called_PO
:= To_Address
(New_Object
);
913 Entry_Call
.Called_Task
:= null;
914 Entry_Call
.E
:= Entry_Index
(E
);
915 Initialization
.Undefer_Abort
(Self_ID
);
916 end Requeue_Task_To_Protected_Entry
;
918 ---------------------
919 -- Service_Entries --
920 ---------------------
922 procedure Service_Entries
(Object
: Protection_Entries_Access
) is
923 Self_ID
: constant Task_Id
:= STPO
.Self
;
925 PO_Service_Entries
(Self_ID
, Object
);
928 --------------------------------
929 -- Timed_Protected_Entry_Call --
930 --------------------------------
932 -- Compiler interface only (do not call from within the RTS)
934 procedure Timed_Protected_Entry_Call
935 (Object
: Protection_Entries_Access
;
936 E
: Protected_Entry_Index
;
937 Uninterpreted_Data
: System
.Address
;
940 Entry_Call_Successful
: out Boolean)
942 Self_Id
: constant Task_Id
:= STPO
.Self
;
943 Entry_Call
: Entry_Call_Link
;
944 Ceiling_Violation
: Boolean;
947 pragma Unreferenced
(Yielded
);
950 if Self_Id
.ATC_Nesting_Level
= ATC_Level
'Last then
951 raise Storage_Error
with "not enough ATC nesting levels";
954 -- If pragma Detect_Blocking is active then Program_Error must be
955 -- raised if this potentially blocking operation is called from a
959 and then Self_Id
.Common
.Protected_Action_Nesting
> 0
961 raise Program_Error
with "potentially blocking operation";
964 Initialization
.Defer_Abort_Nestable
(Self_Id
);
965 Lock_Entries_With_Status
(Object
, Ceiling_Violation
);
967 if Ceiling_Violation
then
968 Initialization
.Undefer_Abort
(Self_Id
);
972 Self_Id
.ATC_Nesting_Level
:= Self_Id
.ATC_Nesting_Level
+ 1;
974 (Debug
.Trace
(Self_Id
, "TPEC: exited to ATC level: " &
975 ATC_Level
'Image (Self_Id
.ATC_Nesting_Level
), 'A'));
976 Entry_Call
:= Self_Id
.Entry_Calls
(Self_Id
.ATC_Nesting_Level
)'Access;
977 Entry_Call
.Next
:= null;
978 Entry_Call
.Mode
:= Timed_Call
;
979 Entry_Call
.Cancellation_Attempted
:= False;
982 (if Self_Id
.Deferral_Level
> 1
986 Entry_Call
.E
:= Entry_Index
(E
);
987 Entry_Call
.Prio
:= STPO
.Get_Priority
(Self_Id
);
988 Entry_Call
.Uninterpreted_Data
:= Uninterpreted_Data
;
989 Entry_Call
.Called_PO
:= To_Address
(Object
);
990 Entry_Call
.Called_Task
:= null;
991 Entry_Call
.Exception_To_Raise
:= Ada
.Exceptions
.Null_Id
;
992 Entry_Call
.With_Abort
:= True;
994 PO_Do_Or_Queue
(Self_Id
, Object
, Entry_Call
);
995 PO_Service_Entries
(Self_Id
, Object
);
1000 STPO
.Write_Lock
(Self_Id
);
1003 -- Try to avoid waiting for completed or cancelled calls
1005 if Entry_Call
.State
>= Done
then
1006 Utilities
.Exit_One_ATC_Level
(Self_Id
);
1011 STPO
.Unlock
(Self_Id
);
1014 Entry_Call_Successful
:= Entry_Call
.State
= Done
;
1015 Initialization
.Undefer_Abort_Nestable
(Self_Id
);
1016 Entry_Calls
.Check_Exception
(Self_Id
, Entry_Call
);
1020 Entry_Calls
.Wait_For_Completion_With_Timeout
1021 (Entry_Call
, Timeout
, Mode
, Yielded
);
1026 STPO
.Unlock
(Self_Id
);
1029 -- ??? Do we need to yield in case Yielded is False
1031 Initialization
.Undefer_Abort_Nestable
(Self_Id
);
1032 Entry_Call_Successful
:= Entry_Call
.State
= Done
;
1033 Entry_Calls
.Check_Exception
(Self_Id
, Entry_Call
);
1034 end Timed_Protected_Entry_Call
;
1036 ----------------------------
1037 -- Update_For_Queue_To_PO --
1038 ----------------------------
1040 -- Update the state of an existing entry call, based on
1041 -- whether the current queuing action is with or without abort.
1042 -- Call this only while holding the server's lock.
1043 -- It returns with the server's lock released.
1045 New_State
: constant array (Boolean, Entry_Call_State
)
1046 of Entry_Call_State
:=
1048 (Never_Abortable
=> Never_Abortable
,
1049 Not_Yet_Abortable
=> Now_Abortable
,
1050 Was_Abortable
=> Now_Abortable
,
1051 Now_Abortable
=> Now_Abortable
,
1053 Cancelled
=> Cancelled
),
1055 (Never_Abortable
=> Never_Abortable
,
1056 Not_Yet_Abortable
=> Not_Yet_Abortable
,
1057 Was_Abortable
=> Was_Abortable
,
1058 Now_Abortable
=> Now_Abortable
,
1060 Cancelled
=> Cancelled
)
1063 procedure Update_For_Queue_To_PO
1064 (Entry_Call
: Entry_Call_Link
;
1065 With_Abort
: Boolean)
1067 Old
: constant Entry_Call_State
:= Entry_Call
.State
;
1070 pragma Assert
(Old
< Done
);
1072 Entry_Call
.State
:= New_State
(With_Abort
, Entry_Call
.State
);
1074 if Entry_Call
.Mode
= Asynchronous_Call
then
1075 if Old
< Was_Abortable
and then
1076 Entry_Call
.State
= Now_Abortable
1082 STPO
.Write_Lock
(Entry_Call
.Self
);
1084 if Entry_Call
.Self
.Common
.State
= Async_Select_Sleep
then
1085 STPO
.Wakeup
(Entry_Call
.Self
, Async_Select_Sleep
);
1088 STPO
.Unlock
(Entry_Call
.Self
);
1096 elsif Entry_Call
.Mode
= Conditional_Call
then
1097 pragma Assert
(Entry_Call
.State
< Was_Abortable
);
1100 end Update_For_Queue_To_PO
;
1102 end System
.Tasking
.Protected_Objects
.Operations
;