blob: cf51f61d1247c687b4d60daf469def528f6195b7 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT RUN-TIME COMPONENTS --
-- --
-- A D A . T A S K _ A T T R I B U T E S --
-- --
-- B o d y --
-- --
-- Copyright (C) 2014-2021, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNARL was developed by the GNARL team at Florida State University. --
-- Extensive contributions were provided by Ada Core Technologies, Inc. --
-- --
------------------------------------------------------------------------------
with System.Tasking;
with System.Tasking.Initialization;
with System.Tasking.Task_Attributes;
pragma Elaborate_All (System.Tasking.Task_Attributes);
with System.Task_Primitives.Operations;
with Ada.Finalization; use Ada.Finalization;
with Ada.Unchecked_Conversion;
with Ada.Unchecked_Deallocation;
package body Ada.Task_Attributes is
use System,
System.Tasking.Initialization,
System.Tasking,
System.Tasking.Task_Attributes;
package STPO renames System.Task_Primitives.Operations;
type Attribute_Cleanup is new Limited_Controlled with null record;
procedure Finalize (Cleanup : in out Attribute_Cleanup);
-- Finalize all tasks' attributes for this package
Cleanup : Attribute_Cleanup;
pragma Unreferenced (Cleanup);
-- Will call Finalize when this instantiation gets out of scope
---------------------------
-- Unchecked Conversions --
---------------------------
type Real_Attribute is record
Free : Deallocator;
Value : Attribute;
end record;
type Real_Attribute_Access is access all Real_Attribute;
pragma No_Strict_Aliasing (Real_Attribute_Access);
-- Each value in the task control block's Attributes array is either
-- mapped to the attribute value directly if Fast_Path is True, or
-- is in effect a Real_Attribute_Access.
--
-- Note: the Deallocator field must be first, for compatibility with
-- System.Tasking.Task_Attributes.Attribute_Record and to allow unchecked
-- conversions between Attribute_Access and Real_Attribute_Access.
function New_Attribute (Val : Attribute) return Atomic_Address;
-- Create a new Real_Attribute using Val, and return its address. The
-- returned value can be converted via To_Real_Attribute.
procedure Deallocate (Ptr : Atomic_Address);
-- Free memory associated with Ptr, a Real_Attribute_Access in reality
function To_Real_Attribute is new
Ada.Unchecked_Conversion (Atomic_Address, Real_Attribute_Access);
pragma Warnings (Off);
-- Kill warning about possible size mismatch
function To_Address is new
Ada.Unchecked_Conversion (Attribute, Atomic_Address);
function To_Attribute is new
Ada.Unchecked_Conversion (Atomic_Address, Attribute);
type Unsigned is mod 2 ** Integer'Size;
function To_Address is new
Ada.Unchecked_Conversion (Attribute, System.Address);
function To_Unsigned is new
Ada.Unchecked_Conversion (Attribute, Unsigned);
pragma Warnings (On);
function To_Address is new
Ada.Unchecked_Conversion (Real_Attribute_Access, Atomic_Address);
pragma Warnings (Off);
-- Kill warning about possible aliasing
function To_Handle is new
Ada.Unchecked_Conversion (System.Address, Attribute_Handle);
pragma Warnings (On);
function To_Task_Id is new
Ada.Unchecked_Conversion (Task_Identification.Task_Id, Task_Id);
-- To access TCB of identified task
procedure Free is new
Ada.Unchecked_Deallocation (Real_Attribute, Real_Attribute_Access);
Fast_Path : constant Boolean :=
(Attribute'Size = Integer'Size
and then Attribute'Alignment <= Atomic_Address'Alignment
and then To_Unsigned (Initial_Value) = 0)
or else (Attribute'Size = System.Address'Size
and then Attribute'Alignment <= Atomic_Address'Alignment
and then To_Address (Initial_Value) = System.Null_Address);
-- If the attribute fits in an Atomic_Address (both size and alignment)
-- and Initial_Value is 0 (or null), then we will map the attribute
-- directly into ATCB.Attributes (Index), otherwise we will create
-- a level of indirection and instead use Attributes (Index) as a
-- Real_Attribute_Access.
Index : constant Integer :=
Next_Index (Require_Finalization => not Fast_Path);
-- Index in the task control block's Attributes array
--------------
-- Finalize --
--------------
procedure Finalize (Cleanup : in out Attribute_Cleanup) is
pragma Unreferenced (Cleanup);
begin
STPO.Lock_RTS;
declare
C : System.Tasking.Task_Id := System.Tasking.All_Tasks_List;
begin
while C /= null loop
STPO.Write_Lock (C);
if C.Attributes (Index) /= 0
and then Require_Finalization (Index)
then
Deallocate (C.Attributes (Index));
C.Attributes (Index) := 0;
end if;
STPO.Unlock (C);
C := C.Common.All_Tasks_Link;
end loop;
end;
Finalize (Index);
STPO.Unlock_RTS;
end Finalize;
----------------
-- Deallocate --
----------------
procedure Deallocate (Ptr : Atomic_Address) is
Obj : Real_Attribute_Access := To_Real_Attribute (Ptr);
begin
Free (Obj);
end Deallocate;
-------------------
-- New_Attribute --
-------------------
function New_Attribute (Val : Attribute) return Atomic_Address is
Tmp : Real_Attribute_Access;
begin
Tmp := new Real_Attribute'(Free => Deallocate'Unrestricted_Access,
Value => Val);
return To_Address (Tmp);
end New_Attribute;
---------------
-- Reference --
---------------
function Reference
(T : Task_Identification.Task_Id := Task_Identification.Current_Task)
return Attribute_Handle
is
Self_Id : Task_Id;
TT : constant Task_Id := To_Task_Id (T);
Error_Message : constant String := "trying to get the reference of a ";
Result : Attribute_Handle;
begin
if TT = null then
raise Program_Error with Error_Message & "null task";
end if;
if TT.Common.State = Terminated then
raise Tasking_Error with Error_Message & "terminated task";
end if;
if Fast_Path then
-- Kill warning about possible alignment mismatch. If this happens,
-- Fast_Path will be False anyway
pragma Warnings (Off);
return To_Handle (TT.Attributes (Index)'Address);
pragma Warnings (On);
else
Self_Id := STPO.Self;
Task_Lock (Self_Id);
if TT.Attributes (Index) = 0 then
TT.Attributes (Index) := New_Attribute (Initial_Value);
end if;
Result := To_Handle
(To_Real_Attribute (TT.Attributes (Index)).Value'Address);
Task_Unlock (Self_Id);
return Result;
end if;
end Reference;
------------------
-- Reinitialize --
------------------
procedure Reinitialize
(T : Task_Identification.Task_Id := Task_Identification.Current_Task)
is
Self_Id : Task_Id;
TT : constant Task_Id := To_Task_Id (T);
Error_Message : constant String := "Trying to Reinitialize a ";
begin
if TT = null then
raise Program_Error with Error_Message & "null task";
end if;
if TT.Common.State = Terminated then
raise Tasking_Error with Error_Message & "terminated task";
end if;
if Fast_Path then
-- No finalization needed, simply reset to Initial_Value
TT.Attributes (Index) := To_Address (Initial_Value);
else
Self_Id := STPO.Self;
Task_Lock (Self_Id);
declare
Attr : Atomic_Address renames TT.Attributes (Index);
begin
if Attr /= 0 then
Deallocate (Attr);
Attr := 0;
end if;
end;
Task_Unlock (Self_Id);
end if;
end Reinitialize;
---------------
-- Set_Value --
---------------
procedure Set_Value
(Val : Attribute;
T : Task_Identification.Task_Id := Task_Identification.Current_Task)
is
Self_Id : Task_Id;
TT : constant Task_Id := To_Task_Id (T);
Error_Message : constant String := "trying to set the value of a ";
begin
if TT = null then
raise Program_Error with Error_Message & "null task";
end if;
if TT.Common.State = Terminated then
raise Tasking_Error with Error_Message & "terminated task";
end if;
if Fast_Path then
-- No finalization needed, simply set to Val
if Attribute'Size = Integer'Size then
TT.Attributes (Index) := Atomic_Address (To_Unsigned (Val));
else
TT.Attributes (Index) := To_Address (Val);
end if;
else
Self_Id := STPO.Self;
Task_Lock (Self_Id);
declare
Attr : Atomic_Address renames TT.Attributes (Index);
begin
if Attr /= 0 then
Deallocate (Attr);
end if;
Attr := New_Attribute (Val);
end;
Task_Unlock (Self_Id);
end if;
end Set_Value;
-----------
-- Value --
-----------
function Value
(T : Task_Identification.Task_Id := Task_Identification.Current_Task)
return Attribute
is
Self_Id : Task_Id;
TT : constant Task_Id := To_Task_Id (T);
Error_Message : constant String := "trying to get the value of a ";
begin
if TT = null then
raise Program_Error with Error_Message & "null task";
end if;
if TT.Common.State = Terminated then
raise Tasking_Error with Error_Message & "terminated task";
end if;
if Fast_Path then
return To_Attribute (TT.Attributes (Index));
else
Self_Id := STPO.Self;
Task_Lock (Self_Id);
declare
Attr : Atomic_Address renames TT.Attributes (Index);
begin
if Attr = 0 then
Task_Unlock (Self_Id);
return Initial_Value;
else
declare
Result : constant Attribute :=
To_Real_Attribute (Attr).Value;
begin
Task_Unlock (Self_Id);
return Result;
end;
end if;
end;
end if;
end Value;
end Ada.Task_Attributes;