| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- S Y S T E M . F I N A L I Z A T I O N _ M A S T E R S -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2015-2022, 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/>. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Ada.Exceptions; use Ada.Exceptions; |
| |
| with System.Address_Image; |
| with System.HTable; use System.HTable; |
| with System.IO; use System.IO; |
| with System.Soft_Links; use System.Soft_Links; |
| with System.Storage_Elements; use System.Storage_Elements; |
| |
| package body System.Finalization_Masters is |
| |
| -- Finalize_Address hash table types. In general, masters are homogeneous |
| -- collections of controlled objects. Rare cases such as allocations on a |
| -- subpool require heterogeneous masters. The following table provides a |
| -- relation between object address and its Finalize_Address routine. |
| |
| type Header_Num is range 0 .. 127; |
| |
| function Hash (Key : System.Address) return Header_Num; |
| |
| -- Address --> Finalize_Address_Ptr |
| |
| package Finalize_Address_Table is new Simple_HTable |
| (Header_Num => Header_Num, |
| Element => Finalize_Address_Ptr, |
| No_Element => null, |
| Key => System.Address, |
| Hash => Hash, |
| Equal => "="); |
| |
| --------------------------- |
| -- Add_Offset_To_Address -- |
| --------------------------- |
| |
| function Add_Offset_To_Address |
| (Addr : System.Address; |
| Offset : System.Storage_Elements.Storage_Offset) return System.Address |
| is |
| begin |
| return System.Storage_Elements."+" (Addr, Offset); |
| end Add_Offset_To_Address; |
| |
| ------------ |
| -- Attach -- |
| ------------ |
| |
| procedure Attach (N : not null FM_Node_Ptr; L : not null FM_Node_Ptr) is |
| begin |
| Lock_Task.all; |
| Attach_Unprotected (N, L); |
| Unlock_Task.all; |
| |
| -- Note: No need to unlock in case of an exception because the above |
| -- code can never raise one. |
| end Attach; |
| |
| ------------------------ |
| -- Attach_Unprotected -- |
| ------------------------ |
| |
| procedure Attach_Unprotected |
| (N : not null FM_Node_Ptr; |
| L : not null FM_Node_Ptr) |
| is |
| begin |
| L.Next.Prev := N; |
| N.Next := L.Next; |
| L.Next := N; |
| N.Prev := L; |
| end Attach_Unprotected; |
| |
| --------------- |
| -- Base_Pool -- |
| --------------- |
| |
| function Base_Pool |
| (Master : Finalization_Master) return Any_Storage_Pool_Ptr |
| is |
| begin |
| return Master.Base_Pool; |
| end Base_Pool; |
| |
| ----------------------------------------- |
| -- Delete_Finalize_Address_Unprotected -- |
| ----------------------------------------- |
| |
| procedure Delete_Finalize_Address_Unprotected (Obj : System.Address) is |
| begin |
| Finalize_Address_Table.Remove (Obj); |
| end Delete_Finalize_Address_Unprotected; |
| |
| ------------------------ |
| -- Detach_Unprotected -- |
| ------------------------ |
| |
| procedure Detach_Unprotected (N : not null FM_Node_Ptr) is |
| begin |
| if N.Prev /= null and then N.Next /= null then |
| N.Prev.Next := N.Next; |
| N.Next.Prev := N.Prev; |
| N.Prev := null; |
| N.Next := null; |
| end if; |
| end Detach_Unprotected; |
| |
| -------------- |
| -- Finalize -- |
| -------------- |
| |
| overriding procedure Finalize (Master : in out Finalization_Master) is |
| Cleanup : Finalize_Address_Ptr; |
| Curr_Ptr : FM_Node_Ptr; |
| Ex_Occur : Exception_Occurrence; |
| Obj_Addr : Address; |
| Raised : Boolean := False; |
| |
| function Is_Empty_List (L : not null FM_Node_Ptr) return Boolean; |
| -- Determine whether a list contains only one element, the dummy head |
| |
| ------------------- |
| -- Is_Empty_List -- |
| ------------------- |
| |
| function Is_Empty_List (L : not null FM_Node_Ptr) return Boolean is |
| begin |
| return L.Next = L and then L.Prev = L; |
| end Is_Empty_List; |
| |
| -- Start of processing for Finalize |
| |
| begin |
| Lock_Task.all; |
| |
| -- Synchronization: |
| -- Read - allocation, finalization |
| -- Write - finalization |
| |
| if Master.Finalization_Started then |
| Unlock_Task.all; |
| |
| -- Double finalization may occur during the handling of stand alone |
| -- libraries or the finalization of a pool with subpools. Due to the |
| -- potential aliasing of masters in these two cases, do not process |
| -- the same master twice. |
| |
| return; |
| end if; |
| |
| -- Lock the master to prevent any allocations while the objects are |
| -- being finalized. The master remains locked because either the master |
| -- is explicitly deallocated or the associated access type is about to |
| -- go out of scope. |
| |
| -- Synchronization: |
| -- Read - allocation, finalization |
| -- Write - finalization |
| |
| Master.Finalization_Started := True; |
| |
| while not Is_Empty_List (Master.Objects'Unchecked_Access) loop |
| Curr_Ptr := Master.Objects.Next; |
| |
| -- Synchronization: |
| -- Write - allocation, deallocation, finalization |
| |
| Detach_Unprotected (Curr_Ptr); |
| |
| -- Skip the list header in order to offer proper object layout for |
| -- finalization. |
| |
| Obj_Addr := Curr_Ptr.all'Address + Header_Size; |
| |
| -- Retrieve TSS primitive Finalize_Address depending on the master's |
| -- mode of operation. |
| |
| -- Synchronization: |
| -- Read - allocation, finalization |
| -- Write - outside |
| |
| if Master.Is_Homogeneous then |
| |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, outside |
| |
| Cleanup := Master.Finalize_Address; |
| |
| else |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, deallocation |
| |
| Cleanup := Finalize_Address_Unprotected (Obj_Addr); |
| end if; |
| |
| begin |
| Cleanup (Obj_Addr); |
| exception |
| when Fin_Occur : others => |
| if not Raised then |
| Raised := True; |
| Save_Occurrence (Ex_Occur, Fin_Occur); |
| end if; |
| end; |
| |
| -- When the master is a heterogeneous collection, destroy the object |
| -- - Finalize_Address pair since it is no longer needed. |
| |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - outside |
| |
| if not Master.Is_Homogeneous then |
| |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, deallocation, finalization |
| |
| Delete_Finalize_Address_Unprotected (Obj_Addr); |
| end if; |
| end loop; |
| |
| Unlock_Task.all; |
| |
| -- If the finalization of a particular object failed or Finalize_Address |
| -- was not set, reraise the exception now. |
| |
| if Raised then |
| Reraise_Occurrence (Ex_Occur); |
| end if; |
| end Finalize; |
| |
| ---------------------- |
| -- Finalize_Address -- |
| ---------------------- |
| |
| function Finalize_Address |
| (Master : Finalization_Master) return Finalize_Address_Ptr |
| is |
| begin |
| return Master.Finalize_Address; |
| end Finalize_Address; |
| |
| ---------------------------------- |
| -- Finalize_Address_Unprotected -- |
| ---------------------------------- |
| |
| function Finalize_Address_Unprotected |
| (Obj : System.Address) return Finalize_Address_Ptr |
| is |
| begin |
| return Finalize_Address_Table.Get (Obj); |
| end Finalize_Address_Unprotected; |
| |
| -------------------------- |
| -- Finalization_Started -- |
| -------------------------- |
| |
| function Finalization_Started |
| (Master : Finalization_Master) return Boolean |
| is |
| begin |
| return Master.Finalization_Started; |
| end Finalization_Started; |
| |
| ---------- |
| -- Hash -- |
| ---------- |
| |
| function Hash (Key : System.Address) return Header_Num is |
| begin |
| return |
| Header_Num |
| (To_Integer (Key) mod Integer_Address (Header_Num'Range_Length)); |
| end Hash; |
| |
| ----------------- |
| -- Header_Size -- |
| ----------------- |
| |
| function Header_Size return System.Storage_Elements.Storage_Count is |
| begin |
| return FM_Node'Size / Storage_Unit; |
| end Header_Size; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| overriding procedure Initialize (Master : in out Finalization_Master) is |
| begin |
| -- The dummy head must point to itself in both directions |
| |
| Master.Objects.Next := Master.Objects'Unchecked_Access; |
| Master.Objects.Prev := Master.Objects'Unchecked_Access; |
| end Initialize; |
| |
| -------------------- |
| -- Is_Homogeneous -- |
| -------------------- |
| |
| function Is_Homogeneous (Master : Finalization_Master) return Boolean is |
| begin |
| return Master.Is_Homogeneous; |
| end Is_Homogeneous; |
| |
| ------------- |
| -- Objects -- |
| ------------- |
| |
| function Objects (Master : Finalization_Master) return FM_Node_Ptr is |
| begin |
| return Master.Objects'Unrestricted_Access; |
| end Objects; |
| |
| ------------------ |
| -- Print_Master -- |
| ------------------ |
| |
| procedure Print_Master (Master : Finalization_Master) is |
| Head : constant FM_Node_Ptr := Master.Objects'Unrestricted_Access; |
| Head_Seen : Boolean := False; |
| N_Ptr : FM_Node_Ptr; |
| |
| begin |
| -- Output the basic contents of a master |
| |
| -- Master : 0x123456789 |
| -- Is_Hmgen : TURE <or> FALSE |
| -- Base_Pool: null <or> 0x123456789 |
| -- Fin_Addr : null <or> 0x123456789 |
| -- Fin_Start: TRUE <or> FALSE |
| |
| Put ("Master : "); |
| Put_Line (Address_Image (Master'Address)); |
| |
| Put ("Is_Hmgen : "); |
| Put_Line (Master.Is_Homogeneous'Img); |
| |
| Put ("Base_Pool: "); |
| if Master.Base_Pool = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (Master.Base_Pool'Address)); |
| end if; |
| |
| Put ("Fin_Addr : "); |
| if Master.Finalize_Address = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (Master.Finalize_Address'Address)); |
| end if; |
| |
| Put ("Fin_Start: "); |
| Put_Line (Master.Finalization_Started'Img); |
| |
| -- Output all chained elements. The format is the following: |
| |
| -- ^ <or> ? <or> null |
| -- |Header: 0x123456789 (dummy head) |
| -- | Prev: 0x123456789 |
| -- | Next: 0x123456789 |
| -- V |
| |
| -- ^ - the current element points back to the correct element |
| -- ? - the current element points back to an erroneous element |
| -- n - the current element points back to null |
| |
| -- Header - the address of the list header |
| -- Prev - the address of the list header which the current element |
| -- points back to |
| -- Next - the address of the list header which the current element |
| -- points to |
| -- (dummy head) - present if dummy head |
| |
| N_Ptr := Head; |
| while N_Ptr /= null loop -- Should never be null |
| Put_Line ("V"); |
| |
| -- We see the head initially; we want to exit when we see the head a |
| -- second time. |
| |
| if N_Ptr = Head then |
| exit when Head_Seen; |
| |
| Head_Seen := True; |
| end if; |
| |
| -- The current element is null. This should never happen since the |
| -- list is circular. |
| |
| if N_Ptr.Prev = null then |
| Put_Line ("null (ERROR)"); |
| |
| -- The current element points back to the correct element |
| |
| elsif N_Ptr.Prev.Next = N_Ptr then |
| Put_Line ("^"); |
| |
| -- The current element points to an erroneous element |
| |
| else |
| Put_Line ("? (ERROR)"); |
| end if; |
| |
| -- Output the header and fields |
| |
| Put ("|Header: "); |
| Put (Address_Image (N_Ptr.all'Address)); |
| |
| -- Detect the dummy head |
| |
| if N_Ptr = Head then |
| Put_Line (" (dummy head)"); |
| else |
| Put_Line (""); |
| end if; |
| |
| Put ("| Prev: "); |
| |
| if N_Ptr.Prev = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (N_Ptr.Prev.all'Address)); |
| end if; |
| |
| Put ("| Next: "); |
| |
| if N_Ptr.Next = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (N_Ptr.Next.all'Address)); |
| end if; |
| |
| N_Ptr := N_Ptr.Next; |
| end loop; |
| end Print_Master; |
| |
| ------------------- |
| -- Set_Base_Pool -- |
| ------------------- |
| |
| procedure Set_Base_Pool |
| (Master : in out Finalization_Master; |
| Pool_Ptr : Any_Storage_Pool_Ptr) |
| is |
| begin |
| Master.Base_Pool := Pool_Ptr; |
| end Set_Base_Pool; |
| |
| -------------------------- |
| -- Set_Finalize_Address -- |
| -------------------------- |
| |
| procedure Set_Finalize_Address |
| (Master : in out Finalization_Master; |
| Fin_Addr_Ptr : Finalize_Address_Ptr) |
| is |
| begin |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, outside |
| |
| Lock_Task.all; |
| Set_Finalize_Address_Unprotected (Master, Fin_Addr_Ptr); |
| Unlock_Task.all; |
| end Set_Finalize_Address; |
| |
| -------------------------------------- |
| -- Set_Finalize_Address_Unprotected -- |
| -------------------------------------- |
| |
| procedure Set_Finalize_Address_Unprotected |
| (Master : in out Finalization_Master; |
| Fin_Addr_Ptr : Finalize_Address_Ptr) |
| is |
| begin |
| if Master.Finalize_Address = null then |
| Master.Finalize_Address := Fin_Addr_Ptr; |
| end if; |
| end Set_Finalize_Address_Unprotected; |
| |
| ---------------------------------------------------- |
| -- Set_Heterogeneous_Finalize_Address_Unprotected -- |
| ---------------------------------------------------- |
| |
| procedure Set_Heterogeneous_Finalize_Address_Unprotected |
| (Obj : System.Address; |
| Fin_Addr_Ptr : Finalize_Address_Ptr) |
| is |
| begin |
| Finalize_Address_Table.Set (Obj, Fin_Addr_Ptr); |
| end Set_Heterogeneous_Finalize_Address_Unprotected; |
| |
| -------------------------- |
| -- Set_Is_Heterogeneous -- |
| -------------------------- |
| |
| procedure Set_Is_Heterogeneous (Master : in out Finalization_Master) is |
| begin |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - outside |
| |
| Lock_Task.all; |
| Master.Is_Homogeneous := False; |
| Unlock_Task.all; |
| end Set_Is_Heterogeneous; |
| |
| end System.Finalization_Masters; |