OSDN Git Service

Minor reformatting.
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-tasren.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 . R E N D E Z V O U S             --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --         Copyright (C) 1992-2007, Free Software Foundation, Inc.          --
10 --                                                                          --
11 -- GNARL is free software; you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNARL; see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNARL was developed by the GNARL team at Florida State University.       --
30 -- Extensive contributions were provided by Ada Core Technologies, Inc.     --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with System.Task_Primitives.Operations;
35 --  used for Get_Priority
36 --           Set_Priority
37 --           Write_Lock
38 --           Unlock
39 --           Sleep
40 --           Wakeup
41 --           Timed_Sleep
42
43 with System.Tasking.Entry_Calls;
44 --  Used for Wait_For_Completion
45 --           Wait_For_Completion_With_Timeout
46 --           Wait_Until_Abortable
47
48 with System.Tasking.Initialization;
49 --  used for Defer_Abort
50 --           Undefer_Abort
51 --           Do_Pending_Action
52
53 with System.Tasking.Queuing;
54 --  used for Enqueue
55 --           Dequeue_Head
56 --           Select_Task_Entry_Call
57 --           Count_Waiting
58
59 with System.Tasking.Utilities;
60 --  used for Check_Exception
61 --           Make_Passive
62 --           Wakeup_Entry_Caller
63 --           Exit_One_ATC_Level
64
65 with System.Tasking.Protected_Objects.Operations;
66 --  used for PO_Do_Or_Queue
67 --           PO_Service_Entries
68 --           Lock_Entries
69
70 with System.Tasking.Debug;
71 --  used for Trace
72
73 with System.Restrictions;
74 --  used for Abort_Allowed
75
76 with System.Parameters;
77 --  used for Single_Lock
78 --           Runtime_Traces
79
80 with System.Traces.Tasking;
81 --  used for Send_Trace_Info
82
83 package body System.Tasking.Rendezvous is
84
85    package STPO renames System.Task_Primitives.Operations;
86    package POO renames Protected_Objects.Operations;
87    package POE renames Protected_Objects.Entries;
88
89    use Parameters;
90    use Task_Primitives.Operations;
91    use System.Traces;
92    use System.Traces.Tasking;
93
94    type Select_Treatment is (
95      Accept_Alternative_Selected,   --  alternative with non-null body
96      Accept_Alternative_Completed,  --  alternative with null body
97      Else_Selected,
98      Terminate_Selected,
99      Accept_Alternative_Open,
100      No_Alternative_Open);
101
102    ----------------
103    -- Local Data --
104    ----------------
105
106    Default_Treatment : constant array (Select_Modes) of Select_Treatment :=
107      (Simple_Mode         => No_Alternative_Open,
108       Else_Mode           => Else_Selected,
109       Terminate_Mode      => Terminate_Selected,
110       Delay_Mode          => No_Alternative_Open);
111
112    New_State : constant array (Boolean, Entry_Call_State)
113      of Entry_Call_State :=
114        (True =>
115          (Never_Abortable   => Never_Abortable,
116           Not_Yet_Abortable => Now_Abortable,
117           Was_Abortable     => Now_Abortable,
118           Now_Abortable     => Now_Abortable,
119           Done              => Done,
120           Cancelled         => Cancelled),
121         False =>
122          (Never_Abortable   => Never_Abortable,
123           Not_Yet_Abortable => Not_Yet_Abortable,
124           Was_Abortable     => Was_Abortable,
125           Now_Abortable     => Now_Abortable,
126           Done              => Done,
127           Cancelled         => Cancelled)
128        );
129
130    -----------------------
131    -- Local Subprograms --
132    -----------------------
133
134    procedure Local_Defer_Abort (Self_Id : Task_Id) renames
135      System.Tasking.Initialization.Defer_Abort_Nestable;
136
137    procedure Local_Undefer_Abort (Self_Id : Task_Id) renames
138      System.Tasking.Initialization.Undefer_Abort_Nestable;
139
140    --  Florist defers abort around critical sections that
141    --  make entry calls to the Interrupt_Manager task, which
142    --  violates the general rule about top-level runtime system
143    --  calls from abort-deferred regions.  It is not that this is
144    --  unsafe, but when it occurs in "normal" programs it usually
145    --  means either the user is trying to do a potentially blocking
146    --  operation from within a protected object, or there is a
147    --  runtime system/compiler error that has failed to undefer
148    --  an earlier abort deferral. Thus, for debugging it may be
149    --  wise to modify the above renamings to the non-nestable forms.
150
151    procedure Boost_Priority (Call : Entry_Call_Link; Acceptor : Task_Id);
152    pragma Inline (Boost_Priority);
153    --  Call this only with abort deferred and holding lock of Acceptor
154
155    procedure Call_Synchronous
156      (Acceptor              : Task_Id;
157       E                     : Task_Entry_Index;
158       Uninterpreted_Data    : System.Address;
159       Mode                  : Call_Modes;
160       Rendezvous_Successful : out Boolean);
161    pragma Inline (Call_Synchronous);
162    --  This call is used to make a simple or conditional entry call.
163    --  Called from Call_Simple and Task_Entry_Call.
164
165    procedure Setup_For_Rendezvous_With_Body
166      (Entry_Call : Entry_Call_Link;
167       Acceptor   : Task_Id);
168    pragma Inline (Setup_For_Rendezvous_With_Body);
169    --  Call this only with abort deferred and holding lock of Acceptor.
170    --  When a rendezvous selected (ready for rendezvous) we need to save
171    --  previous caller and adjust the priority. Also we need to make
172    --  this call not Abortable (Cancellable) since the rendezvous has
173    --  already been started.
174
175    procedure Wait_For_Call (Self_Id : Task_Id);
176    pragma Inline (Wait_For_Call);
177    --  Call this only with abort deferred and holding lock of Self_Id.
178    --  An accepting task goes into Sleep by calling this routine
179    --  waiting for a call from the caller or waiting for an abort.
180    --  Make sure Self_Id is locked before calling this routine.
181
182    -----------------
183    -- Accept_Call --
184    -----------------
185
186    procedure Accept_Call
187      (E                  : Task_Entry_Index;
188       Uninterpreted_Data : out System.Address)
189    is
190       Self_Id      : constant Task_Id := STPO.Self;
191       Caller       : Task_Id := null;
192       Open_Accepts : aliased Accept_List (1 .. 1);
193       Entry_Call   : Entry_Call_Link;
194
195    begin
196       Initialization.Defer_Abort (Self_Id);
197
198       if Single_Lock then
199          Lock_RTS;
200       end if;
201
202       STPO.Write_Lock (Self_Id);
203
204       if not Self_Id.Callable then
205          pragma Assert (Self_Id.Pending_ATC_Level = 0);
206
207          pragma Assert (Self_Id.Pending_Action);
208
209          STPO.Unlock (Self_Id);
210
211          if Single_Lock then
212             Unlock_RTS;
213          end if;
214
215          Initialization.Undefer_Abort (Self_Id);
216
217          --  Should never get here ???
218
219          pragma Assert (False);
220          raise Standard'Abort_Signal;
221       end if;
222
223       Queuing.Dequeue_Head (Self_Id.Entry_Queues (E), Entry_Call);
224
225       if Entry_Call /= null then
226          Caller := Entry_Call.Self;
227          Setup_For_Rendezvous_With_Body (Entry_Call, Self_Id);
228          Uninterpreted_Data := Entry_Call.Uninterpreted_Data;
229
230       else
231          --  Wait for a caller
232
233          Open_Accepts (1).Null_Body := False;
234          Open_Accepts (1).S := E;
235          Self_Id.Open_Accepts := Open_Accepts'Unrestricted_Access;
236
237          --  Wait for normal call
238
239          if Parameters.Runtime_Traces then
240             Send_Trace_Info (W_Accept, Self_Id, Integer (Open_Accepts'Length));
241          end if;
242
243          pragma Debug
244            (Debug.Trace (Self_Id, "Accept_Call: wait", 'R'));
245          Wait_For_Call (Self_Id);
246
247          pragma Assert (Self_Id.Open_Accepts = null);
248
249          if Self_Id.Common.Call /= null then
250             Caller := Self_Id.Common.Call.Self;
251             Uninterpreted_Data :=
252               Caller.Entry_Calls (Caller.ATC_Nesting_Level).Uninterpreted_Data;
253          else
254             --  Case of an aborted task
255
256             Uninterpreted_Data := System.Null_Address;
257          end if;
258       end if;
259
260       --  Self_Id.Common.Call should already be updated by the Caller
261       --  On return, we will start the rendezvous.
262
263       STPO.Unlock (Self_Id);
264
265       if Single_Lock then
266          Unlock_RTS;
267       end if;
268
269       Initialization.Undefer_Abort (Self_Id);
270
271       if Parameters.Runtime_Traces then
272          Send_Trace_Info (M_Accept_Complete, Caller, Entry_Index (E));
273       end if;
274    end Accept_Call;
275
276    --------------------
277    -- Accept_Trivial --
278    --------------------
279
280    procedure Accept_Trivial (E : Task_Entry_Index) is
281       Self_Id      : constant Task_Id := STPO.Self;
282       Caller       : Task_Id := null;
283       Open_Accepts : aliased Accept_List (1 .. 1);
284       Entry_Call   : Entry_Call_Link;
285
286    begin
287       Initialization.Defer_Abort_Nestable (Self_Id);
288
289       if Single_Lock then
290          Lock_RTS;
291       end if;
292
293       STPO.Write_Lock (Self_Id);
294
295       if not Self_Id.Callable then
296          pragma Assert (Self_Id.Pending_ATC_Level = 0);
297
298          pragma Assert (Self_Id.Pending_Action);
299
300          STPO.Unlock (Self_Id);
301
302          if Single_Lock then
303             Unlock_RTS;
304          end if;
305
306          Initialization.Undefer_Abort_Nestable (Self_Id);
307
308          --  Should never get here ???
309
310          pragma Assert (False);
311          raise Standard'Abort_Signal;
312       end if;
313
314       Queuing.Dequeue_Head (Self_Id.Entry_Queues (E), Entry_Call);
315
316       if Entry_Call = null then
317          --  Need to wait for entry call
318
319          Open_Accepts (1).Null_Body := True;
320          Open_Accepts (1).S := E;
321          Self_Id.Open_Accepts := Open_Accepts'Unrestricted_Access;
322
323          if Parameters.Runtime_Traces then
324             Send_Trace_Info (W_Accept, Self_Id, Integer (Open_Accepts'Length));
325          end if;
326
327          pragma Debug
328           (Debug.Trace (Self_Id, "Accept_Trivial: wait", 'R'));
329
330          Wait_For_Call (Self_Id);
331
332          pragma Assert (Self_Id.Open_Accepts = null);
333
334          --  No need to do anything special here for pending abort.
335          --  Abort_Signal will be raised by Undefer on exit.
336
337          STPO.Unlock (Self_Id);
338
339       else  --  found caller already waiting
340          pragma Assert (Entry_Call.State < Done);
341
342          STPO.Unlock (Self_Id);
343          Caller := Entry_Call.Self;
344
345          STPO.Write_Lock (Caller);
346          Initialization.Wakeup_Entry_Caller (Self_Id, Entry_Call, Done);
347          STPO.Unlock (Caller);
348       end if;
349
350       if Parameters.Runtime_Traces then
351          Send_Trace_Info (M_Accept_Complete);
352
353          --  Fake one, since there is (???) no way
354          --  to know that the rendezvous is over
355
356          Send_Trace_Info (M_RDV_Complete);
357       end if;
358
359       if Single_Lock then
360          Unlock_RTS;
361       end if;
362
363       Initialization.Undefer_Abort_Nestable (Self_Id);
364    end Accept_Trivial;
365
366    --------------------
367    -- Boost_Priority --
368    --------------------
369
370    procedure Boost_Priority (Call : Entry_Call_Link; Acceptor : Task_Id) is
371       Caller        : constant Task_Id := Call.Self;
372       Caller_Prio   : constant System.Any_Priority := Get_Priority (Caller);
373       Acceptor_Prio : constant System.Any_Priority := Get_Priority (Acceptor);
374
375    begin
376       if Caller_Prio > Acceptor_Prio then
377          Call.Acceptor_Prev_Priority := Acceptor_Prio;
378          Set_Priority (Acceptor, Caller_Prio);
379
380       else
381          Call.Acceptor_Prev_Priority := Priority_Not_Boosted;
382       end if;
383    end Boost_Priority;
384
385    -----------------
386    -- Call_Simple --
387    -----------------
388
389    procedure Call_Simple
390      (Acceptor           : Task_Id;
391       E                  : Task_Entry_Index;
392       Uninterpreted_Data : System.Address)
393    is
394       Rendezvous_Successful : Boolean;
395
396    begin
397       --  If pragma Detect_Blocking is active then Program_Error must be
398       --  raised if this potentially blocking operation is called from a
399       --  protected action.
400
401       if System.Tasking.Detect_Blocking
402         and then STPO.Self.Common.Protected_Action_Nesting > 0
403       then
404          Ada.Exceptions.Raise_Exception
405            (Program_Error'Identity, "potentially blocking operation");
406       end if;
407
408       Call_Synchronous
409         (Acceptor, E, Uninterpreted_Data, Simple_Call, Rendezvous_Successful);
410    end Call_Simple;
411
412    ----------------------
413    -- Call_Synchronous --
414    ----------------------
415
416    procedure Call_Synchronous
417      (Acceptor              : Task_Id;
418       E                     : Task_Entry_Index;
419       Uninterpreted_Data    : System.Address;
420       Mode                  : Call_Modes;
421       Rendezvous_Successful : out Boolean)
422    is
423       Self_Id    : constant Task_Id := STPO.Self;
424       Level      : ATC_Level;
425       Entry_Call : Entry_Call_Link;
426
427    begin
428       pragma Assert (Mode /= Asynchronous_Call);
429
430       Local_Defer_Abort (Self_Id);
431       Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
432       pragma Debug
433         (Debug.Trace (Self_Id, "CS: entered ATC level: " &
434          ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
435       Level := Self_Id.ATC_Nesting_Level;
436       Entry_Call := Self_Id.Entry_Calls (Level)'Access;
437       Entry_Call.Next := null;
438       Entry_Call.Mode := Mode;
439       Entry_Call.Cancellation_Attempted := False;
440
441       if Parameters.Runtime_Traces then
442          Send_Trace_Info (W_Call, Acceptor, Entry_Index (E));
443       end if;
444
445       --  If this is a call made inside of an abort deferred region,
446       --  the call should be never abortable.
447
448       if Self_Id.Deferral_Level > 1 then
449          Entry_Call.State := Never_Abortable;
450       else
451          Entry_Call.State := Now_Abortable;
452       end if;
453
454       Entry_Call.E := Entry_Index (E);
455       Entry_Call.Prio := Get_Priority (Self_Id);
456       Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
457       Entry_Call.Called_Task := Acceptor;
458       Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
459       Entry_Call.With_Abort := True;
460
461       --  Note: the caller will undefer abort on return (see WARNING above)
462
463       if Single_Lock then
464          Lock_RTS;
465       end if;
466
467       if not Task_Do_Or_Queue (Self_Id, Entry_Call) then
468          STPO.Write_Lock (Self_Id);
469          Utilities.Exit_One_ATC_Level (Self_Id);
470          STPO.Unlock (Self_Id);
471
472          if Single_Lock then
473             Unlock_RTS;
474          end if;
475
476          if Parameters.Runtime_Traces then
477             Send_Trace_Info (E_Missed, Acceptor);
478          end if;
479
480          Local_Undefer_Abort (Self_Id);
481          raise Tasking_Error;
482       end if;
483
484       STPO.Write_Lock (Self_Id);
485       pragma Debug
486         (Debug.Trace (Self_Id, "Call_Synchronous: wait", 'R'));
487       Entry_Calls.Wait_For_Completion (Entry_Call);
488       pragma Debug
489         (Debug.Trace (Self_Id, "Call_Synchronous: done waiting", 'R'));
490       Rendezvous_Successful := Entry_Call.State = Done;
491       STPO.Unlock (Self_Id);
492
493       if Single_Lock then
494          Unlock_RTS;
495       end if;
496
497       Local_Undefer_Abort (Self_Id);
498       Entry_Calls.Check_Exception (Self_Id, Entry_Call);
499    end Call_Synchronous;
500
501    --------------
502    -- Callable --
503    --------------
504
505    function Callable (T : Task_Id) return Boolean is
506       Result  : Boolean;
507       Self_Id : constant Task_Id := STPO.Self;
508
509    begin
510       Initialization.Defer_Abort_Nestable (Self_Id);
511
512       if Single_Lock then
513          Lock_RTS;
514       end if;
515
516       STPO.Write_Lock (T);
517       Result := T.Callable;
518       STPO.Unlock (T);
519
520       if Single_Lock then
521          Unlock_RTS;
522       end if;
523
524       Initialization.Undefer_Abort_Nestable (Self_Id);
525       return Result;
526    end Callable;
527
528    ----------------------------
529    -- Cancel_Task_Entry_Call --
530    ----------------------------
531
532    procedure Cancel_Task_Entry_Call (Cancelled : out Boolean) is
533    begin
534       Entry_Calls.Try_To_Cancel_Entry_Call (Cancelled);
535    end Cancel_Task_Entry_Call;
536
537    -------------------------
538    -- Complete_Rendezvous --
539    -------------------------
540
541    procedure Complete_Rendezvous is
542    begin
543       Exceptional_Complete_Rendezvous (Ada.Exceptions.Null_Id);
544    end Complete_Rendezvous;
545
546    -------------------------------------
547    -- Exceptional_Complete_Rendezvous --
548    -------------------------------------
549
550    procedure Exceptional_Complete_Rendezvous
551      (Ex : Ada.Exceptions.Exception_Id)
552    is
553       Self_Id                : constant Task_Id := STPO.Self;
554       Entry_Call             : Entry_Call_Link := Self_Id.Common.Call;
555       Caller                 : Task_Id;
556       Called_PO              : STPE.Protection_Entries_Access;
557       Acceptor_Prev_Priority : Integer;
558
559       Exception_To_Raise : Ada.Exceptions.Exception_Id := Ex;
560       Ceiling_Violation  : Boolean;
561
562       use type Ada.Exceptions.Exception_Id;
563       procedure Internal_Reraise;
564       pragma Import (C, Internal_Reraise, "__gnat_reraise");
565
566       procedure Transfer_Occurrence
567         (Target : Ada.Exceptions.Exception_Occurrence_Access;
568          Source : Ada.Exceptions.Exception_Occurrence);
569       pragma Import (C, Transfer_Occurrence, "__gnat_transfer_occurrence");
570
571       use type STPE.Protection_Entries_Access;
572
573    begin
574       --  Consider phasing out Complete_Rendezvous in favor
575       --  of direct call to this with Ada.Exceptions.Null_ID.
576       --  See code expansion examples for Accept_Call and Selective_Wait.
577       --  Also consider putting an explicit re-raise after this call, in
578       --  the generated code. That way we could eliminate the
579       --  code here that reraises the exception.
580
581       --  The deferral level is critical here,
582       --  since we want to raise an exception or allow abort to take
583       --  place, if there is an exception or abort pending.
584
585       pragma Debug
586        (Debug.Trace (Self_Id, "Exceptional_Complete_Rendezvous", 'R'));
587
588       if Ex = Ada.Exceptions.Null_Id then
589          --  The call came from normal end-of-rendezvous,
590          --  so abort is not yet deferred.
591
592          if Parameters.Runtime_Traces then
593             Send_Trace_Info (M_RDV_Complete, Entry_Call.Self);
594          end if;
595
596          Initialization.Defer_Abort_Nestable (Self_Id);
597       end if;
598
599       --  We need to clean up any accepts which Self may have
600       --  been serving when it was aborted.
601
602       if Ex = Standard'Abort_Signal'Identity then
603          if Single_Lock then
604             Lock_RTS;
605          end if;
606
607          while Entry_Call /= null loop
608             Entry_Call.Exception_To_Raise := Tasking_Error'Identity;
609
610             --  All forms of accept make sure that the acceptor is not
611             --  completed, before accepting further calls, so that we
612             --  can be sure that no further calls are made after the
613             --  current calls are purged.
614
615             Caller := Entry_Call.Self;
616
617             --  Take write lock. This follows the lock precedence rule that
618             --  Caller may be locked while holding lock of Acceptor.
619             --  Complete the call abnormally, with exception.
620
621             STPO.Write_Lock (Caller);
622             Initialization.Wakeup_Entry_Caller (Self_Id, Entry_Call, Done);
623             STPO.Unlock (Caller);
624             Entry_Call := Entry_Call.Acceptor_Prev_Call;
625          end loop;
626
627          if Single_Lock then
628             Unlock_RTS;
629          end if;
630
631       else
632          Caller := Entry_Call.Self;
633
634          if Entry_Call.Needs_Requeue then
635             --  We dare not lock Self_Id at the same time as Caller,
636             --  for fear of deadlock.
637
638             Entry_Call.Needs_Requeue := False;
639             Self_Id.Common.Call := Entry_Call.Acceptor_Prev_Call;
640
641             if Entry_Call.Called_Task /= null then
642                --  Requeue to another task entry
643
644                if Single_Lock then
645                   Lock_RTS;
646                end if;
647
648                if not Task_Do_Or_Queue (Self_Id, Entry_Call) then
649                   if Single_Lock then
650                      Unlock_RTS;
651                   end if;
652
653                   Initialization.Undefer_Abort (Self_Id);
654                   raise Tasking_Error;
655                end if;
656
657                if Single_Lock then
658                   Unlock_RTS;
659                end if;
660
661             else
662                --  Requeue to a protected entry
663
664                Called_PO := POE.To_Protection (Entry_Call.Called_PO);
665                STPE.Lock_Entries (Called_PO, Ceiling_Violation);
666
667                if Ceiling_Violation then
668                   pragma Assert (Ex = Ada.Exceptions.Null_Id);
669
670                   Exception_To_Raise := Program_Error'Identity;
671                   Entry_Call.Exception_To_Raise := Exception_To_Raise;
672
673                   if Single_Lock then
674                      Lock_RTS;
675                   end if;
676
677                   STPO.Write_Lock (Caller);
678                   Initialization.Wakeup_Entry_Caller
679                     (Self_Id, Entry_Call, Done);
680                   STPO.Unlock (Caller);
681
682                   if Single_Lock then
683                      Unlock_RTS;
684                   end if;
685
686                else
687                   POO.PO_Do_Or_Queue (Self_Id, Called_PO, Entry_Call);
688                   POO.PO_Service_Entries (Self_Id, Called_PO);
689                end if;
690             end if;
691
692             Entry_Calls.Reset_Priority
693               (Self_Id, Entry_Call.Acceptor_Prev_Priority);
694
695          else
696             --  The call does not need to be requeued
697
698             Self_Id.Common.Call := Entry_Call.Acceptor_Prev_Call;
699             Entry_Call.Exception_To_Raise := Ex;
700
701             if Single_Lock then
702                Lock_RTS;
703             end if;
704
705             STPO.Write_Lock (Caller);
706
707             --  Done with Caller locked to make sure that Wakeup is not lost
708
709             if Ex /= Ada.Exceptions.Null_Id then
710                Transfer_Occurrence
711                  (Caller.Common.Compiler_Data.Current_Excep'Access,
712                   Self_Id.Common.Compiler_Data.Current_Excep);
713             end if;
714
715             Acceptor_Prev_Priority := Entry_Call.Acceptor_Prev_Priority;
716             Initialization.Wakeup_Entry_Caller (Self_Id, Entry_Call, Done);
717
718             STPO.Unlock (Caller);
719
720             if Single_Lock then
721                Unlock_RTS;
722             end if;
723
724             Entry_Calls.Reset_Priority (Self_Id, Acceptor_Prev_Priority);
725          end if;
726       end if;
727
728       Initialization.Undefer_Abort (Self_Id);
729
730       if Exception_To_Raise /= Ada.Exceptions.Null_Id then
731          Internal_Reraise;
732       end if;
733
734       --  ??? Do we need to give precedence to Program_Error that might be
735       --  raised due to failure of finalization, over Tasking_Error from
736       --  failure of requeue?
737    end Exceptional_Complete_Rendezvous;
738
739    -------------------------------------
740    -- Requeue_Protected_To_Task_Entry --
741    -------------------------------------
742
743    procedure Requeue_Protected_To_Task_Entry
744      (Object     : STPE.Protection_Entries_Access;
745       Acceptor   : Task_Id;
746       E          : Task_Entry_Index;
747       With_Abort : Boolean)
748    is
749       Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
750    begin
751       pragma Assert (STPO.Self.Deferral_Level > 0);
752
753       Entry_Call.E := Entry_Index (E);
754       Entry_Call.Called_Task := Acceptor;
755       Entry_Call.Called_PO := Null_Address;
756       Entry_Call.With_Abort := With_Abort;
757       Object.Call_In_Progress := null;
758    end Requeue_Protected_To_Task_Entry;
759
760    ------------------------
761    -- Requeue_Task_Entry --
762    ------------------------
763
764    procedure Requeue_Task_Entry
765      (Acceptor   : Task_Id;
766       E          : Task_Entry_Index;
767       With_Abort : Boolean)
768    is
769       Self_Id    : constant Task_Id := STPO.Self;
770       Entry_Call : constant Entry_Call_Link := Self_Id.Common.Call;
771
772    begin
773       Initialization.Defer_Abort (Self_Id);
774       Entry_Call.Needs_Requeue := True;
775       Entry_Call.With_Abort := With_Abort;
776       Entry_Call.E := Entry_Index (E);
777       Entry_Call.Called_Task := Acceptor;
778       Initialization.Undefer_Abort (Self_Id);
779    end Requeue_Task_Entry;
780
781    --------------------
782    -- Selective_Wait --
783    --------------------
784
785    procedure Selective_Wait
786      (Open_Accepts       : Accept_List_Access;
787       Select_Mode        : Select_Modes;
788       Uninterpreted_Data : out System.Address;
789       Index              : out Select_Index)
790    is
791       Self_Id          : constant Task_Id := STPO.Self;
792       Entry_Call       : Entry_Call_Link;
793       Treatment        : Select_Treatment;
794       Caller           : Task_Id;
795       Selection        : Select_Index;
796       Open_Alternative : Boolean;
797
798    begin
799       Initialization.Defer_Abort (Self_Id);
800
801       if Single_Lock then
802          Lock_RTS;
803       end if;
804
805       STPO.Write_Lock (Self_Id);
806
807       if not Self_Id.Callable then
808          pragma Assert (Self_Id.Pending_ATC_Level = 0);
809
810          pragma Assert (Self_Id.Pending_Action);
811
812          STPO.Unlock (Self_Id);
813
814          if Single_Lock then
815             Unlock_RTS;
816          end if;
817
818          --  ??? In some cases abort is deferred more than once. Need to
819          --  figure out why this happens.
820
821          if Self_Id.Deferral_Level > 1 then
822             Self_Id.Deferral_Level := 1;
823          end if;
824
825          Initialization.Undefer_Abort (Self_Id);
826
827          --  Should never get here ???
828
829          pragma Assert (False);
830          raise Standard'Abort_Signal;
831       end if;
832
833       pragma Assert (Open_Accepts /= null);
834
835       Uninterpreted_Data := Null_Address;
836
837       Queuing.Select_Task_Entry_Call
838         (Self_Id, Open_Accepts, Entry_Call, Selection, Open_Alternative);
839
840       --  Determine the kind and disposition of the select
841
842       Treatment := Default_Treatment (Select_Mode);
843       Self_Id.Chosen_Index := No_Rendezvous;
844
845       if Open_Alternative then
846          if Entry_Call /= null then
847             if Open_Accepts (Selection).Null_Body then
848                Treatment := Accept_Alternative_Completed;
849             else
850                Setup_For_Rendezvous_With_Body (Entry_Call, Self_Id);
851                Treatment := Accept_Alternative_Selected;
852             end if;
853
854             Self_Id.Chosen_Index := Selection;
855
856          elsif Treatment = No_Alternative_Open then
857             Treatment := Accept_Alternative_Open;
858          end if;
859       end if;
860
861       --  Handle the select according to the disposition selected above
862
863       case Treatment is
864          when Accept_Alternative_Selected =>
865             --  Ready to rendezvous
866
867             Uninterpreted_Data := Self_Id.Common.Call.Uninterpreted_Data;
868
869             --  In this case the accept body is not Null_Body. Defer abort
870             --  until it gets into the accept body.
871
872             pragma Assert (Self_Id.Deferral_Level = 1);
873
874             Initialization.Defer_Abort_Nestable (Self_Id);
875             STPO.Unlock (Self_Id);
876
877          when Accept_Alternative_Completed =>
878
879             --  Accept body is null, so rendezvous is over immediately
880
881             if Parameters.Runtime_Traces then
882                Send_Trace_Info (M_RDV_Complete, Entry_Call.Self);
883             end if;
884
885             STPO.Unlock (Self_Id);
886             Caller := Entry_Call.Self;
887
888             STPO.Write_Lock (Caller);
889             Initialization.Wakeup_Entry_Caller (Self_Id, Entry_Call, Done);
890             STPO.Unlock (Caller);
891
892          when Accept_Alternative_Open =>
893
894             --  Wait for caller
895
896             Self_Id.Open_Accepts := Open_Accepts;
897             pragma Debug
898               (Debug.Trace (Self_Id, "Selective_Wait: wait", 'R'));
899
900             if Parameters.Runtime_Traces then
901                Send_Trace_Info (W_Select, Self_Id,
902                                 Integer (Open_Accepts'Length));
903             end if;
904
905             Wait_For_Call (Self_Id);
906
907             pragma Assert (Self_Id.Open_Accepts = null);
908
909             --  Self_Id.Common.Call should already be updated by the Caller if
910             --  not aborted. It might also be ready to do rendezvous even if
911             --  this wakes up due to an abort. Therefore, if the call is not
912             --  empty we need to do the rendezvous if the accept body is not
913             --  Null_Body.
914
915             --  Aren't the first two conditions below redundant???
916
917             if Self_Id.Chosen_Index /= No_Rendezvous
918               and then Self_Id.Common.Call /= null
919               and then not Open_Accepts (Self_Id.Chosen_Index).Null_Body
920             then
921                Uninterpreted_Data := Self_Id.Common.Call.Uninterpreted_Data;
922
923                pragma Assert
924                  (Self_Id.Deferral_Level = 1
925                    or else
926                      (Self_Id.Deferral_Level = 0
927                        and then not Restrictions.Abort_Allowed));
928
929                Initialization.Defer_Abort_Nestable (Self_Id);
930
931                --  Leave abort deferred until the accept body
932             end if;
933
934             STPO.Unlock (Self_Id);
935
936          when Else_Selected =>
937             pragma Assert (Self_Id.Open_Accepts = null);
938
939             if Parameters.Runtime_Traces then
940                Send_Trace_Info (M_Select_Else);
941             end if;
942
943             STPO.Unlock (Self_Id);
944
945          when Terminate_Selected =>
946             --  Terminate alternative is open
947
948             Self_Id.Open_Accepts := Open_Accepts;
949             Self_Id.Common.State := Acceptor_Sleep;
950
951             --  Notify ancestors that this task is on a terminate alternative
952
953             STPO.Unlock (Self_Id);
954             Utilities.Make_Passive (Self_Id, Task_Completed => False);
955             STPO.Write_Lock (Self_Id);
956
957             --  Wait for normal entry call or termination
958
959             Wait_For_Call (Self_Id);
960
961             pragma Assert (Self_Id.Open_Accepts = null);
962
963             if Self_Id.Terminate_Alternative then
964                --  An entry call should have reset this to False,
965                --  so we must be aborted.
966                --  We cannot be in an async. select, since that
967                --  is not legal, so the abort must be of the entire
968                --  task.  Therefore, we do not need to cancel the
969                --  terminate alternative.  The cleanup will be done
970                --  in Complete_Master.
971
972                pragma Assert (Self_Id.Pending_ATC_Level = 0);
973                pragma Assert (Self_Id.Awake_Count = 0);
974
975                STPO.Unlock (Self_Id);
976
977                if Single_Lock then
978                   Unlock_RTS;
979                end if;
980
981                Index := Self_Id.Chosen_Index;
982                Initialization.Undefer_Abort_Nestable (Self_Id);
983
984                if Self_Id.Pending_Action then
985                   Initialization.Do_Pending_Action (Self_Id);
986                end if;
987
988                return;
989
990             else
991                --  Self_Id.Common.Call and Self_Id.Chosen_Index
992                --  should already be updated by the Caller.
993
994                if Self_Id.Chosen_Index /= No_Rendezvous
995                  and then not Open_Accepts (Self_Id.Chosen_Index).Null_Body
996                then
997                   Uninterpreted_Data := Self_Id.Common.Call.Uninterpreted_Data;
998
999                   pragma Assert (Self_Id.Deferral_Level = 1);
1000
1001                   --  We need an extra defer here, to keep abort
1002                   --  deferred until we get into the accept body
1003
1004                   Initialization.Defer_Abort_Nestable (Self_Id);
1005                end if;
1006             end if;
1007
1008             STPO.Unlock (Self_Id);
1009
1010          when No_Alternative_Open =>
1011             --  In this case, Index will be No_Rendezvous on return, which
1012             --  should cause a Program_Error if it is not a Delay_Mode.
1013
1014             --  If delay alternative exists (Delay_Mode) we should suspend
1015             --  until the delay expires.
1016
1017             Self_Id.Open_Accepts := null;
1018
1019             if Select_Mode = Delay_Mode then
1020                Self_Id.Common.State := Delay_Sleep;
1021
1022                loop
1023                   exit when
1024                     Self_Id.Pending_ATC_Level < Self_Id.ATC_Nesting_Level;
1025                   Sleep (Self_Id, Delay_Sleep);
1026                end loop;
1027
1028                Self_Id.Common.State := Runnable;
1029                STPO.Unlock (Self_Id);
1030
1031             else
1032                STPO.Unlock (Self_Id);
1033
1034                if Single_Lock then
1035                   Unlock_RTS;
1036                end if;
1037
1038                Initialization.Undefer_Abort (Self_Id);
1039                Ada.Exceptions.Raise_Exception
1040                  (Program_Error'Identity, "Entry call not a delay mode");
1041             end if;
1042       end case;
1043
1044       if Single_Lock then
1045          Unlock_RTS;
1046       end if;
1047
1048       --  Caller has been chosen.
1049       --  Self_Id.Common.Call should already be updated by the Caller.
1050       --  Self_Id.Chosen_Index should either be updated by the Caller
1051       --  or by Test_Selective_Wait.
1052       --  On return, we sill start rendezvous unless the accept body is
1053       --  null. In the latter case, we will have already completed the RV.
1054
1055       Index := Self_Id.Chosen_Index;
1056       Initialization.Undefer_Abort_Nestable (Self_Id);
1057    end Selective_Wait;
1058
1059    ------------------------------------
1060    -- Setup_For_Rendezvous_With_Body --
1061    ------------------------------------
1062
1063    procedure Setup_For_Rendezvous_With_Body
1064      (Entry_Call : Entry_Call_Link;
1065       Acceptor   : Task_Id) is
1066    begin
1067       Entry_Call.Acceptor_Prev_Call := Acceptor.Common.Call;
1068       Acceptor.Common.Call := Entry_Call;
1069
1070       if Entry_Call.State = Now_Abortable then
1071          Entry_Call.State := Was_Abortable;
1072       end if;
1073
1074       Boost_Priority (Entry_Call, Acceptor);
1075    end Setup_For_Rendezvous_With_Body;
1076
1077    ----------------
1078    -- Task_Count --
1079    ----------------
1080
1081    function Task_Count (E : Task_Entry_Index) return Natural is
1082       Self_Id      : constant Task_Id := STPO.Self;
1083       Return_Count : Natural;
1084
1085    begin
1086       Initialization.Defer_Abort (Self_Id);
1087
1088       if Single_Lock then
1089          Lock_RTS;
1090       end if;
1091
1092       STPO.Write_Lock (Self_Id);
1093       Return_Count := Queuing.Count_Waiting (Self_Id.Entry_Queues (E));
1094       STPO.Unlock (Self_Id);
1095
1096       if Single_Lock then
1097          Unlock_RTS;
1098       end if;
1099
1100       Initialization.Undefer_Abort (Self_Id);
1101
1102       --  Call Yield to let other tasks get a chance to run as this is a
1103       --  potential dispatching point.
1104
1105       Yield (Do_Yield => False);
1106       return Return_Count;
1107    end Task_Count;
1108
1109    ----------------------
1110    -- Task_Do_Or_Queue --
1111    ----------------------
1112
1113    function Task_Do_Or_Queue
1114      (Self_ID    : Task_Id;
1115       Entry_Call : Entry_Call_Link) return Boolean
1116    is
1117       E             : constant Task_Entry_Index :=
1118                         Task_Entry_Index (Entry_Call.E);
1119       Old_State     : constant Entry_Call_State := Entry_Call.State;
1120       Acceptor      : constant Task_Id := Entry_Call.Called_Task;
1121       Parent        : constant Task_Id := Acceptor.Common.Parent;
1122       Parent_Locked : Boolean := False;
1123       Null_Body     : Boolean;
1124
1125    begin
1126       --  Find out whether Entry_Call can be accepted immediately
1127
1128       --  If the Acceptor is not callable, return False.
1129       --  If the rendezvous can start, initiate it.
1130       --  If the accept-body is trivial, also complete the rendezvous.
1131       --  If the acceptor is not ready, enqueue the call.
1132
1133       --  This should have a special case for Accept_Call and Accept_Trivial,
1134       --  so that we don't have the loop setup overhead, below.
1135
1136       --  The call state Done is used here and elsewhere to include both the
1137       --  case of normal successful completion, and the case of an exception
1138       --  being raised. The difference is that if an exception is raised no one
1139       --  will pay attention to the fact that State = Done. Instead the
1140       --  exception will be raised in Undefer_Abort, and control will skip past
1141       --  the place where we normally would resume from an entry call.
1142
1143       pragma Assert (not Queuing.Onqueue (Entry_Call));
1144
1145       --  We rely that the call is off-queue for protection, that the caller
1146       --  will not exit the Entry_Caller_Sleep, and so will not reuse the call
1147       --  record for another call.
1148       --  We rely on the Caller's lock for call State mod's.
1149
1150       --  We can't lock Acceptor.Parent while holding Acceptor,
1151       --  so lock it in advance if we expect to need to lock it.
1152
1153       if Acceptor.Terminate_Alternative then
1154          STPO.Write_Lock (Parent);
1155          Parent_Locked := True;
1156       end if;
1157
1158       STPO.Write_Lock (Acceptor);
1159
1160       --  If the acceptor is not callable, abort the call and return False
1161
1162       if not Acceptor.Callable then
1163          STPO.Unlock (Acceptor);
1164
1165          if Parent_Locked then
1166             STPO.Unlock (Parent);
1167          end if;
1168
1169          pragma Assert (Entry_Call.State < Done);
1170
1171          --  In case we are not the caller, set up the caller
1172          --  to raise Tasking_Error when it wakes up.
1173
1174          STPO.Write_Lock (Entry_Call.Self);
1175          Entry_Call.Exception_To_Raise := Tasking_Error'Identity;
1176          Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
1177          STPO.Unlock (Entry_Call.Self);
1178
1179          return False;
1180       end if;
1181
1182       --  Try to serve the call immediately
1183
1184       if Acceptor.Open_Accepts /= null then
1185          for J in Acceptor.Open_Accepts'Range loop
1186             if Entry_Call.E = Entry_Index (Acceptor.Open_Accepts (J).S) then
1187
1188                --  Commit acceptor to rendezvous with us
1189
1190                Acceptor.Chosen_Index := J;
1191                Null_Body := Acceptor.Open_Accepts (J).Null_Body;
1192                Acceptor.Open_Accepts := null;
1193
1194                --  Prevent abort while call is being served
1195
1196                if Entry_Call.State = Now_Abortable then
1197                   Entry_Call.State := Was_Abortable;
1198                end if;
1199
1200                if Acceptor.Terminate_Alternative then
1201
1202                   --  Cancel terminate alternative. See matching code in
1203                   --  Selective_Wait and Vulnerable_Complete_Master.
1204
1205                   Acceptor.Terminate_Alternative := False;
1206                   Acceptor.Awake_Count := Acceptor.Awake_Count + 1;
1207
1208                   if Acceptor.Awake_Count = 1 then
1209
1210                      --  Notify parent that acceptor is awake
1211
1212                      pragma Assert (Parent.Awake_Count > 0);
1213
1214                      Parent.Awake_Count := Parent.Awake_Count + 1;
1215
1216                      if Parent.Common.State = Master_Completion_Sleep
1217                        and then Acceptor.Master_of_Task = Parent.Master_Within
1218                      then
1219                         Parent.Common.Wait_Count :=
1220                           Parent.Common.Wait_Count + 1;
1221                      end if;
1222                   end if;
1223                end if;
1224
1225                if Null_Body then
1226
1227                   --  Rendezvous is over immediately
1228
1229                   STPO.Wakeup (Acceptor, Acceptor_Sleep);
1230                   STPO.Unlock (Acceptor);
1231
1232                   if Parent_Locked then
1233                      STPO.Unlock (Parent);
1234                   end if;
1235
1236                   STPO.Write_Lock (Entry_Call.Self);
1237                   Initialization.Wakeup_Entry_Caller
1238                     (Self_ID, Entry_Call, Done);
1239                   STPO.Unlock (Entry_Call.Self);
1240
1241                else
1242                   Setup_For_Rendezvous_With_Body (Entry_Call, Acceptor);
1243
1244                   --  For terminate_alternative, acceptor may not be asleep
1245                   --  yet, so we skip the wakeup
1246
1247                   if Acceptor.Common.State /= Runnable then
1248                      STPO.Wakeup (Acceptor, Acceptor_Sleep);
1249                   end if;
1250
1251                   STPO.Unlock (Acceptor);
1252
1253                   if Parent_Locked then
1254                      STPO.Unlock (Parent);
1255                   end if;
1256                end if;
1257
1258                return True;
1259             end if;
1260          end loop;
1261
1262          --  The acceptor is accepting, but not this entry
1263       end if;
1264
1265       --  If the acceptor was ready to accept this call,
1266       --  we would not have gotten this far, so now we should
1267       --  (re)enqueue the call, if the mode permits that.
1268
1269       if Entry_Call.Mode /= Conditional_Call
1270         or else not Entry_Call.With_Abort
1271       then
1272          --  Timed_Call, Simple_Call, or Asynchronous_Call
1273
1274          Queuing.Enqueue (Acceptor.Entry_Queues (E), Entry_Call);
1275
1276          --  Update abortability of call
1277
1278          pragma Assert (Old_State < Done);
1279
1280          Entry_Call.State :=
1281            New_State (Entry_Call.With_Abort, Entry_Call.State);
1282
1283          STPO.Unlock (Acceptor);
1284
1285          if Parent_Locked then
1286             STPO.Unlock (Parent);
1287          end if;
1288
1289          if Old_State /= Entry_Call.State
1290            and then Entry_Call.State = Now_Abortable
1291            and then Entry_Call.Mode > Simple_Call
1292            and then Entry_Call.Self /= Self_ID
1293
1294          --  Asynchronous_Call or Conditional_Call
1295
1296          then
1297             --  Because of ATCB lock ordering rule
1298
1299             STPO.Write_Lock (Entry_Call.Self);
1300
1301             if Entry_Call.Self.Common.State = Async_Select_Sleep then
1302
1303                --  Caller may not yet have reached wait-point
1304
1305                STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1306             end if;
1307
1308             STPO.Unlock (Entry_Call.Self);
1309          end if;
1310
1311       else
1312          --  Conditional_Call and With_Abort
1313
1314          STPO.Unlock (Acceptor);
1315
1316          if Parent_Locked then
1317             STPO.Unlock (Parent);
1318          end if;
1319
1320          STPO.Write_Lock (Entry_Call.Self);
1321
1322          pragma Assert (Entry_Call.State >= Was_Abortable);
1323
1324          Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
1325          STPO.Unlock (Entry_Call.Self);
1326       end if;
1327
1328       return True;
1329    end Task_Do_Or_Queue;
1330
1331    ---------------------
1332    -- Task_Entry_Call --
1333    ---------------------
1334
1335    procedure Task_Entry_Call
1336      (Acceptor              : Task_Id;
1337       E                     : Task_Entry_Index;
1338       Uninterpreted_Data    : System.Address;
1339       Mode                  : Call_Modes;
1340       Rendezvous_Successful : out Boolean)
1341    is
1342       Self_Id    : constant Task_Id := STPO.Self;
1343       Entry_Call : Entry_Call_Link;
1344
1345    begin
1346       --  If pragma Detect_Blocking is active then Program_Error must be
1347       --  raised if this potentially blocking operation is called from a
1348       --  protected action.
1349
1350       if System.Tasking.Detect_Blocking
1351         and then Self_Id.Common.Protected_Action_Nesting > 0
1352       then
1353          Ada.Exceptions.Raise_Exception
1354            (Program_Error'Identity, "potentially blocking operation");
1355       end if;
1356
1357       if Parameters.Runtime_Traces then
1358          Send_Trace_Info (W_Call, Acceptor, Entry_Index (E));
1359       end if;
1360
1361       if Mode = Simple_Call or else Mode = Conditional_Call then
1362          Call_Synchronous
1363            (Acceptor, E, Uninterpreted_Data, Mode, Rendezvous_Successful);
1364
1365       else
1366          --  This is an asynchronous call
1367
1368          --  Abort must already be deferred by the compiler-generated code.
1369          --  Without this, an abort that occurs between the time that this
1370          --  call is made and the time that the abortable part's cleanup
1371          --  handler is set up might miss the cleanup handler and leave the
1372          --  call pending.
1373
1374          Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
1375          pragma Debug
1376            (Debug.Trace (Self_Id, "TEC: entered ATC level: " &
1377             ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
1378          Entry_Call := Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
1379          Entry_Call.Next := null;
1380          Entry_Call.Mode := Mode;
1381          Entry_Call.Cancellation_Attempted := False;
1382          Entry_Call.State := Not_Yet_Abortable;
1383          Entry_Call.E := Entry_Index (E);
1384          Entry_Call.Prio := Get_Priority (Self_Id);
1385          Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
1386          Entry_Call.Called_Task := Acceptor;
1387          Entry_Call.Called_PO := Null_Address;
1388          Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
1389          Entry_Call.With_Abort := True;
1390
1391          if Single_Lock then
1392             Lock_RTS;
1393          end if;
1394
1395          if not Task_Do_Or_Queue (Self_Id, Entry_Call) then
1396             STPO.Write_Lock (Self_Id);
1397             Utilities.Exit_One_ATC_Level (Self_Id);
1398             STPO.Unlock (Self_Id);
1399
1400             if Single_Lock then
1401                Unlock_RTS;
1402             end if;
1403
1404             Initialization.Undefer_Abort (Self_Id);
1405
1406             if Parameters.Runtime_Traces then
1407                Send_Trace_Info (E_Missed, Acceptor);
1408             end if;
1409
1410             raise Tasking_Error;
1411          end if;
1412
1413          --  The following is special for async. entry calls.
1414          --  If the call was not queued abortably, we need to wait until
1415          --  it is before proceeding with the abortable part.
1416
1417          --  Wait_Until_Abortable can be called unconditionally here,
1418          --  but it is expensive.
1419
1420          if Entry_Call.State < Was_Abortable then
1421             Entry_Calls.Wait_Until_Abortable (Self_Id, Entry_Call);
1422          end if;
1423
1424          if Single_Lock then
1425             Unlock_RTS;
1426          end if;
1427
1428          --  Note: following assignment needs to be atomic
1429
1430          Rendezvous_Successful := Entry_Call.State = Done;
1431       end if;
1432    end Task_Entry_Call;
1433
1434    -----------------------
1435    -- Task_Entry_Caller --
1436    -----------------------
1437
1438    function Task_Entry_Caller (D : Task_Entry_Nesting_Depth) return Task_Id is
1439       Self_Id    : constant Task_Id := STPO.Self;
1440       Entry_Call : Entry_Call_Link;
1441
1442    begin
1443       Entry_Call := Self_Id.Common.Call;
1444
1445       for Depth in 1 .. D loop
1446          Entry_Call := Entry_Call.Acceptor_Prev_Call;
1447          pragma Assert (Entry_Call /= null);
1448       end loop;
1449
1450       return Entry_Call.Self;
1451    end Task_Entry_Caller;
1452
1453    --------------------------
1454    -- Timed_Selective_Wait --
1455    --------------------------
1456
1457    procedure Timed_Selective_Wait
1458      (Open_Accepts       : Accept_List_Access;
1459       Select_Mode        : Select_Modes;
1460       Uninterpreted_Data : out System.Address;
1461       Timeout            : Duration;
1462       Mode               : Delay_Modes;
1463       Index              : out Select_Index)
1464    is
1465       Self_Id          : constant Task_Id := STPO.Self;
1466       Treatment        : Select_Treatment;
1467       Entry_Call       : Entry_Call_Link;
1468       Caller           : Task_Id;
1469       Selection        : Select_Index;
1470       Open_Alternative : Boolean;
1471       Timedout         : Boolean := False;
1472       Yielded          : Boolean := True;
1473
1474    begin
1475       pragma Assert (Select_Mode = Delay_Mode);
1476
1477       Initialization.Defer_Abort (Self_Id);
1478
1479       --  If we are aborted here, the effect will be pending
1480
1481       if Single_Lock then
1482          Lock_RTS;
1483       end if;
1484
1485       STPO.Write_Lock (Self_Id);
1486
1487       if not Self_Id.Callable then
1488          pragma Assert (Self_Id.Pending_ATC_Level = 0);
1489
1490          pragma Assert (Self_Id.Pending_Action);
1491
1492          STPO.Unlock (Self_Id);
1493
1494          if Single_Lock then
1495             Unlock_RTS;
1496          end if;
1497
1498          Initialization.Undefer_Abort (Self_Id);
1499
1500          --  Should never get here ???
1501
1502          pragma Assert (False);
1503          raise Standard'Abort_Signal;
1504       end if;
1505
1506       Uninterpreted_Data := Null_Address;
1507
1508       pragma Assert (Open_Accepts /= null);
1509
1510       Queuing.Select_Task_Entry_Call
1511         (Self_Id, Open_Accepts, Entry_Call, Selection, Open_Alternative);
1512
1513       --  Determine the kind and disposition of the select
1514
1515       Treatment := Default_Treatment (Select_Mode);
1516       Self_Id.Chosen_Index := No_Rendezvous;
1517
1518       if Open_Alternative then
1519          if Entry_Call /= null then
1520             if Open_Accepts (Selection).Null_Body then
1521                Treatment := Accept_Alternative_Completed;
1522
1523             else
1524                Setup_For_Rendezvous_With_Body (Entry_Call, Self_Id);
1525                Treatment := Accept_Alternative_Selected;
1526             end if;
1527
1528             Self_Id.Chosen_Index := Selection;
1529
1530          elsif Treatment = No_Alternative_Open then
1531             Treatment := Accept_Alternative_Open;
1532          end if;
1533       end if;
1534
1535       --  Handle the select according to the disposition selected above
1536
1537       case Treatment is
1538          when Accept_Alternative_Selected =>
1539             --  Ready to rendezvous
1540             --  In this case the accept body is not Null_Body. Defer abort
1541             --  until it gets into the accept body.
1542
1543             Uninterpreted_Data := Self_Id.Common.Call.Uninterpreted_Data;
1544             Initialization.Defer_Abort (Self_Id);
1545             STPO.Unlock (Self_Id);
1546
1547          when Accept_Alternative_Completed =>
1548             --  Rendezvous is over
1549
1550             if Parameters.Runtime_Traces then
1551                Send_Trace_Info (M_RDV_Complete, Entry_Call.Self);
1552             end if;
1553
1554             STPO.Unlock (Self_Id);
1555             Caller := Entry_Call.Self;
1556
1557             STPO.Write_Lock (Caller);
1558             Initialization.Wakeup_Entry_Caller (Self_Id, Entry_Call, Done);
1559             STPO.Unlock (Caller);
1560
1561          when Accept_Alternative_Open =>
1562
1563             --  Wait for caller
1564
1565             Self_Id.Open_Accepts := Open_Accepts;
1566
1567             --  Wait for a normal call and a pending action until the
1568             --  Wakeup_Time is reached.
1569
1570             Self_Id.Common.State := Acceptor_Sleep;
1571
1572             --  Try to remove calls to Sleep in the loop below by letting the
1573             --  caller a chance of getting ready immediately, using Unlock
1574             --  Yield. See similar action in Wait_For_Completion/Wait_For_Call.
1575
1576             if Single_Lock then
1577                Unlock_RTS;
1578             else
1579                Unlock (Self_Id);
1580             end if;
1581
1582             if Self_Id.Open_Accepts /= null then
1583                Yield;
1584             end if;
1585
1586             if Single_Lock then
1587                Lock_RTS;
1588             else
1589                Write_Lock (Self_Id);
1590             end if;
1591
1592             --  Check if this task has been aborted while the lock was released
1593
1594             if Self_Id.Pending_ATC_Level < Self_Id.ATC_Nesting_Level then
1595                Self_Id.Open_Accepts := null;
1596             end if;
1597
1598             loop
1599                exit when Self_Id.Open_Accepts = null;
1600
1601                if Timedout then
1602                   Sleep (Self_Id, Acceptor_Sleep);
1603                else
1604                   if Parameters.Runtime_Traces then
1605                      Send_Trace_Info (WT_Select,
1606                                       Self_Id,
1607                                       Integer (Open_Accepts'Length),
1608                                       Timeout);
1609                   end if;
1610
1611                   STPO.Timed_Sleep (Self_Id, Timeout, Mode,
1612                     Acceptor_Sleep, Timedout, Yielded);
1613                end if;
1614
1615                if Timedout then
1616                   Self_Id.Open_Accepts := null;
1617
1618                   if Parameters.Runtime_Traces then
1619                      Send_Trace_Info (E_Timeout);
1620                   end if;
1621                end if;
1622             end loop;
1623
1624             Self_Id.Common.State := Runnable;
1625
1626             --  Self_Id.Common.Call should already be updated by the Caller if
1627             --  not aborted. It might also be ready to do rendezvous even if
1628             --  this wakes up due to an abort. Therefore, if the call is not
1629             --  empty we need to do the rendezvous if the accept body is not
1630             --  Null_Body.
1631
1632             if Self_Id.Chosen_Index /= No_Rendezvous
1633               and then Self_Id.Common.Call /= null
1634               and then not Open_Accepts (Self_Id.Chosen_Index).Null_Body
1635             then
1636                Uninterpreted_Data := Self_Id.Common.Call.Uninterpreted_Data;
1637
1638                pragma Assert (Self_Id.Deferral_Level = 1);
1639
1640                Initialization.Defer_Abort_Nestable (Self_Id);
1641
1642                --  Leave abort deferred until the accept body
1643             end if;
1644
1645             STPO.Unlock (Self_Id);
1646
1647          when No_Alternative_Open =>
1648             --  In this case, Index will be No_Rendezvous on return. We sleep
1649             --  for the time we need to.
1650             --  Wait for a signal or timeout. A wakeup can be made
1651             --  for several reasons:
1652             --  1) Delay is expired
1653             --  2) Pending_Action needs to be checked
1654             --     (Abort, Priority change)
1655             --  3) Spurious wakeup
1656
1657             Self_Id.Open_Accepts := null;
1658             Self_Id.Common.State := Acceptor_Sleep;
1659
1660             STPO.Timed_Sleep (Self_Id, Timeout, Mode, Acceptor_Sleep,
1661               Timedout, Yielded);
1662
1663             Self_Id.Common.State := Runnable;
1664
1665             STPO.Unlock (Self_Id);
1666
1667          when others =>
1668             --  Should never get here
1669             pragma Assert (False);
1670             null;
1671       end case;
1672
1673       if Single_Lock then
1674          Unlock_RTS;
1675       end if;
1676
1677       if not Yielded then
1678          Yield;
1679       end if;
1680
1681       --  Caller has been chosen
1682
1683       --  Self_Id.Common.Call should already be updated by the Caller
1684
1685       --  Self_Id.Chosen_Index should either be updated by the Caller
1686       --  or by Test_Selective_Wait
1687
1688       Index := Self_Id.Chosen_Index;
1689       Initialization.Undefer_Abort_Nestable (Self_Id);
1690
1691       --  Start rendezvous, if not already completed
1692    end Timed_Selective_Wait;
1693
1694    ---------------------------
1695    -- Timed_Task_Entry_Call --
1696    ---------------------------
1697
1698    procedure Timed_Task_Entry_Call
1699      (Acceptor              : Task_Id;
1700       E                     : Task_Entry_Index;
1701       Uninterpreted_Data    : System.Address;
1702       Timeout               : Duration;
1703       Mode                  : Delay_Modes;
1704       Rendezvous_Successful : out Boolean)
1705    is
1706       Self_Id    : constant Task_Id := STPO.Self;
1707       Level      : ATC_Level;
1708       Entry_Call : Entry_Call_Link;
1709       Yielded    : Boolean;
1710
1711    begin
1712       --  If pragma Detect_Blocking is active then Program_Error must be
1713       --  raised if this potentially blocking operation is called from a
1714       --  protected action.
1715
1716       if System.Tasking.Detect_Blocking
1717         and then Self_Id.Common.Protected_Action_Nesting > 0
1718       then
1719          Ada.Exceptions.Raise_Exception
1720            (Program_Error'Identity, "potentially blocking operation");
1721       end if;
1722
1723       Initialization.Defer_Abort (Self_Id);
1724       Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
1725
1726       pragma Debug
1727         (Debug.Trace (Self_Id, "TTEC: entered ATC level: " &
1728          ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
1729
1730       if Parameters.Runtime_Traces then
1731          Send_Trace_Info (WT_Call, Acceptor,
1732                           Entry_Index (E), Timeout);
1733       end if;
1734
1735       Level := Self_Id.ATC_Nesting_Level;
1736       Entry_Call := Self_Id.Entry_Calls (Level)'Access;
1737       Entry_Call.Next := null;
1738       Entry_Call.Mode := Timed_Call;
1739       Entry_Call.Cancellation_Attempted := False;
1740
1741       --  If this is a call made inside of an abort deferred region,
1742       --  the call should be never abortable.
1743
1744       if Self_Id.Deferral_Level > 1 then
1745          Entry_Call.State := Never_Abortable;
1746       else
1747          Entry_Call.State := Now_Abortable;
1748       end if;
1749
1750       Entry_Call.E := Entry_Index (E);
1751       Entry_Call.Prio := Get_Priority (Self_Id);
1752       Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
1753       Entry_Call.Called_Task := Acceptor;
1754       Entry_Call.Called_PO := Null_Address;
1755       Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
1756       Entry_Call.With_Abort := True;
1757
1758       --  Note: the caller will undefer abort on return (see WARNING above)
1759
1760       if Single_Lock then
1761          Lock_RTS;
1762       end if;
1763
1764       if not Task_Do_Or_Queue (Self_Id, Entry_Call) then
1765          STPO.Write_Lock (Self_Id);
1766          Utilities.Exit_One_ATC_Level (Self_Id);
1767          STPO.Unlock (Self_Id);
1768
1769          if Single_Lock then
1770             Unlock_RTS;
1771          end if;
1772
1773          Initialization.Undefer_Abort (Self_Id);
1774
1775          if Parameters.Runtime_Traces then
1776             Send_Trace_Info (E_Missed, Acceptor);
1777          end if;
1778          raise Tasking_Error;
1779       end if;
1780
1781       Write_Lock (Self_Id);
1782       Entry_Calls.Wait_For_Completion_With_Timeout
1783         (Entry_Call, Timeout, Mode, Yielded);
1784       Unlock (Self_Id);
1785
1786       if Single_Lock then
1787          Unlock_RTS;
1788       end if;
1789
1790       --  ??? Do we need to yield in case Yielded is False
1791
1792       Rendezvous_Successful := Entry_Call.State = Done;
1793       Initialization.Undefer_Abort (Self_Id);
1794       Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1795    end Timed_Task_Entry_Call;
1796
1797    -------------------
1798    -- Wait_For_Call --
1799    -------------------
1800
1801    procedure Wait_For_Call (Self_Id : Task_Id) is
1802    begin
1803       Self_Id.Common.State := Acceptor_Sleep;
1804
1805       --  Try to remove calls to Sleep in the loop below by letting the caller
1806       --  a chance of getting ready immediately, using Unlock & Yield.
1807       --  See similar action in Wait_For_Completion & Timed_Selective_Wait.
1808
1809       if Single_Lock then
1810          Unlock_RTS;
1811       else
1812          Unlock (Self_Id);
1813       end if;
1814
1815       if Self_Id.Open_Accepts /= null then
1816          Yield;
1817       end if;
1818
1819       if Single_Lock then
1820          Lock_RTS;
1821       else
1822          Write_Lock (Self_Id);
1823       end if;
1824
1825       --  Check if this task has been aborted while the lock was released
1826
1827       if Self_Id.Pending_ATC_Level < Self_Id.ATC_Nesting_Level then
1828          Self_Id.Open_Accepts := null;
1829       end if;
1830
1831       loop
1832          exit when Self_Id.Open_Accepts = null;
1833          Sleep (Self_Id, Acceptor_Sleep);
1834       end loop;
1835
1836       Self_Id.Common.State := Runnable;
1837    end Wait_For_Call;
1838
1839 end System.Tasking.Rendezvous;