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