home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
gnat-2.06-src.tgz
/
tar.out
/
fsf
/
gnat
/
ada
/
s-tasren.adb
< prev
next >
Wrap
Text File
|
1996-09-28
|
35KB
|
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;