home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-10-17 | 34.1 KB | 1,059 lines |
- ------------------------------------------------------------------------------
- -- --
- -- GNU ADA RUNTIME LIBRARY (GNARL) COMPONENTS --
- -- --
- -- S Y S T E M . T A S K I N G . R E N D E Z V O U S --
- -- --
- -- B o d y --
- -- --
- -- $Revision: 1.34 $ --
- -- --
- -- Copyright (c) 1991,1992,1993,1994,1995 FSU, All Rights Reserved --
- -- --
- -- GNARL is free software; you can redistribute it and/or modify it under --
- -- terms of the GNU Library General Public License as published by the --
- -- Free Software Foundation; either version 2, or (at your option) any --
- -- later version. GNARL is distributed in the hope that it will be use- --
- -- ful, but but WITHOUT ANY WARRANTY; without even the implied warranty of --
- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Gen- --
- -- eral Library Public License for more details. You should have received --
- -- a copy of the GNU Library General Public License along with GNARL; see --
- -- file COPYING.LIB. If not, write to the Free Software Foundation, 675 --
- -- Mass Ave, Cambridge, MA 02139, USA. --
- -- --
- ------------------------------------------------------------------------------
-
- with System.Task_Primitives; use System.Task_Primitives;
-
- with System.Tasking.Abortion;
- -- Used for, Abortion.Defer_Abortion,
- -- Abortion.Undefer_Abortion,
- -- Abortion.Change_Base_Priority
-
- with System.Tasking.Queuing; use System.Tasking.Queuing;
- -- Used for, Queuing.Enqueue,
- -- Queuing.Dequeue,
- -- Queuing.Dequeue_Head,
- -- Queuing.Count_Waiting,
- -- Queuing.Select_Task_Entry_Call
-
- with System.Tasking.Utilities;
- -- Used for, Utilities.Abort_To_Level
- -- Utilities.Reset_Priority
- -- Utilities.Terminate_Alternative
- -- Utilities.Runtime_Assert_Shutdown
- -- Utilities.Wait_For_Completion;
-
- with System.Tasking.Entry_Calls;
- -- Used for, Wait_For_Completion
- -- Wait_Until_Abortable
-
- with System.Compiler_Exceptions;
- -- Used for, Compiler_Exceptions."="
- -- Exception_ID
- -- Null_Exception
-
- package body System.Tasking.Rendezvous is
-
- procedure Defer_Abortion
- renames Abortion.Defer_Abortion;
-
- procedure Undefer_Abortion renames
- Abortion.Undefer_Abortion;
-
- type Select_Treatment is (
- Accept_Alternative_Selected,
- Accept_Alternative_Completed,
- Else_Selected,
- Terminate_Selected,
- Accept_Alternative_Open,
- No_Alternative_Open);
-
- Default_Treatment : constant array (Select_Modes) of Select_Treatment :=
- (Simple_Mode => No_Alternative_Open,
- Else_Mode => Else_Selected,
- Terminate_Mode => Terminate_Selected);
-
- -----------------------
- -- Local Subprograms --
- -----------------------
-
- procedure Boost_Priority
- (Call : Entry_Call_Link;
- Acceptor : Task_ID);
- pragma Inline (Boost_Priority);
-
- procedure Call_Synchronous
- (Acceptor : Task_ID;
- E : Task_Entry_Index;
- Uninterpreted_Data : System.Address;
- Mode : Call_Modes;
- Rendezvous_Successful : out Boolean);
- pragma Inline (Call_Synchronous);
- -- This call is used to make a simple or conditional entry call.
-
- procedure Do_Or_Queue
- (Entry_Call : in out Entry_Call_Link);
- -- Either initiate the entry call, such that the accepting task is
- -- free to execute the rendezvous, queue the call on the acceptor's
- -- queue, or cancel the call. Conditional calls that cannot be
- -- accepted immediately are cancelled.
-
- procedure Adjust_For_Terminate_Alternative_Call (Acceptor : Task_ID);
- -- Called by caller to wake up the acceptor if it is waiting on
- -- terminate_alternative.
-
- --------------------
- -- Boost_Priority --
- --------------------
-
- procedure Boost_Priority
- (Call : Entry_Call_Link;
- Acceptor : Task_ID)
- is
- Caller : Task_ID := Call.Self;
-
- begin
- if Get_Priority (Caller.LL_TCB'Access) >
- Get_Priority (Acceptor.LL_TCB'Access)
- then
- Call.Acceptor_Prev_Priority := Acceptor.Current_Priority;
- Acceptor.Current_Priority := Caller.Current_Priority;
- Set_Priority (Acceptor.LL_TCB'Access, Acceptor.Current_Priority);
- else
- Call.Acceptor_Prev_Priority := Priority_Not_Boosted;
- end if;
- end Boost_Priority;
-
- -----------------
- -- Do_Or_Queue --
- -----------------
-
- procedure Do_Or_Queue
- (Entry_Call : in out Entry_Call_Link)
- is
- E : Task_Entry_Index := Task_Entry_Index (Entry_Call.E);
- Acceptor : Task_ID := Entry_Call.Called_Task;
- begin
-
- if Acceptor.Accepting = Not_Accepting then
- if Callable (Acceptor) then
- if Entry_Call.Mode /= Conditional_Call
- or else not Entry_Call.Abortable
- then
- Enqueue (Acceptor.Entry_Queues (E), Entry_Call);
- end if;
- else
-
- -- If the acceptor is not callable, cancel the call
- -- and raise Tasking_Error. The call is not cancelled
- -- for an asynchronous call, since Cancel_Task_Entry_Call
- -- will do the decrement in that case.
-
- -- ??? It would be better if all entry call cancellation
- -- and the raising of Tasking_Error could be isolated
- -- to Wait_For_Completion.
-
- if Entry_Call.Mode /= Asynchronous_Call then
- Entry_Call.Self.ATC_Nesting_Level :=
- Entry_Call.Self.ATC_Nesting_Level - 1;
- end if;
-
- Unlock (Acceptor.L);
- Undefer_Abortion;
- raise Tasking_Error;
-
- end if;
-
- else
-
- -- ??? This should have a special case for Trivial_Accept, so that
- -- we don't have the loop setup overhead.
-
- for J in Acceptor.Open_Accepts'Range loop
- if Entry_Call.E = Entry_Index (Acceptor.Open_Accepts (J).S) then
- -- do rendezvous
- Acceptor.Accepting := Not_Accepting;
-
- Entry_Call.Acceptor_Prev_Call := Acceptor.Call;
- Acceptor.Call := Entry_Call;
- Acceptor.Chosen_Index := J;
-
- Entry_Call.Abortable := False;
- -- Not abortable while in progress.
-
- if Acceptor.Open_Accepts (J).Null_Body then
-
- Entry_Call.Done := True;
- -- Normally, this would have to be protected by
- -- the caller's mutex. However, in this case we
- -- know that the acceptor is accepting, which means
- -- that it has yet to remove a call from its queue,
- -- and it will need to lock its own mutex to do that,
- -- which we hold. It won't look at Entry_Call.Done
- -- until it has the call, so it should be safe to
- -- set it here.
-
- Cond_Signal (Acceptor.Cond);
- else
- Boost_Priority (Entry_Call, Acceptor);
- Cond_Signal (Acceptor.Cond);
- end if;
- exit;
- end if;
-
- end loop;
-
- -- If the acceptor was ready to accept this call,
- -- Acceptor.Accepting will have been set to Not_Accepting
- -- in the above loop. Otherwise, the acceptor is accepting,
- -- but not this entry. Try to queue the call.
-
- if Acceptor.Accepting /= Not_Accepting
- and then (Entry_Call.Mode /= Conditional_Call
- or else not Entry_Call.Abortable)
- then
- Enqueue (Acceptor.Entry_Queues (E), Entry_Call);
- end if;
-
- end if;
- end Do_Or_Queue;
-
- -------------------------------------------
- -- Adjust_For_Terminate_Alternative_Call --
- -------------------------------------------
-
- procedure Adjust_For_Terminate_Alternative_Call (Acceptor : Task_ID) is
- P : Task_ID;
- Error : boolean;
- begin
- Write_Lock (Acceptor.L, Error);
-
- if Acceptor.Terminate_Alternative then
- Acceptor.Stage := Active;
- Acceptor.Awake_Count := Acceptor.Awake_Count + 1;
-
- -- At this point, T.Awake_Count and P.Awaited_Dependent_Count could
- -- be out of synchronization. However, we know that
- -- P.Awaited_Dependent_Count cannot be zero, and cannot go to zero,
- -- since some other dependent must have just called us. There should
- -- therefore be no danger of the parent terminating before we
- -- increment P.Awaited_Dependent_Count below.
-
- if Acceptor.Awake_Count = 1 then
- Unlock (Acceptor.L);
-
- if Acceptor.Pending_ATC_Level <
- Acceptor.ATC_Nesting_Level then
- Abortion.Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing after being aborted!"));
- end if;
-
- P := Acceptor.Parent;
- Write_Lock (P.L, Error);
-
- if P.Awake_Count /= 0 then
- P.Awake_Count := P.Awake_Count + 1;
-
- else
- Unlock (P.L);
- Utilities.Abort_To_Level (Acceptor, 0);
- Abortion.Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing after being aborted!"));
- end if;
-
- -- Conservative checks which should only matter when an interrupt
- -- entry was chosen. In this case, the current task completes if
- -- the parent has already been signaled that all children have
- -- terminated.
-
- if Acceptor.Master_of_Task = P.Master_Within then
- if P.Awaited_Dependent_Count /= 0 then
- P.Awaited_Dependent_Count := P.Awaited_Dependent_Count + 1;
-
- elsif P.Stage = Await_Dependents then
- Unlock (P.L);
- Utilities.Abort_To_Level (Acceptor, 0);
- Abortion.Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing after being aborted!"));
- end if;
- end if;
-
- Unlock (P.L);
-
- else
- Unlock (Acceptor.L);
-
- if Acceptor.Pending_ATC_Level <
- Acceptor.ATC_Nesting_Level then
- Abortion.Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing after being aborted!"));
- end if;
- end if;
-
- Write_Lock (Acceptor.L, Error);
-
- Acceptor.Terminate_Alternative := false;
- -- Need to set this flag off in order not to make subsequent calls
- -- to be treated to calls to Select With Terminate Alternative.
-
- end if;
- Unlock (Acceptor.L);
-
- end Adjust_For_Terminate_Alternative_Call;
-
- ----------------------
- -- Call_Synchronous --
- ----------------------
-
- procedure Call_Synchronous
- (Acceptor : Task_ID;
- E : Task_Entry_Index;
- Uninterpreted_Data : System.Address;
- Mode : Call_Modes;
- Rendezvous_Successful : out Boolean)
- is
- Caller : constant Task_ID := Self;
- Level : ATC_Level;
- Entry_Call : Entry_Call_Link;
- Error : Boolean;
-
- begin
-
- pragma Assert (Mode /= Asynchronous_Call
- or else Utilities.Runtime_Assert_Shutdown (
- "Asynchronous call being treated synchronously."));
-
- Defer_Abortion;
- Caller.ATC_Nesting_Level := Caller.ATC_Nesting_Level + 1;
- Level := Caller.ATC_Nesting_Level;
-
- Entry_Call := Caller.Entry_Calls (Level)'Access;
-
- Entry_Call.Next := null;
- Entry_Call.Mode := Mode;
- Entry_Call.Abortable := True;
- Entry_Call.Done := False;
- Entry_Call.E := Entry_Index (E);
- Entry_Call.Prio := Caller.Current_Priority;
- Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
- Entry_Call.Called_Task := Acceptor;
- Entry_Call.Exception_To_Raise := Compiler_Exceptions.Null_Exception;
-
- -- Note: the caller will undefer abortion on return (see WARNING above)
-
- Adjust_For_Terminate_Alternative_Call (Acceptor);
-
- Write_Lock (Acceptor.L, Error);
- Do_Or_Queue (Entry_Call);
- Unlock (Acceptor.L);
- System.Tasking.Entry_Calls.Wait_For_Completion (Entry_Call);
- Rendezvous_Successful := Entry_Call.Done;
- Undefer_Abortion;
-
- pragma Assert (
- Caller.Pending_ATC_Level >= Caller.ATC_Nesting_Level or else
- Utilities.Runtime_Assert_Shutdown (
- "Continuing after aborting self!"));
-
- Utilities.Check_Exception;
- end Call_Synchronous;
-
- -----------------
- -- Call_Simple --
- -----------------
-
- procedure Call_Simple
- (Acceptor : Task_ID;
- E : Task_Entry_Index;
- Uninterpreted_Data : System.Address)
- is
- Rendezvous_Successful : Boolean;
-
- begin
- Call_Synchronous
- (Acceptor, E, Uninterpreted_Data, Simple_Call, Rendezvous_Successful);
- end Call_Simple;
-
- ----------------------------
- -- Cancel_Task_Entry_Call --
- ----------------------------
-
- procedure Cancel_Task_Entry_Call (Cancelled : out Boolean) is
- Caller : Task_ID := Self;
- Call : Entry_Call_Link;
-
- begin
- pragma Assert (Caller.ATC_Nesting_Level > ATC_Level_Base'First or else
- Utilities.Runtime_Assert_Shutdown (
- "Attempt to cancel nonexistent task entry call."));
-
- Call := Caller.Entry_Calls (Caller.ATC_Nesting_Level)'Access;
-
- pragma Assert (Call.Mode = Asynchronous_Call or else
- Utilities.Runtime_Assert_Shutdown (
- "Attempt to perform ATC on non-asynchronous task entry call"));
-
- pragma Assert (Call.Called_PO = Null_PO or else
- Utilities.Runtime_Assert_Shutdown (
- "Attempt to use Cancel_Task_Entry_Call on protected entry call."));
-
- Defer_Abortion;
-
- Utilities.Abort_To_Level (Caller, Call.Level - 1);
- System.Tasking.Entry_Calls.Wait_For_Completion (Call);
-
- Cancelled := not Call.Done;
- -- This allows the triggered statements to be skipped.
-
- Undefer_Abortion;
- Utilities.Check_Exception;
- end Cancel_Task_Entry_Call;
-
- ------------------------
- -- Requeue_Task_Entry --
- ------------------------
-
- procedure Requeue_Task_Entry
- (Acceptor : Task_ID;
- E : Task_Entry_Index;
- With_Abort : Boolean)
- is
- Old_Acceptor : Task_ID := Self;
- Caller : Task_ID;
- Entry_Call : Entry_Call_Link;
- Error : Boolean;
-
- begin
- Defer_Abortion;
- Write_Lock (Old_Acceptor.L, Error);
- Entry_Call := Old_Acceptor.Call;
- Caller := Entry_Call.Self;
- Old_Acceptor.Call := null;
-
- Entry_Call.Abortable := False;
- -- Don't permit this call to be aborted until we have switched to
- -- the new acceptor. Otherwise, we may queue a cancelled call below.
-
- Unlock (Old_Acceptor.L);
-
- Entry_Call.E := Entry_Index (E);
-
- Write_Lock (Acceptor.L, Error);
- Entry_Call.Called_Task := Acceptor;
- Entry_Call.Abortable := With_Abort;
- Entry_Call.Has_Been_Abortable :=
- With_Abort or Entry_Call.Has_Been_Abortable;
- Do_Or_Queue (Entry_Call);
- Unlock (Acceptor.L);
-
- Write_Lock (Caller.L, Error);
- Caller.Pending_Action := True;
-
- Cond_Signal (Caller.Cond);
- -- If this is a conditional entry call, and has just become
- -- abortable, the caller should be awakened to cancel the call.
-
- Unlock (Caller.L);
- Undefer_Abortion;
- end Requeue_Task_Entry;
-
- -------------------------------------
- -- Requeue_Protected_To_Task_Entry --
- -------------------------------------
-
- procedure Requeue_Protected_To_Task_Entry
- (Object : Protection_Access;
- Acceptor : Task_ID;
- E : Task_Entry_Index;
- With_Abort : Boolean)
- is
- Entry_Call : Entry_Call_Link := Object.Call_In_Progress;
- Caller : Task_ID := Entry_Call.Self;
- Error : Boolean;
- Abortable : Boolean;
- begin
- Defer_Abortion;
- Entry_Call.E := Entry_Index (E);
- Object.Call_In_Progress := null;
-
- Write_Lock (Acceptor.L, Error);
- Entry_Call.Called_Task := Acceptor;
- Entry_Call.Called_PO := Null_PO;
- Entry_Call.Abortable := With_Abort;
- Entry_Call.Has_Been_Abortable :=
- With_Abort or Entry_Call.Has_Been_Abortable;
- Do_Or_Queue (Entry_Call);
- Unlock (Acceptor.L);
-
- Write_Lock (Caller.L, Error);
- Entry_Call.E := Entry_Index (E);
-
- Caller.Pending_Action := True;
- Cond_Signal (Caller.Cond);
- -- If this is a conditional entry call, and has just become
- -- abortable, the caller should be awakened to cancel the call.
-
- Unlock (Caller.L);
- Undefer_Abortion;
- end Requeue_Protected_To_Task_Entry;
-
- ---------------------
- -- Task_Entry_Call --
- ---------------------
-
- procedure Task_Entry_Call
- (Acceptor : Task_ID;
- E : Task_Entry_Index;
- Uninterpreted_Data : System.Address;
- Mode : Call_Modes;
- Rendezvous_Successful : out Boolean)
- is
- Caller : constant Task_ID := Self;
- Rendezvous_Completed : Boolean;
- Entry_Call : Entry_Call_Link;
- Cancel_Was_Successful : Boolean;
- Error : Boolean;
- Initially_Abortable : Boolean;
-
- begin
- -- Simple or conditional call
-
- if Mode = Simple_Call or else Mode = Conditional_Call then
- Call_Synchronous
- (Acceptor, E, Uninterpreted_Data, Mode, Rendezvous_Successful);
-
- -- Asynchronous call
-
- else
-
- -- Abortion must already be deferred by the compiler-generated
- -- code. Without this, an abortion that occurs between the time
- -- that this call is made and the time that the abortable part's
- -- cleanup handler is set up might miss the cleanup handler and
- -- leave the call pending.
-
- Caller.ATC_Nesting_Level := Caller.ATC_Nesting_Level + 1;
-
- Entry_Call := Caller.Entry_Calls (Caller.ATC_Nesting_Level)'Access;
-
- Entry_Call.Next := null;
- Entry_Call.Mode := Mode;
- Entry_Call.Abortable := True;
- Entry_Call.Done := False;
- Entry_Call.E := Entry_Index (E);
- Entry_Call.Prio := Caller.Current_Priority;
- Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
- Entry_Call.Called_Task := Acceptor;
- Entry_Call.Called_PO := Null_PO;
- Entry_Call.Exception_To_Raise := Compiler_Exceptions.Null_Exception;
-
- Adjust_For_Terminate_Alternative_Call (Acceptor);
-
- Write_Lock (Acceptor.L, Error);
- Do_Or_Queue (Entry_Call);
-
- Initially_Abortable := Entry_Call.Abortable;
-
- Unlock (Acceptor.L);
-
- -- If the call was not queued abortably, we need to wait until
- -- it is before proceeding with the abortable part.
- -- Wait_Until_Abortable can be called unconditionally here,
- -- but it is expensive.
-
- if not Initially_Abortable then
- System.Tasking.Entry_Calls.Wait_Until_Abortable
- (Caller, Entry_Call);
- end if;
-
- Rendezvous_Successful := Entry_Call.Done;
- -- This needs to be atomic.
-
- end if;
- end Task_Entry_Call;
-
- -----------------
- -- Accept_Call --
- -----------------
-
- procedure Accept_Call
- (E : Task_Entry_Index;
- Uninterpreted_Data : out System.Address)
- is
- Acceptor : constant Task_ID := Self;
- Caller : Task_ID := null;
- Open_Accepts : aliased Accept_List (1 .. 1);
- Entry_Call : Entry_Call_Link;
- Error : Boolean;
-
- begin
- Defer_Abortion;
- Write_Lock (Acceptor.L, Error);
-
- -- If someone is completing this task, it must be because they plan
- -- to abort it. This task should not try to access its pending entry
- -- calls or queues in this case, as they are being emptied. Wait for
- -- abortion to kill us.
-
- if Acceptor.Stage >= Completing then
-
- loop
- if Acceptor.Pending_Action then
- if Acceptor.Pending_Priority_Change then
- Abortion.Change_Base_Priority (Acceptor);
- end if;
-
- exit when
- Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level;
- Acceptor.Pending_Action := False;
- end if;
- Cond_Wait (Acceptor.Cond, Acceptor.L);
- end loop;
-
- Unlock (Acceptor.L);
- Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing execution after being aborted."));
- end if;
-
- Dequeue_Head (Acceptor.Entry_Queues (E), Entry_Call);
-
- if Entry_Call /= null then
- Caller := Entry_Call.Self;
- Boost_Priority (Entry_Call, Acceptor);
- Entry_Call.Acceptor_Prev_Call := Acceptor.Call;
-
- Entry_Call.Abortable := False;
- -- Not abortable while in progress.
-
- Acceptor.Call := Entry_Call;
- Uninterpreted_Data := Entry_Call.Uninterpreted_Data;
-
- else
- -- Wait for a caller
-
- Open_Accepts (1).Null_Body := false;
- Open_Accepts (1).S := E;
- Acceptor.Open_Accepts := Open_Accepts'Unchecked_Access;
-
- Acceptor.Accepting := Simple_Accept;
-
- -- Wait for normal call
-
- while Acceptor.Accepting /= Not_Accepting loop
- if Acceptor.Pending_Action then
- if Acceptor.Pending_Priority_Change then
- Abortion.Change_Base_Priority (Acceptor);
- end if;
-
- exit when
- Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level;
- Acceptor.Pending_Action := False;
- end if;
- Cond_Wait (Acceptor.Cond, Acceptor.L);
- end loop;
-
- if Acceptor.Pending_ATC_Level >= Acceptor.ATC_Nesting_Level then
- Caller := Acceptor.Call.Self;
- Uninterpreted_Data :=
- Caller.Entry_Calls (Caller.ATC_Nesting_Level).Uninterpreted_Data;
- end if;
-
- -- If this task has been aborted, skip the Uninterpreted_Data load
- -- (Caller will not be reliable) and fall through to
- -- Undefer_Abortion which will allow the task to be killed.
- end if;
-
- -- Acceptor.Call should already be updated by the Caller
-
- Unlock (Acceptor.L);
- Undefer_Abortion;
-
- -- Start rendezvous
- end Accept_Call;
-
- --------------------
- -- Accept_Trivial --
- --------------------
-
- procedure Accept_Trivial (E : Task_Entry_Index) is
- Acceptor : constant Task_ID := Self;
- Caller : Task_ID := null;
- Open_Accepts : aliased Accept_List (1 .. 1);
- Entry_Call : Entry_Call_Link;
- Error : Boolean;
-
- begin
- Defer_Abortion;
- Write_Lock (Acceptor.L, Error);
-
- -- If someone is completing this task, it must be because they plan
- -- to abort it. This task should not try to access its pending entry
- -- calls or queues in this case, as they are being emptied. Wait for
- -- abortion to kill us.
-
- if Acceptor.Stage >= Completing then
-
- loop
- if Acceptor.Pending_Action then
- if Acceptor.Pending_Priority_Change then
- Abortion.Change_Base_Priority (Acceptor);
- end if;
-
- exit when
- Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level;
- Acceptor.Pending_Action := False;
- end if;
- Cond_Wait (Acceptor.Cond, Acceptor.L);
- end loop;
-
- Unlock (Acceptor.L);
- Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing execution after being aborted."));
- end if;
-
- Dequeue_Head (Acceptor.Entry_Queues (E), Entry_Call);
-
- if Entry_Call = null then
-
- -- Need to wait for call
-
- Open_Accepts (1).Null_Body := False;
- Open_Accepts (1).S := E;
- Acceptor.Open_Accepts := Open_Accepts'Unchecked_Access;
-
- Acceptor.Accepting := Trivial_Accept;
-
- -- Wait for normal entry call
-
- while Acceptor.Accepting /= Not_Accepting loop
- if Acceptor.Pending_Action then
- if Acceptor.Pending_Priority_Change then
- Abortion.Change_Base_Priority (Acceptor);
- end if;
-
- exit when
- Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level;
- Acceptor.Pending_Action := False;
- end if;
- Cond_Wait (Acceptor.Cond, Acceptor.L);
- end loop;
-
-
- if Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level then
- Unlock (Acceptor.L);
- Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing after being aborted!"));
- else
- Entry_Call := Acceptor.Call;
- Acceptor.Call := Entry_Call.Acceptor_Prev_Call;
- end if;
-
- else
- Entry_Call.Abortable := False;
- -- No longer abortable.
- end if;
-
- Unlock (Acceptor.L);
- Caller := Entry_Call.Self;
- Write_Lock (Caller.L, Error);
-
- Entry_Call.Done := True;
- -- Done with mutex locked to make sure that signal is not lost.
-
- Unlock (Caller.L);
-
- if Entry_Call.Mode = Asynchronous_Call then
- Utilities.Abort_To_Level (Caller, Entry_Call.Level - 1);
- else
- Cond_Signal (Caller.Cond);
- end if;
-
- Undefer_Abortion;
- end Accept_Trivial;
-
- -------------------------------------
- -- Exceptional_Complete_Rendezvous --
- -------------------------------------
-
- procedure Exceptional_Complete_Rendezvous
- (Ex : Compiler_Exceptions.Exception_ID)
- is
- Acceptor : constant Task_ID := Self;
- Caller : Task_ID;
- Call : Entry_Call_Link;
- Prev_Priority : Rendezvous_Priority;
- Error : Boolean;
-
- begin
- Defer_Abortion;
- Call := Acceptor.Call;
- Acceptor.Call := Call.Acceptor_Prev_Call;
- Prev_Priority := Call.Acceptor_Prev_Priority;
- Call.Exception_To_Raise := Ex;
- Caller := Call.Self;
- Write_Lock (Caller.L, Error);
-
- Call.Done := True;
- -- Done with mutex locked to make sure that signal is not lost.
-
- Unlock (Caller.L);
-
- if Call.Mode = Asynchronous_Call then
- Utilities.Abort_To_Level (Caller, Call.Level - 1);
- else
- Cond_Signal (Caller.Cond);
- end if;
-
- Utilities.Reset_Priority (Prev_Priority, Acceptor);
-
- Acceptor.Exception_To_Raise := Ex;
-
- Undefer_Abortion;
- Utilities.Check_Exception;
- end Exceptional_Complete_Rendezvous;
-
- -------------------------
- -- Complete_Rendezvous --
- -------------------------
-
- procedure Complete_Rendezvous is
- begin
- Exceptional_Complete_Rendezvous (Compiler_Exceptions.Null_Exception);
- end Complete_Rendezvous;
-
- --------------------
- -- Selective_Wait --
- --------------------
-
- procedure Selective_Wait
- (Open_Accepts : Accept_List_Access;
- Select_Mode : Select_Modes;
- Uninterpreted_Data : out System.Address;
- Index : out Select_Index)
- is
- Acceptor : constant Task_ID := Self;
- Treatment : Select_Treatment;
- I_Result : Integer;
- Error : Boolean;
- Entry_Call : Entry_Call_Link;
- Caller : Task_ID;
- Selection : Select_Index;
- Open_Alternative : Boolean;
-
- begin
- Defer_Abortion;
- Write_Lock (Acceptor.L, Error);
-
- -- If someone is completing this task, it must be because they plan
- -- to abort it. This task should not try to access its pending entry
- -- calls or queues in this case, as they are being emptied. Wait for
- -- abortion to kill us.
-
- if Acceptor.Stage >= Completing then
-
- loop
- if Acceptor.Pending_Action then
- if Acceptor.Pending_Priority_Change then
- Abortion.Change_Base_Priority (Acceptor);
- end if;
-
- exit when
- Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level;
- Acceptor.Pending_Action := False;
- end if;
- Cond_Wait (Acceptor.Cond, Acceptor.L);
- end loop;
-
- Unlock (Acceptor.L);
- Undefer_Abortion;
- pragma Assert (
- Utilities.Runtime_Assert_Shutdown (
- "Continuing execution after being aborted."));
- end if;
-
- Select_Task_Entry_Call
- (Acceptor, Open_Accepts, Entry_Call, Selection, Open_Alternative);
-
- -- Determine the kind and disposition of the select.
-
- Treatment := Default_Treatment (Select_Mode);
- Acceptor.Chosen_Index := No_Rendezvous;
-
- if Open_Alternative then
- if Entry_Call /= null then
- if Open_Accepts (Selection).Null_Body then
- Treatment := Accept_Alternative_Completed;
- else
- Boost_Priority (Entry_Call, Acceptor);
- Entry_Call.Acceptor_Prev_Call := Acceptor.Call;
- Acceptor.Call := Entry_Call;
- Treatment := Accept_Alternative_Selected;
- end if;
- Acceptor.Chosen_Index := Selection;
- elsif Treatment = No_Alternative_Open then
- Treatment := Accept_Alternative_Open;
- end if;
- end if;
-
- -- Handle the select according to the disposition selected above.
-
- case Treatment is
-
- when Accept_Alternative_Selected =>
-
- -- Ready to rendezvous already
-
- Unlock (Acceptor.L);
- Uninterpreted_Data := Acceptor.Call.Uninterpreted_Data;
-
- when Accept_Alternative_Completed =>
-
- -- Rendezvous is over
-
- Unlock (Acceptor.L);
- Caller := Entry_Call.Self;
- Write_Lock (Caller.L, Error);
- Entry_Call.Done := True;
- Unlock (Caller.L);
- if Entry_Call.Mode = Asynchronous_Call then
- Utilities.Abort_To_Level (Caller, Entry_Call.Level - 1);
- else
- Cond_Signal (Caller.Cond);
- end if;
-
- when Accept_Alternative_Open =>
-
- -- Wait for caller.
-
- Acceptor.Open_Accepts := Open_Accepts;
-
- Acceptor.Accepting := Select_Wait;
-
- while Acceptor.Accepting /= Not_Accepting
- loop
- if Acceptor.Pending_Action then
- if Acceptor.Pending_Priority_Change then
- Abortion.Change_Base_Priority (Acceptor);
- end if;
-
- exit when
- Acceptor.Pending_ATC_Level < Acceptor.ATC_Nesting_Level;
- Acceptor.Pending_Action := False;
- end if;
- Cond_Wait (Acceptor.Cond, Acceptor.L);
- end loop;
-
- -- Acceptor.Call should already be updated by the Caller if
- -- not aborted.
-
- if Acceptor.Pending_ATC_Level >= Acceptor.ATC_Nesting_Level and then
- not Open_Accepts (Acceptor.Chosen_Index).Null_Body then
- Uninterpreted_Data := Acceptor.Call.Uninterpreted_Data;
- end if;
-
- Unlock (Acceptor.L);
-
- when Else_Selected =>
- Acceptor.Accepting := Not_Accepting;
- Unlock (Acceptor.L);
-
- when Terminate_Selected =>
-
- -- Terminate alternative is open
-
- Acceptor.Open_Accepts := Open_Accepts;
-
- Acceptor.Accepting := Select_Wait;
-
- -- We need to check if a signal is pending on an open interrupt
- -- entry. Otherwise this task would become passive (since terminate
- -- alternative is open) and, if none of the siblings are active
- -- any more, the task could not wake up any more, even though a
- -- signal might be pending on an open interrupt entry.
-
- Unlock (Acceptor.L);
- Utilities.Terminate_Alternative;
-
- -- Wait for normal entry call or termination
-
- -- consider letting Terminate_Alternative assume mutex L
- -- is already locked, and return with it locked, so
- -- this code could be simplified???
-
- -- No return here if Acceptor completes, otherwise
- -- Acceptor.Call should already be updated by the Caller
-
- Index := Acceptor.Chosen_Index;
- if not Open_Accepts (Acceptor.Chosen_Index).Null_Body then
- Uninterpreted_Data := Acceptor.Call.Uninterpreted_Data;
- end if;
- Undefer_Abortion;
- return;
-
- when No_Alternative_Open =>
-
- -- In this case, Index will be No_Rendezvous on return, which
- -- should cause the compiler-generated code to raise
- -- Program_Error.
-
- Unlock (Acceptor.L);
-
- end case;
-
- -- Caller has been chosen
-
- -- Acceptor.Call should already be updated by the Caller
-
- -- Acceptor.Chosen_Index should either be updated by the Caller
- -- or by Test_Selective_Wait
-
- Index := Acceptor.Chosen_Index;
- Undefer_Abortion;
-
- -- Start rendezvous, if not already completed.
-
- end Selective_Wait;
-
- ----------------
- -- Task_Count --
- ----------------
-
- function Task_Count (E : Task_Entry_Index) return Natural is
- T : constant Task_ID := Self;
- Return_Count : Natural;
- Error : Boolean;
-
- begin
- Write_Lock (T.L, Error);
- Return_Count := Count_Waiting (T.Entry_Queues (E));
- Unlock (T.L);
- return Return_Count;
- end Task_Count;
-
- --------------
- -- Callable --
- --------------
-
- function Callable (T : Task_ID) return Boolean is
- begin
- return T.Stage < Complete
- and then T.Pending_ATC_Level > ATC_Level_Base'First;
- end Callable;
-
- end System.Tasking.Rendezvous;
-