OSDN Git Service

PR c++/27714
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-tpobop.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                 GNAT 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 --         Copyright (C) 1998-2006, Free Software Foundation, Inc.          --
11 --                                                                          --
12 -- GNARL is free software; you can  redistribute it  and/or modify it under --
13 -- terms of the  GNU General Public License as published  by the Free Soft- --
14 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
15 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
18 -- for  more details.  You should have  received  a copy of the GNU General --
19 -- Public License  distributed with GNARL; see file COPYING.  If not, write --
20 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
21 -- Boston, MA 02110-1301, USA.                                              --
22 --                                                                          --
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.                                      --
29 --                                                                          --
30 -- GNARL was developed by the GNARL team at Florida State University.       --
31 -- Extensive contributions were provided by Ada Core Technologies, Inc.     --
32 --                                                                          --
33 ------------------------------------------------------------------------------
34
35 --  This package contains all the extended primitives related to
36 --  Protected_Objects with entries.
37
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.
41
42 --  The split between Entries and Operations is needed to break circular
43 --  dependencies inside the run time.
44
45 --  This package contains all primitives related to Protected_Objects.
46 --  Note: the compiler generates direct calls to this interface, via Rtsfind.
47
48 with System.Task_Primitives.Operations;
49 --  used for Initialize_Lock
50 --           Write_Lock
51 --           Unlock
52 --           Get_Priority
53 --           Wakeup
54
55 with System.Tasking.Entry_Calls;
56 --  used for Wait_For_Completion
57 --           Wait_Until_Abortable
58 --           Wait_For_Completion_With_Timeout
59
60 with System.Tasking.Initialization;
61 --  Used for Defer_Abort,
62 --           Undefer_Abort,
63 --           Change_Base_Priority
64
65 pragma Elaborate_All (System.Tasking.Initialization);
66 --  This insures that tasking is initialized if any protected objects are
67 --  created.
68
69 with System.Tasking.Queuing;
70 --  used for Enqueue
71 --           Broadcast_Program_Error
72 --           Select_Protected_Entry_Call
73 --           Onqueue
74 --           Count_Waiting
75
76 with System.Tasking.Rendezvous;
77 --  used for Task_Do_Or_Queue
78
79 with System.Tasking.Utilities;
80 --  used for Exit_One_ATC_Level
81
82 with System.Tasking.Debug;
83 --  used for Trace
84
85 with System.Parameters;
86 --  used for Single_Lock
87 --           Runtime_Traces
88
89 with System.Traces.Tasking;
90 --  used for Send_Trace_Info
91
92 with System.Restrictions;
93 --  used for Run_Time_Restrictions
94
95 package body System.Tasking.Protected_Objects.Operations is
96
97    package STPO renames System.Task_Primitives.Operations;
98
99    use Parameters;
100    use Task_Primitives;
101    use Ada.Exceptions;
102    use Entries;
103
104    use System.Restrictions;
105    use System.Restrictions.Rident;
106    use System.Traces;
107    use System.Traces.Tasking;
108
109    -----------------------
110    -- Local Subprograms --
111    -----------------------
112
113    procedure Update_For_Queue_To_PO
114      (Entry_Call : Entry_Call_Link;
115       With_Abort : Boolean);
116    pragma Inline (Update_For_Queue_To_PO);
117    --  Update the state of an existing entry call to reflect
118    --  the fact that it is being enqueued, based on
119    --  whether the current queuing action is with or without abort.
120    --  Call this only while holding the PO's lock.
121    --  It returns with the PO's lock still held.
122
123    procedure Requeue_Call
124      (Self_Id    : Task_Id;
125       Object     : Protection_Entries_Access;
126       Entry_Call : Entry_Call_Link;
127       With_Abort : Boolean);
128    --  Handle requeue of Entry_Call.
129    --  In particular, queue the call if needed, or service it immediately
130    --  if possible.
131
132    ---------------------------------
133    -- Cancel_Protected_Entry_Call --
134    ---------------------------------
135
136    --  Compiler interface only.  Do not call from within the RTS.
137    --  This should have analogous effect to Cancel_Task_Entry_Call,
138    --  setting the value of Block.Cancelled instead of returning
139    --  the parameter value Cancelled.
140
141    --  The effect should be idempotent, since the call may already
142    --  have been dequeued.
143
144    --  source code:
145
146    --      select r.e;
147    --         ...A...
148    --      then abort
149    --         ...B...
150    --      end select;
151
152    --  expanded code:
153
154    --      declare
155    --         X : protected_entry_index := 1;
156    --         B80b : communication_block;
157    --         communication_blockIP (B80b);
158    --      begin
159    --         begin
160    --            A79b : label
161    --            A79b : declare
162    --               procedure _clean is
163    --               begin
164    --                  if enqueued (B80b) then
165    --                     cancel_protected_entry_call (B80b);
166    --                  end if;
167    --                  return;
168    --               end _clean;
169    --            begin
170    --               protected_entry_call (rTV!(r)._object'unchecked_access, X,
171    --                 null_address, asynchronous_call, B80b, objectF => 0);
172    --               if enqueued (B80b) then
173    --                  ...B...
174    --               end if;
175    --            at end
176    --               _clean;
177    --            end A79b;
178    --         exception
179    --            when _abort_signal =>
180    --               abort_undefer.all;
181    --               null;
182    --         end;
183    --         if not cancelled (B80b) then
184    --            x := ...A...
185    --         end if;
186    --      end;
187
188    --  If the entry call completes after we get into the abortable part,
189    --  Abort_Signal should be raised and ATC will take us to the at-end
190    --  handler, which will call _clean.
191
192    --  If the entry call returns with the call already completed,
193    --  we can skip this, and use the "if enqueued()" to go past
194    --  the at-end handler, but we will still call _clean.
195
196    --  If the abortable part completes before the entry call is Done,
197    --  it will call _clean.
198
199    --  If the entry call or the abortable part raises an exception,
200    --  we will still call _clean, but the value of Cancelled should not matter.
201
202    --  Whoever calls _clean first gets to decide whether the call
203    --  has been "cancelled".
204
205    --  Enqueued should be true if there is any chance that the call
206    --  is still on a queue. It seems to be safe to make it True if
207    --  the call was Onqueue at some point before return from
208    --  Protected_Entry_Call.
209
210    --  Cancelled should be true iff the abortable part completed
211    --  and succeeded in cancelling the entry call before it completed.
212
213    --  ?????
214    --  The need for Enqueued is less obvious.
215    --  The "if enqueued ()" tests are not necessary, since both
216    --  Cancel_Protected_Entry_Call and Protected_Entry_Call must
217    --  do the same test internally, with locking. The one that
218    --  makes cancellation conditional may be a useful heuristic
219    --  since at least 1/2 the time the call should be off-queue
220    --  by that point. The other one seems totally useless, since
221    --  Protected_Entry_Call must do the same check and then
222    --  possibly wait for the call to be abortable, internally.
223
224    --  We can check Call.State here without locking the caller's mutex,
225    --  since the call must be over after returning from Wait_For_Completion.
226    --  No other task can access the call record at this point.
227
228    procedure Cancel_Protected_Entry_Call
229      (Block : in out Communication_Block) is
230    begin
231       Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
232    end Cancel_Protected_Entry_Call;
233
234    ---------------
235    -- Cancelled --
236    ---------------
237
238    function Cancelled (Block : Communication_Block) return Boolean is
239    begin
240       return Block.Cancelled;
241    end Cancelled;
242
243    -------------------------
244    -- Complete_Entry_Body --
245    -------------------------
246
247    procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
248    begin
249       Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
250    end Complete_Entry_Body;
251
252    --------------
253    -- Enqueued --
254    --------------
255
256    function Enqueued (Block : Communication_Block) return Boolean is
257    begin
258       return Block.Enqueued;
259    end Enqueued;
260
261    -------------------------------------
262    -- Exceptional_Complete_Entry_Body --
263    -------------------------------------
264
265    procedure Exceptional_Complete_Entry_Body
266      (Object : Protection_Entries_Access;
267       Ex     : Ada.Exceptions.Exception_Id)
268    is
269       procedure Transfer_Occurrence
270         (Target : Ada.Exceptions.Exception_Occurrence_Access;
271          Source : Ada.Exceptions.Exception_Occurrence);
272       pragma Import (C, Transfer_Occurrence, "__gnat_transfer_occurrence");
273
274       Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
275    begin
276       pragma Debug
277        (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
278
279       --  We must have abort deferred, since we are inside
280       --  a protected operation.
281
282       if Entry_Call /= null then
283          --  The call was not requeued.
284
285          Entry_Call.Exception_To_Raise := Ex;
286
287          if Ex /= Ada.Exceptions.Null_Id then
288             Transfer_Occurrence
289               (Entry_Call.Self.Common.Compiler_Data.Current_Excep'Access,
290                STPO.Self.Common.Compiler_Data.Current_Excep);
291          end if;
292
293          --  Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
294          --  PO_Service_Entries on return.
295       end if;
296
297       if Runtime_Traces then
298          Send_Trace_Info (PO_Done, Entry_Call.Self);
299       end if;
300    end Exceptional_Complete_Entry_Body;
301
302    --------------------
303    -- PO_Do_Or_Queue --
304    --------------------
305
306    procedure PO_Do_Or_Queue
307      (Self_ID    : Task_Id;
308       Object     : Protection_Entries_Access;
309       Entry_Call : Entry_Call_Link;
310       With_Abort : Boolean)
311    is
312       E             : constant Protected_Entry_Index :=
313                         Protected_Entry_Index (Entry_Call.E);
314       Barrier_Value : Boolean;
315
316    begin
317       --  When the Action procedure for an entry body returns, it is either
318       --  completed (having called [Exceptional_]Complete_Entry_Body) or it
319       --  is queued, having executed a requeue statement.
320
321       Barrier_Value :=
322         Object.Entry_Bodies (
323           Object.Find_Body_Index (Object.Compiler_Info, E)).
324             Barrier (Object.Compiler_Info, E);
325
326       if Barrier_Value then
327
328          --  Not abortable while service is in progress.
329
330          if Entry_Call.State = Now_Abortable then
331             Entry_Call.State := Was_Abortable;
332          end if;
333
334          Object.Call_In_Progress := Entry_Call;
335
336          pragma Debug
337           (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
338          Object.Entry_Bodies (
339            Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
340              Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
341
342          if Object.Call_In_Progress /= null then
343
344             --  Body of current entry served call to completion
345
346             Object.Call_In_Progress := null;
347
348             if Single_Lock then
349                STPO.Lock_RTS;
350             end if;
351
352             STPO.Write_Lock (Entry_Call.Self);
353             Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
354             STPO.Unlock (Entry_Call.Self);
355
356             if Single_Lock then
357                STPO.Unlock_RTS;
358             end if;
359
360          else
361             Requeue_Call (Self_ID, Object, Entry_Call, With_Abort);
362          end if;
363
364       elsif Entry_Call.Mode /= Conditional_Call
365         or else not With_Abort
366       then
367
368          if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
369               and then
370             Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
371               Queuing.Count_Waiting (Object.Entry_Queues (E))
372          then
373             --  This violates the Max_Entry_Queue_Length restriction,
374             --  raise Program_Error.
375
376             Entry_Call.Exception_To_Raise := Program_Error'Identity;
377
378             if Single_Lock then
379                STPO.Lock_RTS;
380             end if;
381
382             STPO.Write_Lock (Entry_Call.Self);
383             Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
384             STPO.Unlock (Entry_Call.Self);
385
386             if Single_Lock then
387                STPO.Unlock_RTS;
388             end if;
389          else
390             Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
391             Update_For_Queue_To_PO (Entry_Call, With_Abort);
392          end if;
393       else
394          --  Conditional_Call and With_Abort
395
396          if Single_Lock then
397             STPO.Lock_RTS;
398          end if;
399
400          STPO.Write_Lock (Entry_Call.Self);
401          pragma Assert (Entry_Call.State >= Was_Abortable);
402          Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
403          STPO.Unlock (Entry_Call.Self);
404
405          if Single_Lock then
406             STPO.Unlock_RTS;
407          end if;
408       end if;
409
410    exception
411       when others =>
412          Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
413    end PO_Do_Or_Queue;
414
415    ------------------------
416    -- PO_Service_Entries --
417    ------------------------
418
419    procedure PO_Service_Entries
420      (Self_ID       : Task_Id;
421       Object        : Entries.Protection_Entries_Access;
422       Unlock_Object : Boolean := True)
423    is
424       E          : Protected_Entry_Index;
425       Caller     : Task_Id;
426       Entry_Call : Entry_Call_Link;
427
428    begin
429       loop
430          Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
431
432          exit when Entry_Call = null;
433
434          E := Protected_Entry_Index (Entry_Call.E);
435
436          --  Not abortable while service is in progress.
437
438          if Entry_Call.State = Now_Abortable then
439             Entry_Call.State := Was_Abortable;
440          end if;
441
442          Object.Call_In_Progress := Entry_Call;
443
444          begin
445             if Runtime_Traces then
446                Send_Trace_Info (PO_Run, Self_ID,
447                                 Entry_Call.Self, Entry_Index (E));
448             end if;
449
450             pragma Debug
451              (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
452             Object.Entry_Bodies (
453               Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
454                 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
455          exception
456             when others =>
457                Queuing.Broadcast_Program_Error
458                  (Self_ID, Object, Entry_Call);
459          end;
460
461          if Object.Call_In_Progress = null then
462             Requeue_Call
463               (Self_ID, Object, Entry_Call, Entry_Call.Requeue_With_Abort);
464             exit when Entry_Call.State = Cancelled;
465
466          else
467             Object.Call_In_Progress := null;
468             Caller := Entry_Call.Self;
469
470             if Single_Lock then
471                STPO.Lock_RTS;
472             end if;
473
474             STPO.Write_Lock (Caller);
475             Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
476             STPO.Unlock (Caller);
477
478             if Single_Lock then
479                STPO.Unlock_RTS;
480             end if;
481          end if;
482       end loop;
483
484       if Unlock_Object then
485          Unlock_Entries (Object);
486       end if;
487    end PO_Service_Entries;
488
489    ---------------------
490    -- Protected_Count --
491    ---------------------
492
493    function Protected_Count
494      (Object : Protection_Entries'Class;
495       E      : Protected_Entry_Index)
496       return   Natural
497    is
498    begin
499       return Queuing.Count_Waiting (Object.Entry_Queues (E));
500    end Protected_Count;
501
502    --------------------------
503    -- Protected_Entry_Call --
504    --------------------------
505
506    --  Compiler interface only.  Do not call from within the RTS.
507
508    --  select r.e;
509    --     ...A...
510    --  else
511    --     ...B...
512    --  end select;
513
514    --  declare
515    --     X : protected_entry_index := 1;
516    --     B85b : communication_block;
517    --     communication_blockIP (B85b);
518    --  begin
519    --     protected_entry_call (rTV!(r)._object'unchecked_access, X,
520    --       null_address, conditional_call, B85b, objectF => 0);
521    --     if cancelled (B85b) then
522    --        ...B...
523    --     else
524    --        ...A...
525    --     end if;
526    --  end;
527
528    --  See also Cancel_Protected_Entry_Call for code expansion of asynchronous
529    --  entry call.
530
531    --  The initial part of this procedure does not need to lock the the calling
532    --  task's ATCB, up to the point where the call record first may be queued
533    --  (PO_Do_Or_Queue), since before that no other task will have access to
534    --  the record.
535
536    --  If this is a call made inside of an abort deferred region, the call
537    --  should be never abortable.
538
539    --  If the call was not queued abortably, we need to wait until it is before
540    --  proceeding with the abortable part.
541
542    --  There are some heuristics here, just to save time for frequently
543    --  occurring cases. For example, we check Initially_Abortable to try to
544    --  avoid calling the procedure Wait_Until_Abortable, since the normal case
545    --  for async.  entry calls is to be queued abortably.
546
547    --  Another heuristic uses the Block.Enqueued to try to avoid calling
548    --  Cancel_Protected_Entry_Call if the call can be served immediately.
549
550    procedure Protected_Entry_Call
551      (Object              : Protection_Entries_Access;
552       E                   : Protected_Entry_Index;
553       Uninterpreted_Data  : System.Address;
554       Mode                : Call_Modes;
555       Block               : out Communication_Block)
556    is
557       Self_ID             : constant Task_Id  := STPO.Self;
558       Entry_Call          : Entry_Call_Link;
559       Initially_Abortable : Boolean;
560       Ceiling_Violation   : Boolean;
561
562    begin
563       pragma Debug
564         (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
565
566       if Runtime_Traces then
567          Send_Trace_Info (PO_Call, Entry_Index (E));
568       end if;
569
570       if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
571          Raise_Exception
572            (Storage_Error'Identity, "not enough ATC nesting levels");
573       end if;
574
575       --  If pragma Detect_Blocking is active then Program_Error must be
576       --  raised if this potentially blocking operation is called from a
577       --  protected action.
578
579       if Detect_Blocking
580         and then Self_ID.Common.Protected_Action_Nesting > 0
581       then
582          Ada.Exceptions.Raise_Exception
583            (Program_Error'Identity, "potentially blocking operation");
584       end if;
585
586       Initialization.Defer_Abort (Self_ID);
587       Lock_Entries (Object, Ceiling_Violation);
588
589       if Ceiling_Violation then
590
591          --  Failed ceiling check
592
593          Initialization.Undefer_Abort (Self_ID);
594          raise Program_Error;
595       end if;
596
597       Block.Self := Self_ID;
598       Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
599       pragma Debug
600         (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
601          ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
602       Entry_Call :=
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;
607
608       if Self_ID.Deferral_Level > 1 then
609          Entry_Call.State := Never_Abortable;
610       else
611          Entry_Call.State := Now_Abortable;
612       end if;
613
614       Entry_Call.E := Entry_Index (E);
615       Entry_Call.Prio := STPO.Get_Priority (Self_ID);
616       Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
617       Entry_Call.Called_PO := To_Address (Object);
618       Entry_Call.Called_Task := null;
619       Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
620
621       PO_Do_Or_Queue (Self_ID, Object, Entry_Call, With_Abort => True);
622       Initially_Abortable := Entry_Call.State = Now_Abortable;
623       PO_Service_Entries (Self_ID, Object);
624
625       --  Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
626       --  for completed or cancelled calls.  (This is a heuristic, only.)
627
628       if Entry_Call.State >= Done then
629
630          --  Once State >= Done it will not change any more.
631
632          if Single_Lock then
633             STPO.Lock_RTS;
634          end if;
635
636          STPO.Write_Lock (Self_ID);
637          Utilities.Exit_One_ATC_Level (Self_ID);
638          STPO.Unlock (Self_ID);
639
640          if Single_Lock then
641             STPO.Unlock_RTS;
642          end if;
643
644          Block.Enqueued := False;
645          Block.Cancelled := Entry_Call.State = Cancelled;
646          Initialization.Undefer_Abort (Self_ID);
647          Entry_Calls.Check_Exception (Self_ID, Entry_Call);
648          return;
649
650       else
651          --  In this case we cannot conclude anything,
652          --  since State can change concurrently.
653          null;
654       end if;
655
656       --  Now for the general case.
657
658       if Mode = Asynchronous_Call then
659
660          --  Try to avoid an expensive call.
661
662          if not Initially_Abortable then
663             if Single_Lock then
664                STPO.Lock_RTS;
665                Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
666                STPO.Unlock_RTS;
667             else
668                Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
669             end if;
670          end if;
671
672       elsif Mode < Asynchronous_Call then
673
674          --  Simple_Call or Conditional_Call
675
676          if Single_Lock then
677             STPO.Lock_RTS;
678             Entry_Calls.Wait_For_Completion (Entry_Call);
679             STPO.Unlock_RTS;
680          else
681             STPO.Write_Lock (Self_ID);
682             Entry_Calls.Wait_For_Completion (Entry_Call);
683             STPO.Unlock (Self_ID);
684          end if;
685
686          Block.Cancelled := Entry_Call.State = Cancelled;
687
688       else
689          pragma Assert (False);
690          null;
691       end if;
692
693       Initialization.Undefer_Abort (Self_ID);
694       Entry_Calls.Check_Exception (Self_ID, Entry_Call);
695    end Protected_Entry_Call;
696
697    ------------------
698    -- Requeue_Call --
699    ------------------
700
701    procedure Requeue_Call
702      (Self_Id    : Task_Id;
703       Object     : Protection_Entries_Access;
704       Entry_Call : Entry_Call_Link;
705       With_Abort : Boolean)
706    is
707       New_Object        : Protection_Entries_Access;
708       Ceiling_Violation : Boolean;
709       Result            : Boolean;
710       E                 : Protected_Entry_Index;
711
712    begin
713       New_Object := To_Protection (Entry_Call.Called_PO);
714
715       if New_Object = null then
716
717          --  Call is to be requeued to a task entry
718
719          if Single_Lock then
720             STPO.Lock_RTS;
721          end if;
722
723          Result := Rendezvous.Task_Do_Or_Queue
724            (Self_Id, Entry_Call,
725             With_Abort => Entry_Call.Requeue_With_Abort);
726
727          if not Result then
728             Queuing.Broadcast_Program_Error
729               (Self_Id, Object, Entry_Call, RTS_Locked => True);
730          end if;
731
732          if Single_Lock then
733             STPO.Unlock_RTS;
734          end if;
735
736       else
737          --  Call should be requeued to a PO
738
739          if Object /= New_Object then
740
741             --  Requeue is to different PO
742
743             Lock_Entries (New_Object, Ceiling_Violation);
744
745             if Ceiling_Violation then
746                Object.Call_In_Progress := null;
747                Queuing.Broadcast_Program_Error
748                  (Self_Id, Object, Entry_Call);
749
750             else
751                PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call, With_Abort);
752                PO_Service_Entries (Self_Id, New_Object);
753             end if;
754
755          else
756             --  Requeue is to same protected object
757
758             if Entry_Call.Requeue_With_Abort
759               and then Entry_Call.Cancellation_Attempted
760             then
761                --  If this is a requeue with abort and someone tried
762                --  to cancel this call, cancel it at this point.
763
764                Entry_Call.State := Cancelled;
765                return;
766             end if;
767
768             if not With_Abort
769               or else Entry_Call.Mode /= Conditional_Call
770             then
771                E := Protected_Entry_Index (Entry_Call.E);
772
773                if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
774                     and then
775                   Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
776                     Queuing.Count_Waiting (Object.Entry_Queues (E))
777                then
778                   --  This violates the Max_Entry_Queue_Length restriction,
779                   --  raise Program_Error.
780
781                   Entry_Call.Exception_To_Raise := Program_Error'Identity;
782
783                   if Single_Lock then
784                      STPO.Lock_RTS;
785                   end if;
786
787                   STPO.Write_Lock (Entry_Call.Self);
788                   Initialization.Wakeup_Entry_Caller
789                     (Self_Id, Entry_Call, Done);
790                   STPO.Unlock (Entry_Call.Self);
791
792                   if Single_Lock then
793                      STPO.Unlock_RTS;
794                   end if;
795                else
796                   Queuing.Enqueue
797                     (New_Object.Entry_Queues (E), Entry_Call);
798                   Update_For_Queue_To_PO (Entry_Call, With_Abort);
799                end if;
800
801             else
802                PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call, With_Abort);
803             end if;
804          end if;
805       end if;
806    end Requeue_Call;
807
808    ----------------------------
809    -- Protected_Entry_Caller --
810    ----------------------------
811
812    function Protected_Entry_Caller
813      (Object : Protection_Entries'Class) return Task_Id is
814    begin
815       return Object.Call_In_Progress.Self;
816    end Protected_Entry_Caller;
817
818    -----------------------------
819    -- Requeue_Protected_Entry --
820    -----------------------------
821
822    --  Compiler interface only.  Do not call from within the RTS.
823
824    --  entry e when b is
825    --  begin
826    --     b := false;
827    --     ...A...
828    --     requeue e2;
829    --  end e;
830
831    --  procedure rPT__E10b (O : address; P : address; E :
832    --    protected_entry_index) is
833    --     type rTVP is access rTV;
834    --     freeze rTVP []
835    --     _object : rTVP := rTVP!(O);
836    --  begin
837    --     declare
838    --        rR : protection renames _object._object;
839    --        vP : integer renames _object.v;
840    --        bP : boolean renames _object.b;
841    --     begin
842    --        b := false;
843    --        ...A...
844    --        requeue_protected_entry (rR'unchecked_access, rR'
845    --          unchecked_access, 2, false, objectF => 0, new_objectF =>
846    --          0);
847    --        return;
848    --     end;
849    --     complete_entry_body (_object._object'unchecked_access, objectF =>
850    --       0);
851    --     return;
852    --  exception
853    --     when others =>
854    --        abort_undefer.all;
855    --        exceptional_complete_entry_body (_object._object'
856    --          unchecked_access, current_exception, objectF => 0);
857    --        return;
858    --  end rPT__E10b;
859
860    procedure Requeue_Protected_Entry
861      (Object     : Protection_Entries_Access;
862       New_Object : Protection_Entries_Access;
863       E          : Protected_Entry_Index;
864       With_Abort : Boolean)
865    is
866       Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
867
868    begin
869       pragma Debug
870         (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
871       pragma Assert (STPO.Self.Deferral_Level > 0);
872
873       Entry_Call.E := Entry_Index (E);
874       Entry_Call.Called_PO := To_Address (New_Object);
875       Entry_Call.Called_Task := null;
876       Entry_Call.Requeue_With_Abort := With_Abort;
877       Object.Call_In_Progress := null;
878    end Requeue_Protected_Entry;
879
880    -------------------------------------
881    -- Requeue_Task_To_Protected_Entry --
882    -------------------------------------
883
884    --  Compiler interface only.
885
886    --    accept e1 do
887    --      ...A...
888    --      requeue r.e2;
889    --    end e1;
890
891    --    A79b : address;
892    --    L78b : label
893    --    begin
894    --       accept_call (1, A79b);
895    --       ...A...
896    --       requeue_task_to_protected_entry (rTV!(r)._object'
897    --         unchecked_access, 2, false, new_objectF => 0);
898    --       goto L78b;
899    --       <<L78b>>
900    --       complete_rendezvous;
901    --    exception
902    --       when all others =>
903    --          exceptional_complete_rendezvous (get_gnat_exception);
904    --    end;
905
906    procedure Requeue_Task_To_Protected_Entry
907      (New_Object : Protection_Entries_Access;
908       E          : Protected_Entry_Index;
909       With_Abort : Boolean)
910    is
911       Self_ID    : constant Task_Id := STPO.Self;
912       Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
913
914    begin
915       Initialization.Defer_Abort (Self_ID);
916
917       --  We do not need to lock Self_ID here since the call is not abortable
918       --  at this point, and therefore, the caller cannot cancel the call.
919
920       Entry_Call.Needs_Requeue := True;
921       Entry_Call.Requeue_With_Abort := With_Abort;
922       Entry_Call.Called_PO := To_Address (New_Object);
923       Entry_Call.Called_Task := null;
924       Entry_Call.E := Entry_Index (E);
925       Initialization.Undefer_Abort (Self_ID);
926    end Requeue_Task_To_Protected_Entry;
927
928    ---------------------
929    -- Service_Entries --
930    ---------------------
931
932    procedure Service_Entries (Object : Protection_Entries_Access) is
933       Self_ID : constant Task_Id := STPO.Self;
934    begin
935       PO_Service_Entries (Self_ID, Object);
936    end Service_Entries;
937
938    --------------------------------
939    -- Timed_Protected_Entry_Call --
940    --------------------------------
941
942    --  Compiler interface only.  Do not call from within the RTS.
943
944    procedure Timed_Protected_Entry_Call
945      (Object                : Protection_Entries_Access;
946       E                     : Protected_Entry_Index;
947       Uninterpreted_Data    : System.Address;
948       Timeout               : Duration;
949       Mode                  : Delay_Modes;
950       Entry_Call_Successful : out Boolean)
951    is
952       Self_Id           : constant Task_Id  := STPO.Self;
953       Entry_Call        : Entry_Call_Link;
954       Ceiling_Violation : Boolean;
955       Yielded           : Boolean;
956
957    begin
958       if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
959          Raise_Exception (Storage_Error'Identity,
960            "not enough ATC nesting levels");
961       end if;
962
963       --  If pragma Detect_Blocking is active then Program_Error must be
964       --  raised if this potentially blocking operation is called from a
965       --  protected action.
966
967       if Detect_Blocking
968         and then Self_Id.Common.Protected_Action_Nesting > 0
969       then
970          Ada.Exceptions.Raise_Exception
971            (Program_Error'Identity, "potentially blocking operation");
972       end if;
973
974       if Runtime_Traces then
975          Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
976       end if;
977
978       Initialization.Defer_Abort (Self_Id);
979       Lock_Entries (Object, Ceiling_Violation);
980
981       if Ceiling_Violation then
982          Initialization.Undefer_Abort (Self_Id);
983          raise Program_Error;
984       end if;
985
986       Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
987       pragma Debug
988         (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
989          ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
990       Entry_Call :=
991         Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
992       Entry_Call.Next := null;
993       Entry_Call.Mode := Timed_Call;
994       Entry_Call.Cancellation_Attempted := False;
995
996       if Self_Id.Deferral_Level > 1 then
997          Entry_Call.State := Never_Abortable;
998       else
999          Entry_Call.State := Now_Abortable;
1000       end if;
1001
1002       Entry_Call.E := Entry_Index (E);
1003       Entry_Call.Prio := STPO.Get_Priority (Self_Id);
1004       Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
1005       Entry_Call.Called_PO := To_Address (Object);
1006       Entry_Call.Called_Task := null;
1007       Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
1008
1009       PO_Do_Or_Queue (Self_Id, Object, Entry_Call, With_Abort => True);
1010       PO_Service_Entries (Self_Id, Object);
1011
1012       if Single_Lock then
1013          STPO.Lock_RTS;
1014       else
1015          STPO.Write_Lock (Self_Id);
1016       end if;
1017
1018       --  Try to avoid waiting for completed or cancelled calls.
1019
1020       if Entry_Call.State >= Done then
1021          Utilities.Exit_One_ATC_Level (Self_Id);
1022
1023          if Single_Lock then
1024             STPO.Unlock_RTS;
1025          else
1026             STPO.Unlock (Self_Id);
1027          end if;
1028
1029          Entry_Call_Successful := Entry_Call.State = Done;
1030          Initialization.Undefer_Abort (Self_Id);
1031          Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1032          return;
1033       end if;
1034
1035       Entry_Calls.Wait_For_Completion_With_Timeout
1036         (Entry_Call, Timeout, Mode, Yielded);
1037
1038       if Single_Lock then
1039          STPO.Unlock_RTS;
1040       else
1041          STPO.Unlock (Self_Id);
1042       end if;
1043
1044       --  ??? Do we need to yield in case Yielded is False
1045
1046       Initialization.Undefer_Abort (Self_Id);
1047       Entry_Call_Successful := Entry_Call.State = Done;
1048       Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1049    end Timed_Protected_Entry_Call;
1050
1051    ----------------------------
1052    -- Update_For_Queue_To_PO --
1053    ----------------------------
1054
1055    --  Update the state of an existing entry call, based on
1056    --  whether the current queuing action is with or without abort.
1057    --  Call this only while holding the server's lock.
1058    --  It returns with the server's lock released.
1059
1060    New_State : constant array (Boolean, Entry_Call_State)
1061      of Entry_Call_State :=
1062        (True =>
1063          (Never_Abortable   => Never_Abortable,
1064           Not_Yet_Abortable => Now_Abortable,
1065           Was_Abortable     => Now_Abortable,
1066           Now_Abortable     => Now_Abortable,
1067           Done              => Done,
1068           Cancelled         => Cancelled),
1069         False =>
1070          (Never_Abortable   => Never_Abortable,
1071           Not_Yet_Abortable => Not_Yet_Abortable,
1072           Was_Abortable     => Was_Abortable,
1073           Now_Abortable     => Now_Abortable,
1074           Done              => Done,
1075           Cancelled         => Cancelled)
1076        );
1077
1078    procedure Update_For_Queue_To_PO
1079      (Entry_Call : Entry_Call_Link;
1080       With_Abort : Boolean)
1081    is
1082       Old : constant Entry_Call_State := Entry_Call.State;
1083
1084    begin
1085       pragma Assert (Old < Done);
1086
1087       Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1088
1089       if Entry_Call.Mode = Asynchronous_Call then
1090          if Old < Was_Abortable and then
1091            Entry_Call.State = Now_Abortable
1092          then
1093             if Single_Lock then
1094                STPO.Lock_RTS;
1095             end if;
1096
1097             STPO.Write_Lock (Entry_Call.Self);
1098
1099             if Entry_Call.Self.Common.State = Async_Select_Sleep then
1100                STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1101             end if;
1102
1103             STPO.Unlock (Entry_Call.Self);
1104
1105             if Single_Lock then
1106                STPO.Unlock_RTS;
1107             end if;
1108
1109          end if;
1110
1111       elsif Entry_Call.Mode = Conditional_Call then
1112          pragma Assert (Entry_Call.State < Was_Abortable);
1113          null;
1114       end if;
1115    end Update_For_Queue_To_PO;
1116
1117 end System.Tasking.Protected_Objects.Operations;