OSDN Git Service

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