Add hppa-openbsd target
[official-gcc.git] / gcc / ada / s-tpobop.adb
blobb92c6af242c8c6e16a1ba14aa13f1cf7a53b94cf
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
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 --
7 -- --
8 -- B o d y --
9 -- --
10 -- --
11 -- Copyright (C) 1998-2001, Free Software Foundation, Inc. --
12 -- --
13 -- GNARL is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNARL; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
23 -- --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
30 -- --
31 -- GNARL was developed by the GNARL team at Florida State University. It is --
32 -- now maintained by Ada Core Technologies, Inc. (http://www.gnat.com). --
33 -- --
34 ------------------------------------------------------------------------------
36 -- This package contains all the extended primitives related to
37 -- Protected_Objects with entries.
39 -- The handling of protected objects with no entries is done in
40 -- System.Tasking.Protected_Objects, the simple routines for protected
41 -- objects with entries in System.Tasking.Protected_Objects.Entries.
43 -- The split between Entries and Operations is needed to break circular
44 -- dependencies inside the run time.
46 -- This package contains all primitives related to Protected_Objects.
47 -- Note: the compiler generates direct calls to this interface, via Rtsfind.
49 with Ada.Exceptions;
50 -- Used for Exception_ID
51 -- Null_Id
52 -- Raise_Exception
54 with System.Task_Primitives.Operations;
55 -- used for Initialize_Lock
56 -- Write_Lock
57 -- Unlock
58 -- Get_Priority
59 -- Wakeup
61 with System.Tasking.Entry_Calls;
62 -- used for Wait_For_Completion
63 -- Wait_Until_Abortable
64 -- Wait_For_Completion_With_Timeout
66 with System.Tasking.Initialization;
67 -- Used for Defer_Abort,
68 -- Undefer_Abort,
69 -- Change_Base_Priority
71 pragma Elaborate_All (System.Tasking.Initialization);
72 -- This insures that tasking is initialized if any protected objects are
73 -- created.
75 with System.Tasking.Queuing;
76 -- used for Enqueue
77 -- Broadcast_Program_Error
78 -- Select_Protected_Entry_Call
79 -- Onqueue
80 -- Count_Waiting
82 with System.Tasking.Rendezvous;
83 -- used for Task_Do_Or_Queue
85 with System.Tasking.Debug;
86 -- used for Trace
88 with System.Parameters;
89 -- used for Single_Lock
90 -- Runtime_Traces
92 with System.Traces.Tasking;
93 -- used for Send_Trace_Info
95 package body System.Tasking.Protected_Objects.Operations is
97 package STPO renames System.Task_Primitives.Operations;
99 use Parameters;
100 use Task_Primitives;
101 use Ada.Exceptions;
102 use Entries;
104 use System.Traces;
105 use System.Traces.Tasking;
107 -----------------------
108 -- Local Subprograms --
109 -----------------------
111 procedure Update_For_Queue_To_PO
112 (Entry_Call : Entry_Call_Link;
113 With_Abort : Boolean);
114 pragma Inline (Update_For_Queue_To_PO);
115 -- Update the state of an existing entry call to reflect
116 -- the fact that it is being enqueued, based on
117 -- whether the current queuing action is with or without abort.
118 -- Call this only while holding the PO's lock.
119 -- It returns with the PO's lock still held.
121 ---------------------------------
122 -- Cancel_Protected_Entry_Call --
123 ---------------------------------
125 -- Compiler interface only. Do not call from within the RTS.
126 -- This should have analogous effect to Cancel_Task_Entry_Call,
127 -- setting the value of Block.Cancelled instead of returning
128 -- the parameter value Cancelled.
130 -- The effect should be idempotent, since the call may already
131 -- have been dequeued.
133 -- source code:
135 -- select r.e;
136 -- ...A...
137 -- then abort
138 -- ...B...
139 -- end select;
141 -- expanded code:
143 -- declare
144 -- X : protected_entry_index := 1;
145 -- B80b : communication_block;
146 -- _init_proc (B80b);
147 -- begin
148 -- begin
149 -- A79b : label
150 -- A79b : declare
151 -- procedure _clean is
152 -- begin
153 -- if enqueued (B80b) then
154 -- cancel_protected_entry_call (B80b);
155 -- end if;
156 -- return;
157 -- end _clean;
158 -- begin
159 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
160 -- null_address, asynchronous_call, B80b, objectF => 0);
161 -- if enqueued (B80b) then
162 -- ...B...
163 -- end if;
164 -- at end
165 -- _clean;
166 -- end A79b;
167 -- exception
168 -- when _abort_signal =>
169 -- abort_undefer.all;
170 -- null;
171 -- end;
172 -- if not cancelled (B80b) then
173 -- x := ...A...
174 -- end if;
175 -- end;
177 -- If the entry call completes after we get into the abortable part,
178 -- Abort_Signal should be raised and ATC will take us to the at-end
179 -- handler, which will call _clean.
181 -- If the entry call returns with the call already completed,
182 -- we can skip this, and use the "if enqueued()" to go past
183 -- the at-end handler, but we will still call _clean.
185 -- If the abortable part completes before the entry call is Done,
186 -- it will call _clean.
188 -- If the entry call or the abortable part raises an exception,
189 -- we will still call _clean, but the value of Cancelled should not matter.
191 -- Whoever calls _clean first gets to decide whether the call
192 -- has been "cancelled".
194 -- Enqueued should be true if there is any chance that the call
195 -- is still on a queue. It seems to be safe to make it True if
196 -- the call was Onqueue at some point before return from
197 -- Protected_Entry_Call.
199 -- Cancelled should be true iff the abortable part completed
200 -- and succeeded in cancelling the entry call before it completed.
202 -- ?????
203 -- The need for Enqueued is less obvious.
204 -- The "if enqueued ()" tests are not necessary, since both
205 -- Cancel_Protected_Entry_Call and Protected_Entry_Call must
206 -- do the same test internally, with locking. The one that
207 -- makes cancellation conditional may be a useful heuristic
208 -- since at least 1/2 the time the call should be off-queue
209 -- by that point. The other one seems totally useless, since
210 -- Protected_Entry_Call must do the same check and then
211 -- possibly wait for the call to be abortable, internally.
213 -- We can check Call.State here without locking the caller's mutex,
214 -- since the call must be over after returning from Wait_For_Completion.
215 -- No other task can access the call record at this point.
217 procedure Cancel_Protected_Entry_Call
218 (Block : in out Communication_Block) is
219 begin
220 Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
221 end Cancel_Protected_Entry_Call;
223 ---------------
224 -- Cancelled --
225 ---------------
227 function Cancelled (Block : Communication_Block) return Boolean is
228 begin
229 return Block.Cancelled;
230 end Cancelled;
232 -------------------------
233 -- Complete_Entry_Body --
234 -------------------------
236 procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
237 begin
238 Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
239 end Complete_Entry_Body;
241 --------------
242 -- Enqueued --
243 --------------
245 function Enqueued (Block : Communication_Block) return Boolean is
246 begin
247 return Block.Enqueued;
248 end Enqueued;
250 -------------------------------------
251 -- Exceptional_Complete_Entry_Body --
252 -------------------------------------
254 procedure Exceptional_Complete_Entry_Body
255 (Object : Protection_Entries_Access;
256 Ex : Ada.Exceptions.Exception_Id)
258 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
259 begin
260 pragma Debug
261 (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
263 -- We must have abort deferred, since we are inside
264 -- a protected operation.
266 if Entry_Call /= null then
267 -- The call was not requeued.
269 Entry_Call.Exception_To_Raise := Ex;
271 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
272 -- PO_Service_Entries on return.
273 end if;
275 if Runtime_Traces then
276 Send_Trace_Info (PO_Done, Entry_Call.Self);
277 end if;
278 end Exceptional_Complete_Entry_Body;
280 --------------------
281 -- PO_Do_Or_Queue --
282 --------------------
284 procedure PO_Do_Or_Queue
285 (Self_ID : Task_ID;
286 Object : Protection_Entries_Access;
287 Entry_Call : Entry_Call_Link;
288 With_Abort : Boolean)
290 E : Protected_Entry_Index := Protected_Entry_Index (Entry_Call.E);
291 New_Object : Protection_Entries_Access;
292 Ceiling_Violation : Boolean;
293 Barrier_Value : Boolean;
294 Result : Boolean;
296 begin
297 -- When the Action procedure for an entry body returns, it is either
298 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
299 -- is queued, having executed a requeue statement.
301 Barrier_Value :=
302 Object.Entry_Bodies (
303 Object.Find_Body_Index (Object.Compiler_Info, E)).
304 Barrier (Object.Compiler_Info, E);
306 if Barrier_Value then
308 -- Not abortable while service is in progress.
310 if Entry_Call.State = Now_Abortable then
311 Entry_Call.State := Was_Abortable;
312 end if;
314 Object.Call_In_Progress := Entry_Call;
316 pragma Debug
317 (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
318 Object.Entry_Bodies (
319 Object.Find_Body_Index (Object.Compiler_Info, E)).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;
328 if Single_Lock then
329 STPO.Lock_RTS;
330 end if;
332 STPO.Write_Lock (Entry_Call.Self);
333 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
334 STPO.Unlock (Entry_Call.Self);
336 if Single_Lock then
337 STPO.Unlock_RTS;
338 end if;
340 else
341 -- Body of current entry requeued the call
342 New_Object := To_Protection (Entry_Call.Called_PO);
344 if New_Object = null then
346 -- Call was requeued to a task
348 if Single_Lock then
349 STPO.Lock_RTS;
350 end if;
352 Result := Rendezvous.Task_Do_Or_Queue
353 (Self_ID, Entry_Call,
354 With_Abort => Entry_Call.Requeue_With_Abort);
356 if not Result then
357 Queuing.Broadcast_Program_Error
358 (Self_ID, Object, Entry_Call, RTS_Locked => True);
359 end if;
361 if Single_Lock then
362 STPO.Unlock_RTS;
363 end if;
365 return;
366 end if;
368 if Object /= New_Object then
369 -- Requeue is on a different object
371 Lock_Entries (New_Object, Ceiling_Violation);
373 if Ceiling_Violation then
374 Object.Call_In_Progress := null;
375 Queuing.Broadcast_Program_Error
376 (Self_ID, Object, Entry_Call);
378 else
379 PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call, With_Abort);
380 PO_Service_Entries (Self_ID, New_Object);
381 Unlock_Entries (New_Object);
382 end if;
384 else
385 -- Requeue is on same protected object
387 if Entry_Call.Requeue_With_Abort
388 and then Entry_Call.Cancellation_Attempted
389 then
390 -- If this is a requeue with abort and someone tried
391 -- to cancel this call, cancel it at this point.
393 Entry_Call.State := Cancelled;
394 return;
395 end if;
397 if not With_Abort or else
398 Entry_Call.Mode /= Conditional_Call
399 then
400 E := Protected_Entry_Index (Entry_Call.E);
401 Queuing.Enqueue
402 (New_Object.Entry_Queues (E), Entry_Call);
403 Update_For_Queue_To_PO (Entry_Call, With_Abort);
405 else
406 -- ?????
407 -- Can we convert this recursion to a loop?
409 PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call, With_Abort);
410 end if;
411 end if;
412 end if;
414 elsif Entry_Call.Mode /= Conditional_Call or else
415 not With_Abort then
416 Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
417 Update_For_Queue_To_PO (Entry_Call, With_Abort);
419 else
420 -- Conditional_Call and With_Abort
422 if Single_Lock then
423 STPO.Lock_RTS;
424 end if;
426 STPO.Write_Lock (Entry_Call.Self);
427 pragma Assert (Entry_Call.State >= Was_Abortable);
428 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
429 STPO.Unlock (Entry_Call.Self);
431 if Single_Lock then
432 STPO.Unlock_RTS;
433 end if;
434 end if;
436 exception
437 when others =>
438 Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
439 end PO_Do_Or_Queue;
441 ------------------------
442 -- PO_Service_Entries --
443 ------------------------
445 procedure PO_Service_Entries
446 (Self_ID : Task_ID;
447 Object : Protection_Entries_Access)
449 Entry_Call : Entry_Call_Link;
450 E : Protected_Entry_Index;
451 Caller : Task_ID;
452 New_Object : Protection_Entries_Access;
453 Ceiling_Violation : Boolean;
454 Result : Boolean;
456 begin
457 loop
458 Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
460 if Entry_Call /= null then
461 E := Protected_Entry_Index (Entry_Call.E);
463 -- Not abortable while service is in progress.
465 if Entry_Call.State = Now_Abortable then
466 Entry_Call.State := Was_Abortable;
467 end if;
469 Object.Call_In_Progress := Entry_Call;
471 begin
472 if Runtime_Traces then
473 Send_Trace_Info (PO_Run, Self_ID,
474 Entry_Call.Self, Entry_Index (E));
475 end if;
477 pragma Debug
478 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
479 Object.Entry_Bodies (
480 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
481 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
482 exception
483 when others =>
484 Queuing.Broadcast_Program_Error
485 (Self_ID, Object, Entry_Call);
486 end;
488 if Object.Call_In_Progress /= null then
489 Object.Call_In_Progress := null;
490 Caller := Entry_Call.Self;
492 if Single_Lock then
493 STPO.Lock_RTS;
494 end if;
496 STPO.Write_Lock (Caller);
497 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
498 STPO.Unlock (Caller);
500 if Single_Lock then
501 STPO.Unlock_RTS;
502 end if;
504 else
505 -- Call needs to be requeued
507 New_Object := To_Protection (Entry_Call.Called_PO);
509 if New_Object = null then
511 -- Call is to be requeued to a task entry
513 if Single_Lock then
514 STPO.Lock_RTS;
515 end if;
517 Result := Rendezvous.Task_Do_Or_Queue
518 (Self_ID, Entry_Call,
519 With_Abort => Entry_Call.Requeue_With_Abort);
521 if not Result then
522 Queuing.Broadcast_Program_Error
523 (Self_ID, Object, Entry_Call, RTS_Locked => True);
524 end if;
526 if Single_Lock then
527 STPO.Unlock_RTS;
528 end if;
530 else
531 -- Call should be requeued to a PO
533 if Object /= New_Object then
534 -- Requeue is to different PO
536 Lock_Entries (New_Object, Ceiling_Violation);
538 if Ceiling_Violation then
539 Object.Call_In_Progress := null;
540 Queuing.Broadcast_Program_Error
541 (Self_ID, Object, Entry_Call);
543 else
544 PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call,
545 Entry_Call.Requeue_With_Abort);
546 PO_Service_Entries (Self_ID, New_Object);
547 Unlock_Entries (New_Object);
548 end if;
550 else
551 -- Requeue is to same protected object
553 -- ??? Try to compensate apparent failure of the
554 -- scheduler on some OS (e.g VxWorks) to give higher
555 -- priority tasks a chance to run (see CXD6002).
557 STPO.Yield (False);
559 if Entry_Call.Requeue_With_Abort
560 and then Entry_Call.Cancellation_Attempted
561 then
562 -- If this is a requeue with abort and someone tried
563 -- to cancel this call, cancel it at this point.
565 Entry_Call.State := Cancelled;
566 exit;
567 end if;
569 if not Entry_Call.Requeue_With_Abort or else
570 Entry_Call.Mode /= Conditional_Call
571 then
572 E := Protected_Entry_Index (Entry_Call.E);
573 Queuing.Enqueue
574 (New_Object.Entry_Queues (E), Entry_Call);
575 Update_For_Queue_To_PO (Entry_Call,
576 Entry_Call.Requeue_With_Abort);
578 else
579 PO_Do_Or_Queue (Self_ID, New_Object, Entry_Call,
580 Entry_Call.Requeue_With_Abort);
581 end if;
582 end if;
583 end if;
584 end if;
586 else
587 exit;
588 end if;
589 end loop;
590 end PO_Service_Entries;
592 ---------------------
593 -- Protected_Count --
594 ---------------------
596 function Protected_Count
597 (Object : Protection_Entries'Class;
598 E : Protected_Entry_Index)
599 return Natural
601 begin
602 return Queuing.Count_Waiting (Object.Entry_Queues (E));
603 end Protected_Count;
605 --------------------------
606 -- Protected_Entry_Call --
607 --------------------------
609 -- Compiler interface only. Do not call from within the RTS.
611 -- select r.e;
612 -- ...A...
613 -- else
614 -- ...B...
615 -- end select;
617 -- declare
618 -- X : protected_entry_index := 1;
619 -- B85b : communication_block;
620 -- _init_proc (B85b);
621 -- begin
622 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
623 -- null_address, conditional_call, B85b, objectF => 0);
624 -- if cancelled (B85b) then
625 -- ...B...
626 -- else
627 -- ...A...
628 -- end if;
629 -- end;
631 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
632 -- entry call.
634 -- The initial part of this procedure does not need to lock the the calling
635 -- task's ATCB, up to the point where the call record first may be queued
636 -- (PO_Do_Or_Queue), since before that no other task will have access to
637 -- the record.
639 -- If this is a call made inside of an abort deferred region, the call
640 -- should be never abortable.
642 -- If the call was not queued abortably, we need to wait until it is before
643 -- proceeding with the abortable part.
645 -- There are some heuristics here, just to save time for frequently
646 -- occurring cases. For example, we check Initially_Abortable to try to
647 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
648 -- for async. entry calls is to be queued abortably.
650 -- Another heuristic uses the Block.Enqueued to try to avoid calling
651 -- Cancel_Protected_Entry_Call if the call can be served immediately.
653 procedure Protected_Entry_Call
654 (Object : Protection_Entries_Access;
655 E : Protected_Entry_Index;
656 Uninterpreted_Data : System.Address;
657 Mode : Call_Modes;
658 Block : out Communication_Block)
660 Self_ID : Task_ID := STPO.Self;
661 Entry_Call : Entry_Call_Link;
662 Initially_Abortable : Boolean;
663 Ceiling_Violation : Boolean;
665 begin
666 pragma Debug
667 (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
669 if Runtime_Traces then
670 Send_Trace_Info (PO_Call, Entry_Index (E));
671 end if;
673 if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
674 Raise_Exception
675 (Storage_Error'Identity, "not enough ATC nesting levels");
676 end if;
678 Initialization.Defer_Abort (Self_ID);
679 Lock_Entries (Object, Ceiling_Violation);
681 if Ceiling_Violation then
683 -- Failed ceiling check
685 Initialization.Undefer_Abort (Self_ID);
686 raise Program_Error;
687 end if;
689 Block.Self := Self_ID;
690 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
691 pragma Debug
692 (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
693 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
694 Entry_Call :=
695 Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
696 Entry_Call.Next := null;
697 Entry_Call.Mode := Mode;
698 Entry_Call.Cancellation_Attempted := False;
700 if Self_ID.Deferral_Level > 1 then
701 Entry_Call.State := Never_Abortable;
702 else
703 Entry_Call.State := Now_Abortable;
704 end if;
706 Entry_Call.E := Entry_Index (E);
707 Entry_Call.Prio := STPO.Get_Priority (Self_ID);
708 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
709 Entry_Call.Called_PO := To_Address (Object);
710 Entry_Call.Called_Task := null;
711 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
713 PO_Do_Or_Queue (Self_ID, Object, Entry_Call, With_Abort => True);
714 Initially_Abortable := Entry_Call.State = Now_Abortable;
715 PO_Service_Entries (Self_ID, Object);
717 Unlock_Entries (Object);
719 -- Try to prevent waiting later (in Cancel_Protected_Entry_Call)
720 -- for completed or cancelled calls. (This is a heuristic, only.)
722 if Entry_Call.State >= Done then
724 -- Once State >= Done it will not change any more.
726 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level - 1;
727 pragma Debug
728 (Debug.Trace (Self_ID, "PEC: exited to ATC level: " &
729 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
730 Block.Enqueued := False;
731 Block.Cancelled := Entry_Call.State = Cancelled;
732 Initialization.Undefer_Abort (Self_ID);
733 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
734 return;
736 else
737 -- In this case we cannot conclude anything,
738 -- since State can change concurrently.
739 null;
740 end if;
742 -- Now for the general case.
744 if Mode = Asynchronous_Call then
746 -- Try to avoid an expensive call.
748 if not Initially_Abortable then
749 if Single_Lock then
750 STPO.Lock_RTS;
751 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
752 STPO.Unlock_RTS;
753 else
754 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
755 end if;
756 end if;
758 elsif Mode < Asynchronous_Call then
760 -- Simple_Call or Conditional_Call
762 if Single_Lock then
763 STPO.Lock_RTS;
764 Entry_Calls.Wait_For_Completion (Entry_Call);
765 STPO.Unlock_RTS;
766 else
767 STPO.Write_Lock (Self_ID);
768 Entry_Calls.Wait_For_Completion (Entry_Call);
769 STPO.Unlock (Self_ID);
770 end if;
772 Block.Cancelled := Entry_Call.State = Cancelled;
774 else
775 pragma Assert (False);
776 null;
777 end if;
779 Initialization.Undefer_Abort (Self_ID);
780 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
781 end Protected_Entry_Call;
783 ----------------------------
784 -- Protected_Entry_Caller --
785 ----------------------------
787 function Protected_Entry_Caller
788 (Object : Protection_Entries'Class) return Task_ID is
789 begin
790 return Object.Call_In_Progress.Self;
791 end Protected_Entry_Caller;
793 -----------------------------
794 -- Requeue_Protected_Entry --
795 -----------------------------
797 -- Compiler interface only. Do not call from within the RTS.
799 -- entry e when b is
800 -- begin
801 -- b := false;
802 -- ...A...
803 -- requeue e2;
804 -- end e;
806 -- procedure rPT__E10b (O : address; P : address; E :
807 -- protected_entry_index) is
808 -- type rTVP is access rTV;
809 -- freeze rTVP []
810 -- _object : rTVP := rTVP!(O);
811 -- begin
812 -- declare
813 -- rR : protection renames _object._object;
814 -- vP : integer renames _object.v;
815 -- bP : boolean renames _object.b;
816 -- begin
817 -- b := false;
818 -- ...A...
819 -- requeue_protected_entry (rR'unchecked_access, rR'
820 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
821 -- 0);
822 -- return;
823 -- end;
824 -- complete_entry_body (_object._object'unchecked_access, objectF =>
825 -- 0);
826 -- return;
827 -- exception
828 -- when others =>
829 -- abort_undefer.all;
830 -- exceptional_complete_entry_body (_object._object'
831 -- unchecked_access, current_exception, objectF => 0);
832 -- return;
833 -- end rPT__E10b;
835 procedure Requeue_Protected_Entry
836 (Object : Protection_Entries_Access;
837 New_Object : Protection_Entries_Access;
838 E : Protected_Entry_Index;
839 With_Abort : Boolean)
841 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
843 begin
844 pragma Debug
845 (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
846 pragma Assert (STPO.Self.Deferral_Level > 0);
848 Entry_Call.E := Entry_Index (E);
849 Entry_Call.Called_PO := To_Address (New_Object);
850 Entry_Call.Called_Task := null;
851 Entry_Call.Requeue_With_Abort := With_Abort;
852 Object.Call_In_Progress := null;
853 end Requeue_Protected_Entry;
855 -------------------------------------
856 -- Requeue_Task_To_Protected_Entry --
857 -------------------------------------
859 -- Compiler interface only.
861 -- accept e1 do
862 -- ...A...
863 -- requeue r.e2;
864 -- end e1;
866 -- A79b : address;
867 -- L78b : label
868 -- begin
869 -- accept_call (1, A79b);
870 -- ...A...
871 -- requeue_task_to_protected_entry (rTV!(r)._object'
872 -- unchecked_access, 2, false, new_objectF => 0);
873 -- goto L78b;
874 -- <<L78b>>
875 -- complete_rendezvous;
876 -- exception
877 -- when all others =>
878 -- exceptional_complete_rendezvous (get_gnat_exception);
879 -- end;
881 procedure Requeue_Task_To_Protected_Entry
882 (New_Object : Protection_Entries_Access;
883 E : Protected_Entry_Index;
884 With_Abort : Boolean)
886 Self_ID : constant Task_ID := STPO.Self;
887 Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
889 begin
890 Initialization.Defer_Abort (Self_ID);
892 -- We do not need to lock Self_ID here since the call is not abortable
893 -- at this point, and therefore, the caller cannot cancel the call.
895 Entry_Call.Needs_Requeue := True;
896 Entry_Call.Requeue_With_Abort := With_Abort;
897 Entry_Call.Called_PO := To_Address (New_Object);
898 Entry_Call.Called_Task := null;
899 Entry_Call.E := Entry_Index (E);
900 Initialization.Undefer_Abort (Self_ID);
901 end Requeue_Task_To_Protected_Entry;
903 ---------------------
904 -- Service_Entries --
905 ---------------------
907 procedure Service_Entries (Object : Protection_Entries_Access) is
908 Self_ID : constant Task_ID := STPO.Self;
909 begin
910 PO_Service_Entries (Self_ID, Object);
911 end Service_Entries;
913 --------------------------------
914 -- Timed_Protected_Entry_Call --
915 --------------------------------
917 -- Compiler interface only. Do not call from within the RTS.
919 procedure Timed_Protected_Entry_Call
920 (Object : Protection_Entries_Access;
921 E : Protected_Entry_Index;
922 Uninterpreted_Data : System.Address;
923 Timeout : Duration;
924 Mode : Delay_Modes;
925 Entry_Call_Successful : out Boolean)
927 Self_Id : constant Task_ID := STPO.Self;
928 Entry_Call : Entry_Call_Link;
929 Ceiling_Violation : Boolean;
930 Yielded : Boolean;
932 begin
933 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
934 Raise_Exception (Storage_Error'Identity,
935 "not enough ATC nesting levels");
936 end if;
938 if Runtime_Traces then
939 Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
940 end if;
942 Initialization.Defer_Abort (Self_Id);
943 Lock_Entries (Object, Ceiling_Violation);
945 if Ceiling_Violation then
946 Initialization.Undefer_Abort (Self_Id);
947 raise Program_Error;
948 end if;
950 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
951 pragma Debug
952 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
953 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
954 Entry_Call :=
955 Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
956 Entry_Call.Next := null;
957 Entry_Call.Mode := Timed_Call;
958 Entry_Call.Cancellation_Attempted := False;
960 if Self_Id.Deferral_Level > 1 then
961 Entry_Call.State := Never_Abortable;
962 else
963 Entry_Call.State := Now_Abortable;
964 end if;
966 Entry_Call.E := Entry_Index (E);
967 Entry_Call.Prio := STPO.Get_Priority (Self_Id);
968 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
969 Entry_Call.Called_PO := To_Address (Object);
970 Entry_Call.Called_Task := null;
971 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
973 PO_Do_Or_Queue (Self_Id, Object, Entry_Call, With_Abort => True);
974 PO_Service_Entries (Self_Id, Object);
976 Unlock_Entries (Object);
978 -- Try to avoid waiting for completed or cancelled calls.
980 if Entry_Call.State >= Done then
981 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level - 1;
982 pragma Debug
983 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
984 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
985 Entry_Call_Successful := Entry_Call.State = Done;
986 Initialization.Undefer_Abort (Self_Id);
987 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
988 return;
989 end if;
991 if Single_Lock then
992 STPO.Lock_RTS;
993 else
994 STPO.Write_Lock (Self_Id);
995 end if;
997 Entry_Calls.Wait_For_Completion_With_Timeout
998 (Entry_Call, Timeout, Mode, Yielded);
1000 if Single_Lock then
1001 STPO.Unlock_RTS;
1002 else
1003 STPO.Unlock (Self_Id);
1004 end if;
1006 -- ??? Do we need to yield in case Yielded is False
1008 Initialization.Undefer_Abort (Self_Id);
1009 Entry_Call_Successful := Entry_Call.State = Done;
1010 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1011 end Timed_Protected_Entry_Call;
1013 ----------------------------
1014 -- Update_For_Queue_To_PO --
1015 ----------------------------
1017 -- Update the state of an existing entry call, based on
1018 -- whether the current queuing action is with or without abort.
1019 -- Call this only while holding the server's lock.
1020 -- It returns with the server's lock released.
1022 New_State : constant array (Boolean, Entry_Call_State)
1023 of Entry_Call_State :=
1024 (True =>
1025 (Never_Abortable => Never_Abortable,
1026 Not_Yet_Abortable => Now_Abortable,
1027 Was_Abortable => Now_Abortable,
1028 Now_Abortable => Now_Abortable,
1029 Done => Done,
1030 Cancelled => Cancelled),
1031 False =>
1032 (Never_Abortable => Never_Abortable,
1033 Not_Yet_Abortable => Not_Yet_Abortable,
1034 Was_Abortable => Was_Abortable,
1035 Now_Abortable => Now_Abortable,
1036 Done => Done,
1037 Cancelled => Cancelled)
1040 procedure Update_For_Queue_To_PO
1041 (Entry_Call : Entry_Call_Link;
1042 With_Abort : Boolean)
1044 Old : Entry_Call_State := Entry_Call.State;
1045 begin
1046 pragma Assert (Old < Done);
1048 Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1050 if Entry_Call.Mode = Asynchronous_Call then
1051 if Old < Was_Abortable and then
1052 Entry_Call.State = Now_Abortable
1053 then
1054 if Single_Lock then
1055 STPO.Lock_RTS;
1056 end if;
1058 STPO.Write_Lock (Entry_Call.Self);
1060 if Entry_Call.Self.Common.State = Async_Select_Sleep then
1061 STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1062 end if;
1064 STPO.Unlock (Entry_Call.Self);
1066 if Single_Lock then
1067 STPO.Unlock_RTS;
1068 end if;
1070 end if;
1072 elsif Entry_Call.Mode = Conditional_Call then
1073 pragma Assert (Entry_Call.State < Was_Abortable);
1074 null;
1075 end if;
1076 end Update_For_Queue_To_PO;
1078 end System.Tasking.Protected_Objects.Operations;