1 ------------------------------------------------------------------------------
3 -- GNU ADA 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-2001, 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, 59 Temple Place - Suite 330, Boston, --
21 -- MA 02111-1307, 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. It is --
31 -- now maintained by Ada Core Technologies, Inc. (http://www.gnat.com). --
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.
49 -- Used for Exception_ID
53 with System
.Task_Primitives
.Operations
;
54 -- used for Initialize_Lock
60 with System
.Tasking
.Entry_Calls
;
61 -- used for Wait_For_Completion
62 -- Wait_Until_Abortable
63 -- Wait_For_Completion_With_Timeout
65 with System
.Tasking
.Initialization
;
66 -- Used for Defer_Abort,
68 -- Change_Base_Priority
70 pragma Elaborate_All
(System
.Tasking
.Initialization
);
71 -- This insures that tasking is initialized if any protected objects are
74 with System
.Tasking
.Queuing
;
76 -- Broadcast_Program_Error
77 -- Select_Protected_Entry_Call
81 with System
.Tasking
.Rendezvous
;
82 -- used for Task_Do_Or_Queue
84 with System
.Tasking
.Debug
;
87 with System
.Parameters
;
88 -- used for Single_Lock
91 with System
.Traces
.Tasking
;
92 -- used for Send_Trace_Info
94 package body System
.Tasking
.Protected_Objects
.Operations
is
96 package STPO
renames System
.Task_Primitives
.Operations
;
104 use System
.Traces
.Tasking
;
106 -----------------------
107 -- Local Subprograms --
108 -----------------------
110 procedure Update_For_Queue_To_PO
111 (Entry_Call
: Entry_Call_Link
;
112 With_Abort
: Boolean);
113 pragma Inline
(Update_For_Queue_To_PO
);
114 -- Update the state of an existing entry call to reflect
115 -- the fact that it is being enqueued, based on
116 -- whether the current queuing action is with or without abort.
117 -- Call this only while holding the PO's lock.
118 -- It returns with the PO's lock still held.
120 ---------------------------------
121 -- Cancel_Protected_Entry_Call --
122 ---------------------------------
124 -- Compiler interface only. Do not call from within the RTS.
125 -- This should have analogous effect to Cancel_Task_Entry_Call,
126 -- setting the value of Block.Cancelled instead of returning
127 -- the parameter value Cancelled.
129 -- The effect should be idempotent, since the call may already
130 -- have been dequeued.
143 -- X : protected_entry_index := 1;
144 -- B80b : communication_block;
145 -- _init_proc (B80b);
150 -- procedure _clean is
152 -- if enqueued (B80b) then
153 -- cancel_protected_entry_call (B80b);
158 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
159 -- null_address, asynchronous_call, B80b, objectF => 0);
160 -- if enqueued (B80b) then
167 -- when _abort_signal =>
168 -- abort_undefer.all;
171 -- if not cancelled (B80b) then
176 -- If the entry call completes after we get into the abortable part,
177 -- Abort_Signal should be raised and ATC will take us to the at-end
178 -- handler, which will call _clean.
180 -- If the entry call returns with the call already completed,
181 -- we can skip this, and use the "if enqueued()" to go past
182 -- the at-end handler, but we will still call _clean.
184 -- If the abortable part completes before the entry call is Done,
185 -- it will call _clean.
187 -- If the entry call or the abortable part raises an exception,
188 -- we will still call _clean, but the value of Cancelled should not matter.
190 -- Whoever calls _clean first gets to decide whether the call
191 -- has been "cancelled".
193 -- Enqueued should be true if there is any chance that the call
194 -- is still on a queue. It seems to be safe to make it True if
195 -- the call was Onqueue at some point before return from
196 -- Protected_Entry_Call.
198 -- Cancelled should be true iff the abortable part completed
199 -- and succeeded in cancelling the entry call before it completed.
202 -- The need for Enqueued is less obvious.
203 -- The "if enqueued ()" tests are not necessary, since both
204 -- Cancel_Protected_Entry_Call and Protected_Entry_Call must
205 -- do the same test internally, with locking. The one that
206 -- makes cancellation conditional may be a useful heuristic
207 -- since at least 1/2 the time the call should be off-queue
208 -- by that point. The other one seems totally useless, since
209 -- Protected_Entry_Call must do the same check and then
210 -- possibly wait for the call to be abortable, internally.
212 -- We can check Call.State here without locking the caller's mutex,
213 -- since the call must be over after returning from Wait_For_Completion.
214 -- No other task can access the call record at this point.
216 procedure Cancel_Protected_Entry_Call
217 (Block
: in out Communication_Block
) is
219 Entry_Calls
.Try_To_Cancel_Entry_Call
(Block
.Cancelled
);
220 end Cancel_Protected_Entry_Call
;
226 function Cancelled
(Block
: Communication_Block
) return Boolean is
228 return Block
.Cancelled
;
231 -------------------------
232 -- Complete_Entry_Body --
233 -------------------------
235 procedure Complete_Entry_Body
(Object
: Protection_Entries_Access
) is
237 Exceptional_Complete_Entry_Body
(Object
, Ada
.Exceptions
.Null_Id
);
238 end Complete_Entry_Body
;
244 function Enqueued
(Block
: Communication_Block
) return Boolean is
246 return Block
.Enqueued
;
249 -------------------------------------
250 -- Exceptional_Complete_Entry_Body --
251 -------------------------------------
253 procedure Exceptional_Complete_Entry_Body
254 (Object
: Protection_Entries_Access
;
255 Ex
: Ada
.Exceptions
.Exception_Id
)
257 Entry_Call
: constant Entry_Call_Link
:= Object
.Call_In_Progress
;
260 (Debug
.Trace
(STPO
.Self
, "Exceptional_Complete_Entry_Body", 'P'));
262 -- We must have abort deferred, since we are inside
263 -- a protected operation.
265 if Entry_Call
/= null then
266 -- The call was not requeued.
268 Entry_Call
.Exception_To_Raise
:= Ex
;
270 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
271 -- PO_Service_Entries on return.
274 if Runtime_Traces
then
275 Send_Trace_Info
(PO_Done
, Entry_Call
.Self
);
277 end Exceptional_Complete_Entry_Body
;
283 procedure PO_Do_Or_Queue
285 Object
: Protection_Entries_Access
;
286 Entry_Call
: Entry_Call_Link
;
287 With_Abort
: Boolean)
289 E
: Protected_Entry_Index
:= Protected_Entry_Index
(Entry_Call
.E
);
290 New_Object
: Protection_Entries_Access
;
291 Ceiling_Violation
: Boolean;
292 Barrier_Value
: Boolean;
296 -- When the Action procedure for an entry body returns, it is either
297 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
298 -- is queued, having executed a requeue statement.
301 Object
.Entry_Bodies
(
302 Object
.Find_Body_Index
(Object
.Compiler_Info
, E
)).
303 Barrier
(Object
.Compiler_Info
, E
);
305 if Barrier_Value
then
307 -- Not abortable while service is in progress.
309 if Entry_Call
.State
= Now_Abortable
then
310 Entry_Call
.State
:= Was_Abortable
;
313 Object
.Call_In_Progress
:= Entry_Call
;
316 (Debug
.Trace
(Self_ID
, "PODOQ: start entry body", 'P'));
317 Object
.Entry_Bodies
(
318 Object
.Find_Body_Index
(Object
.Compiler_Info
, E
)).Action
(
319 Object
.Compiler_Info
, Entry_Call
.Uninterpreted_Data
, E
);
321 if Object
.Call_In_Progress
/= null then
323 -- Body of current entry served call to completion
325 Object
.Call_In_Progress
:= null;
331 STPO
.Write_Lock
(Entry_Call
.Self
);
332 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
333 STPO
.Unlock
(Entry_Call
.Self
);
340 -- Body of current entry requeued the call
341 New_Object
:= To_Protection
(Entry_Call
.Called_PO
);
343 if New_Object
= null then
345 -- Call was requeued to a task
351 Result
:= Rendezvous
.Task_Do_Or_Queue
352 (Self_ID
, Entry_Call
,
353 With_Abort
=> Entry_Call
.Requeue_With_Abort
);
356 Queuing
.Broadcast_Program_Error
357 (Self_ID
, Object
, Entry_Call
, RTS_Locked
=> True);
367 if Object
/= New_Object
then
368 -- Requeue is on a different object
370 Lock_Entries
(New_Object
, Ceiling_Violation
);
372 if Ceiling_Violation
then
373 Object
.Call_In_Progress
:= null;
374 Queuing
.Broadcast_Program_Error
375 (Self_ID
, Object
, Entry_Call
);
378 PO_Do_Or_Queue
(Self_ID
, New_Object
, Entry_Call
, With_Abort
);
379 PO_Service_Entries
(Self_ID
, New_Object
);
380 Unlock_Entries
(New_Object
);
384 -- Requeue is on same protected object
386 if Entry_Call
.Requeue_With_Abort
387 and then Entry_Call
.Cancellation_Attempted
389 -- If this is a requeue with abort and someone tried
390 -- to cancel this call, cancel it at this point.
392 Entry_Call
.State
:= Cancelled
;
396 if not With_Abort
or else
397 Entry_Call
.Mode
/= Conditional_Call
399 E
:= Protected_Entry_Index
(Entry_Call
.E
);
401 (New_Object
.Entry_Queues
(E
), Entry_Call
);
402 Update_For_Queue_To_PO
(Entry_Call
, With_Abort
);
406 -- Can we convert this recursion to a loop?
408 PO_Do_Or_Queue
(Self_ID
, New_Object
, Entry_Call
, With_Abort
);
413 elsif Entry_Call
.Mode
/= Conditional_Call
or else
415 Queuing
.Enqueue
(Object
.Entry_Queues
(E
), Entry_Call
);
416 Update_For_Queue_To_PO
(Entry_Call
, With_Abort
);
419 -- Conditional_Call and With_Abort
425 STPO
.Write_Lock
(Entry_Call
.Self
);
426 pragma Assert
(Entry_Call
.State
>= Was_Abortable
);
427 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Cancelled
);
428 STPO
.Unlock
(Entry_Call
.Self
);
437 Queuing
.Broadcast_Program_Error
(Self_ID
, Object
, Entry_Call
);
440 ------------------------
441 -- PO_Service_Entries --
442 ------------------------
444 procedure PO_Service_Entries
446 Object
: Protection_Entries_Access
)
448 Entry_Call
: Entry_Call_Link
;
449 E
: Protected_Entry_Index
;
451 New_Object
: Protection_Entries_Access
;
452 Ceiling_Violation
: Boolean;
457 Queuing
.Select_Protected_Entry_Call
(Self_ID
, Object
, Entry_Call
);
459 if Entry_Call
/= null then
460 E
:= Protected_Entry_Index
(Entry_Call
.E
);
462 -- Not abortable while service is in progress.
464 if Entry_Call
.State
= Now_Abortable
then
465 Entry_Call
.State
:= Was_Abortable
;
468 Object
.Call_In_Progress
:= Entry_Call
;
471 if Runtime_Traces
then
472 Send_Trace_Info
(PO_Run
, Self_ID
,
473 Entry_Call
.Self
, Entry_Index
(E
));
477 (Debug
.Trace
(Self_ID
, "POSE: start entry body", 'P'));
478 Object
.Entry_Bodies
(
479 Object
.Find_Body_Index
(Object
.Compiler_Info
, E
)).Action
(
480 Object
.Compiler_Info
, Entry_Call
.Uninterpreted_Data
, E
);
483 Queuing
.Broadcast_Program_Error
484 (Self_ID
, Object
, Entry_Call
);
487 if Object
.Call_In_Progress
/= null then
488 Object
.Call_In_Progress
:= null;
489 Caller
:= Entry_Call
.Self
;
495 STPO
.Write_Lock
(Caller
);
496 Initialization
.Wakeup_Entry_Caller
(Self_ID
, Entry_Call
, Done
);
497 STPO
.Unlock
(Caller
);
504 -- Call needs to be requeued
506 New_Object
:= To_Protection
(Entry_Call
.Called_PO
);
508 if New_Object
= null then
510 -- Call is to be requeued to a task entry
516 Result
:= Rendezvous
.Task_Do_Or_Queue
517 (Self_ID
, Entry_Call
,
518 With_Abort
=> Entry_Call
.Requeue_With_Abort
);
521 Queuing
.Broadcast_Program_Error
522 (Self_ID
, Object
, Entry_Call
, RTS_Locked
=> True);
530 -- Call should be requeued to a PO
532 if Object
/= New_Object
then
533 -- Requeue is to different PO
535 Lock_Entries
(New_Object
, Ceiling_Violation
);
537 if Ceiling_Violation
then
538 Object
.Call_In_Progress
:= null;
539 Queuing
.Broadcast_Program_Error
540 (Self_ID
, Object
, Entry_Call
);
543 PO_Do_Or_Queue
(Self_ID
, New_Object
, Entry_Call
,
544 Entry_Call
.Requeue_With_Abort
);
545 PO_Service_Entries
(Self_ID
, New_Object
);
546 Unlock_Entries
(New_Object
);
550 -- Requeue is to same protected object
552 -- ??? Try to compensate apparent failure of the
553 -- scheduler on some OS (e.g VxWorks) to give higher
554 -- priority tasks a chance to run (see CXD6002).
558 if Entry_Call
.Requeue_With_Abort
559 and then Entry_Call
.Cancellation_Attempted
561 -- If this is a requeue with abort and someone tried
562 -- to cancel this call, cancel it at this point.
564 Entry_Call
.State
:= Cancelled
;
568 if not Entry_Call
.Requeue_With_Abort
or else
569 Entry_Call
.Mode
/= Conditional_Call
571 E
:= Protected_Entry_Index
(Entry_Call
.E
);
573 (New_Object
.Entry_Queues
(E
), Entry_Call
);
574 Update_For_Queue_To_PO
(Entry_Call
,
575 Entry_Call
.Requeue_With_Abort
);
578 PO_Do_Or_Queue
(Self_ID
, New_Object
, Entry_Call
,
579 Entry_Call
.Requeue_With_Abort
);
589 end PO_Service_Entries
;
591 ---------------------
592 -- Protected_Count --
593 ---------------------
595 function Protected_Count
596 (Object
: Protection_Entries
'Class;
597 E
: Protected_Entry_Index
)
601 return Queuing
.Count_Waiting
(Object
.Entry_Queues
(E
));
604 --------------------------
605 -- Protected_Entry_Call --
606 --------------------------
608 -- Compiler interface only. Do not call from within the RTS.
617 -- X : protected_entry_index := 1;
618 -- B85b : communication_block;
619 -- _init_proc (B85b);
621 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
622 -- null_address, conditional_call, B85b, objectF => 0);
623 -- if cancelled (B85b) then
630 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
633 -- The initial part of this procedure does not need to lock the the calling
634 -- task's ATCB, up to the point where the call record first may be queued
635 -- (PO_Do_Or_Queue), since before that no other task will have access to
638 -- If this is a call made inside of an abort deferred region, the call
639 -- should be never abortable.
641 -- If the call was not queued abortably, we need to wait until it is before
642 -- proceeding with the abortable part.
644 -- There are some heuristics here, just to save time for frequently
645 -- occurring cases. For example, we check Initially_Abortable to try to
646 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
647 -- for async. entry calls is to be queued abortably.
649 -- Another heuristic uses the Block.Enqueued to try to avoid calling
650 -- Cancel_Protected_Entry_Call if the call can be served immediately.
652 procedure Protected_Entry_Call
653 (Object
: Protection_Entries_Access
;
654 E
: Protected_Entry_Index
;
655 Uninterpreted_Data
: System
.Address
;
657 Block
: out Communication_Block
)
659 Self_ID
: Task_ID
:= STPO
.Self
;
660 Entry_Call
: Entry_Call_Link
;
661 Initially_Abortable
: Boolean;
662 Ceiling_Violation
: Boolean;
666 (Debug
.Trace
(Self_ID
, "Protected_Entry_Call", 'P'));
668 if Runtime_Traces
then
669 Send_Trace_Info
(PO_Call
, Entry_Index
(E
));
672 if Self_ID
.ATC_Nesting_Level
= ATC_Level
'Last then
674 (Storage_Error
'Identity, "not enough ATC nesting levels");
677 Initialization
.Defer_Abort
(Self_ID
);
678 Lock_Entries
(Object
, Ceiling_Violation
);
680 if Ceiling_Violation
then
682 -- Failed ceiling check
684 Initialization
.Undefer_Abort
(Self_ID
);
688 Block
.Self
:= Self_ID
;
689 Self_ID
.ATC_Nesting_Level
:= Self_ID
.ATC_Nesting_Level
+ 1;
691 (Debug
.Trace
(Self_ID
, "PEC: entered ATC level: " &
692 ATC_Level
'Image (Self_ID
.ATC_Nesting_Level
), 'A'));
694 Self_ID
.Entry_Calls
(Self_ID
.ATC_Nesting_Level
)'Access;
695 Entry_Call
.Next
:= null;
696 Entry_Call
.Mode
:= Mode
;
697 Entry_Call
.Cancellation_Attempted
:= False;
699 if Self_ID
.Deferral_Level
> 1 then
700 Entry_Call
.State
:= Never_Abortable
;
702 Entry_Call
.State
:= Now_Abortable
;
705 Entry_Call
.E
:= Entry_Index
(E
);
706 Entry_Call
.Prio
:= STPO
.Get_Priority
(Self_ID
);
707 Entry_Call
.Uninterpreted_Data
:= Uninterpreted_Data
;
708 Entry_Call
.Called_PO
:= To_Address
(Object
);
709 Entry_Call
.Called_Task
:= null;
710 Entry_Call
.Exception_To_Raise
:= Ada
.Exceptions
.Null_Id
;
712 PO_Do_Or_Queue
(Self_ID
, Object
, Entry_Call
, With_Abort
=> True);
713 Initially_Abortable
:= Entry_Call
.State
= Now_Abortable
;
714 PO_Service_Entries
(Self_ID
, Object
);
716 Unlock_Entries
(Object
);
718 -- Try to prevent waiting later (in Cancel_Protected_Entry_Call)
719 -- for completed or cancelled calls. (This is a heuristic, only.)
721 if Entry_Call
.State
>= Done
then
723 -- Once State >= Done it will not change any more.
725 Self_ID
.ATC_Nesting_Level
:= Self_ID
.ATC_Nesting_Level
- 1;
727 (Debug
.Trace
(Self_ID
, "PEC: exited to ATC level: " &
728 ATC_Level
'Image (Self_ID
.ATC_Nesting_Level
), 'A'));
729 Block
.Enqueued
:= False;
730 Block
.Cancelled
:= Entry_Call
.State
= Cancelled
;
731 Initialization
.Undefer_Abort
(Self_ID
);
732 Entry_Calls
.Check_Exception
(Self_ID
, Entry_Call
);
736 -- In this case we cannot conclude anything,
737 -- since State can change concurrently.
741 -- Now for the general case.
743 if Mode
= Asynchronous_Call
then
745 -- Try to avoid an expensive call.
747 if not Initially_Abortable
then
750 Entry_Calls
.Wait_Until_Abortable
(Self_ID
, Entry_Call
);
753 Entry_Calls
.Wait_Until_Abortable
(Self_ID
, Entry_Call
);
757 elsif Mode
< Asynchronous_Call
then
759 -- Simple_Call or Conditional_Call
763 Entry_Calls
.Wait_For_Completion
(Entry_Call
);
766 STPO
.Write_Lock
(Self_ID
);
767 Entry_Calls
.Wait_For_Completion
(Entry_Call
);
768 STPO
.Unlock
(Self_ID
);
771 Block
.Cancelled
:= Entry_Call
.State
= Cancelled
;
774 pragma Assert
(False);
778 Initialization
.Undefer_Abort
(Self_ID
);
779 Entry_Calls
.Check_Exception
(Self_ID
, Entry_Call
);
780 end Protected_Entry_Call
;
782 ----------------------------
783 -- Protected_Entry_Caller --
784 ----------------------------
786 function Protected_Entry_Caller
787 (Object
: Protection_Entries
'Class) return Task_ID
is
789 return Object
.Call_In_Progress
.Self
;
790 end Protected_Entry_Caller
;
792 -----------------------------
793 -- Requeue_Protected_Entry --
794 -----------------------------
796 -- Compiler interface only. Do not call from within the RTS.
805 -- procedure rPT__E10b (O : address; P : address; E :
806 -- protected_entry_index) is
807 -- type rTVP is access rTV;
809 -- _object : rTVP := rTVP!(O);
812 -- rR : protection renames _object._object;
813 -- vP : integer renames _object.v;
814 -- bP : boolean renames _object.b;
818 -- requeue_protected_entry (rR'unchecked_access, rR'
819 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
823 -- complete_entry_body (_object._object'unchecked_access, objectF =>
828 -- abort_undefer.all;
829 -- exceptional_complete_entry_body (_object._object'
830 -- unchecked_access, current_exception, objectF => 0);
834 procedure Requeue_Protected_Entry
835 (Object
: Protection_Entries_Access
;
836 New_Object
: Protection_Entries_Access
;
837 E
: Protected_Entry_Index
;
838 With_Abort
: Boolean)
840 Entry_Call
: constant Entry_Call_Link
:= Object
.Call_In_Progress
;
844 (Debug
.Trace
(STPO
.Self
, "Requeue_Protected_Entry", 'P'));
845 pragma Assert
(STPO
.Self
.Deferral_Level
> 0);
847 Entry_Call
.E
:= Entry_Index
(E
);
848 Entry_Call
.Called_PO
:= To_Address
(New_Object
);
849 Entry_Call
.Called_Task
:= null;
850 Entry_Call
.Requeue_With_Abort
:= With_Abort
;
851 Object
.Call_In_Progress
:= null;
852 end Requeue_Protected_Entry
;
854 -------------------------------------
855 -- Requeue_Task_To_Protected_Entry --
856 -------------------------------------
858 -- Compiler interface only.
868 -- accept_call (1, A79b);
870 -- requeue_task_to_protected_entry (rTV!(r)._object'
871 -- unchecked_access, 2, false, new_objectF => 0);
874 -- complete_rendezvous;
876 -- when all others =>
877 -- exceptional_complete_rendezvous (get_gnat_exception);
880 procedure Requeue_Task_To_Protected_Entry
881 (New_Object
: Protection_Entries_Access
;
882 E
: Protected_Entry_Index
;
883 With_Abort
: Boolean)
885 Self_ID
: constant Task_ID
:= STPO
.Self
;
886 Entry_Call
: constant Entry_Call_Link
:= Self_ID
.Common
.Call
;
889 Initialization
.Defer_Abort
(Self_ID
);
891 -- We do not need to lock Self_ID here since the call is not abortable
892 -- at this point, and therefore, the caller cannot cancel the call.
894 Entry_Call
.Needs_Requeue
:= True;
895 Entry_Call
.Requeue_With_Abort
:= With_Abort
;
896 Entry_Call
.Called_PO
:= To_Address
(New_Object
);
897 Entry_Call
.Called_Task
:= null;
898 Entry_Call
.E
:= Entry_Index
(E
);
899 Initialization
.Undefer_Abort
(Self_ID
);
900 end Requeue_Task_To_Protected_Entry
;
902 ---------------------
903 -- Service_Entries --
904 ---------------------
906 procedure Service_Entries
(Object
: Protection_Entries_Access
) is
907 Self_ID
: constant Task_ID
:= STPO
.Self
;
909 PO_Service_Entries
(Self_ID
, Object
);
912 --------------------------------
913 -- Timed_Protected_Entry_Call --
914 --------------------------------
916 -- Compiler interface only. Do not call from within the RTS.
918 procedure Timed_Protected_Entry_Call
919 (Object
: Protection_Entries_Access
;
920 E
: Protected_Entry_Index
;
921 Uninterpreted_Data
: System
.Address
;
924 Entry_Call_Successful
: out Boolean)
926 Self_Id
: constant Task_ID
:= STPO
.Self
;
927 Entry_Call
: Entry_Call_Link
;
928 Ceiling_Violation
: Boolean;
932 if Self_Id
.ATC_Nesting_Level
= ATC_Level
'Last then
933 Raise_Exception
(Storage_Error
'Identity,
934 "not enough ATC nesting levels");
937 if Runtime_Traces
then
938 Send_Trace_Info
(POT_Call
, Entry_Index
(E
), Timeout
);
941 Initialization
.Defer_Abort
(Self_Id
);
942 Lock_Entries
(Object
, Ceiling_Violation
);
944 if Ceiling_Violation
then
945 Initialization
.Undefer_Abort
(Self_Id
);
949 Self_Id
.ATC_Nesting_Level
:= Self_Id
.ATC_Nesting_Level
+ 1;
951 (Debug
.Trace
(Self_Id
, "TPEC: exited to ATC level: " &
952 ATC_Level
'Image (Self_Id
.ATC_Nesting_Level
), 'A'));
954 Self_Id
.Entry_Calls
(Self_Id
.ATC_Nesting_Level
)'Access;
955 Entry_Call
.Next
:= null;
956 Entry_Call
.Mode
:= Timed_Call
;
957 Entry_Call
.Cancellation_Attempted
:= False;
959 if Self_Id
.Deferral_Level
> 1 then
960 Entry_Call
.State
:= Never_Abortable
;
962 Entry_Call
.State
:= Now_Abortable
;
965 Entry_Call
.E
:= Entry_Index
(E
);
966 Entry_Call
.Prio
:= STPO
.Get_Priority
(Self_Id
);
967 Entry_Call
.Uninterpreted_Data
:= Uninterpreted_Data
;
968 Entry_Call
.Called_PO
:= To_Address
(Object
);
969 Entry_Call
.Called_Task
:= null;
970 Entry_Call
.Exception_To_Raise
:= Ada
.Exceptions
.Null_Id
;
972 PO_Do_Or_Queue
(Self_Id
, Object
, Entry_Call
, With_Abort
=> True);
973 PO_Service_Entries
(Self_Id
, Object
);
975 Unlock_Entries
(Object
);
977 -- Try to avoid waiting for completed or cancelled calls.
979 if Entry_Call
.State
>= Done
then
980 Self_Id
.ATC_Nesting_Level
:= Self_Id
.ATC_Nesting_Level
- 1;
982 (Debug
.Trace
(Self_Id
, "TPEC: exited to ATC level: " &
983 ATC_Level
'Image (Self_Id
.ATC_Nesting_Level
), 'A'));
984 Entry_Call_Successful
:= Entry_Call
.State
= Done
;
985 Initialization
.Undefer_Abort
(Self_Id
);
986 Entry_Calls
.Check_Exception
(Self_Id
, Entry_Call
);
993 STPO
.Write_Lock
(Self_Id
);
996 Entry_Calls
.Wait_For_Completion_With_Timeout
997 (Entry_Call
, Timeout
, Mode
, Yielded
);
1002 STPO
.Unlock
(Self_Id
);
1005 -- ??? Do we need to yield in case Yielded is False
1007 Initialization
.Undefer_Abort
(Self_Id
);
1008 Entry_Call_Successful
:= Entry_Call
.State
= Done
;
1009 Entry_Calls
.Check_Exception
(Self_Id
, Entry_Call
);
1010 end Timed_Protected_Entry_Call
;
1012 ----------------------------
1013 -- Update_For_Queue_To_PO --
1014 ----------------------------
1016 -- Update the state of an existing entry call, based on
1017 -- whether the current queuing action is with or without abort.
1018 -- Call this only while holding the server's lock.
1019 -- It returns with the server's lock released.
1021 New_State
: constant array (Boolean, Entry_Call_State
)
1022 of Entry_Call_State
:=
1024 (Never_Abortable
=> Never_Abortable
,
1025 Not_Yet_Abortable
=> Now_Abortable
,
1026 Was_Abortable
=> Now_Abortable
,
1027 Now_Abortable
=> Now_Abortable
,
1029 Cancelled
=> Cancelled
),
1031 (Never_Abortable
=> Never_Abortable
,
1032 Not_Yet_Abortable
=> Not_Yet_Abortable
,
1033 Was_Abortable
=> Was_Abortable
,
1034 Now_Abortable
=> Now_Abortable
,
1036 Cancelled
=> Cancelled
)
1039 procedure Update_For_Queue_To_PO
1040 (Entry_Call
: Entry_Call_Link
;
1041 With_Abort
: Boolean)
1043 Old
: Entry_Call_State
:= Entry_Call
.State
;
1045 pragma Assert
(Old
< Done
);
1047 Entry_Call
.State
:= New_State
(With_Abort
, Entry_Call
.State
);
1049 if Entry_Call
.Mode
= Asynchronous_Call
then
1050 if Old
< Was_Abortable
and then
1051 Entry_Call
.State
= Now_Abortable
1057 STPO
.Write_Lock
(Entry_Call
.Self
);
1059 if Entry_Call
.Self
.Common
.State
= Async_Select_Sleep
then
1060 STPO
.Wakeup
(Entry_Call
.Self
, Async_Select_Sleep
);
1063 STPO
.Unlock
(Entry_Call
.Self
);
1071 elsif Entry_Call
.Mode
= Conditional_Call
then
1072 pragma Assert
(Entry_Call
.State
< Was_Abortable
);
1075 end Update_For_Queue_To_PO
;
1077 end System
.Tasking
.Protected_Objects
.Operations
;