| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- S Y S T E M . S T O R A G E _ P O O L S . S U B P O O L S -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2011-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 Ada.Unchecked_Conversion; |
| |
| with System.Address_Image; |
| with System.Finalization_Masters; use System.Finalization_Masters; |
| with System.IO; use System.IO; |
| with System.Soft_Links; use System.Soft_Links; |
| with System.Storage_Elements; use System.Storage_Elements; |
| |
| with System.Storage_Pools.Subpools.Finalization; |
| use System.Storage_Pools.Subpools.Finalization; |
| |
| package body System.Storage_Pools.Subpools is |
| |
| Finalize_Address_Table_In_Use : Boolean := False; |
| -- This flag should be set only when a successful allocation on a subpool |
| -- has been performed and the associated Finalize_Address has been added to |
| -- the hash table in System.Finalization_Masters. |
| |
| function Address_To_FM_Node_Ptr is |
| new Ada.Unchecked_Conversion (Address, FM_Node_Ptr); |
| |
| procedure Attach (N : not null SP_Node_Ptr; L : not null SP_Node_Ptr); |
| -- Attach a subpool node to a pool |
| |
| ----------------------------------- |
| -- Adjust_Controlled_Dereference -- |
| ----------------------------------- |
| |
| procedure Adjust_Controlled_Dereference |
| (Addr : in out System.Address; |
| Storage_Size : in out System.Storage_Elements.Storage_Count; |
| Alignment : System.Storage_Elements.Storage_Count) |
| is |
| Header_And_Padding : constant Storage_Offset := |
| Header_Size_With_Padding (Alignment); |
| begin |
| -- Expose the two hidden pointers by shifting the address from the |
| -- start of the object to the FM_Node equivalent of the pointers. |
| |
| Addr := Addr - Header_And_Padding; |
| |
| -- Update the size of the object to include the two pointers |
| |
| Storage_Size := Storage_Size + Header_And_Padding; |
| end Adjust_Controlled_Dereference; |
| |
| -------------- |
| -- Allocate -- |
| -------------- |
| |
| overriding procedure Allocate |
| (Pool : in out Root_Storage_Pool_With_Subpools; |
| Storage_Address : out System.Address; |
| Size_In_Storage_Elements : System.Storage_Elements.Storage_Count; |
| Alignment : System.Storage_Elements.Storage_Count) |
| is |
| begin |
| -- Dispatch to the user-defined implementations of Allocate_From_Subpool |
| -- and Default_Subpool_For_Pool. |
| |
| Allocate_From_Subpool |
| (Root_Storage_Pool_With_Subpools'Class (Pool), |
| Storage_Address, |
| Size_In_Storage_Elements, |
| Alignment, |
| Default_Subpool_For_Pool |
| (Root_Storage_Pool_With_Subpools'Class (Pool))); |
| end Allocate; |
| |
| ----------------------------- |
| -- Allocate_Any_Controlled -- |
| ----------------------------- |
| |
| procedure Allocate_Any_Controlled |
| (Pool : in out Root_Storage_Pool'Class; |
| Context_Subpool : Subpool_Handle; |
| Context_Master : Finalization_Masters.Finalization_Master_Ptr; |
| Fin_Address : Finalization_Masters.Finalize_Address_Ptr; |
| Addr : out System.Address; |
| Storage_Size : System.Storage_Elements.Storage_Count; |
| Alignment : System.Storage_Elements.Storage_Count; |
| Is_Controlled : Boolean; |
| On_Subpool : Boolean) |
| is |
| Is_Subpool_Allocation : constant Boolean := |
| Pool in Root_Storage_Pool_With_Subpools'Class; |
| |
| Master : Finalization_Master_Ptr := null; |
| N_Addr : Address; |
| N_Ptr : FM_Node_Ptr; |
| N_Size : Storage_Count; |
| Subpool : Subpool_Handle := null; |
| Lock_Taken : Boolean := False; |
| |
| Header_And_Padding : Storage_Offset; |
| -- This offset includes the size of a FM_Node plus any additional |
| -- padding due to a larger alignment. |
| |
| begin |
| -- Step 1: Pool-related runtime checks |
| |
| -- Allocation on a pool_with_subpools. In this scenario there is a |
| -- master for each subpool. The master of the access type is ignored. |
| |
| if Is_Subpool_Allocation then |
| |
| -- Case of an allocation without a Subpool_Handle. Dispatch to the |
| -- implementation of Default_Subpool_For_Pool. |
| |
| if Context_Subpool = null then |
| Subpool := |
| Default_Subpool_For_Pool |
| (Root_Storage_Pool_With_Subpools'Class (Pool)); |
| |
| -- Allocation with a Subpool_Handle |
| |
| else |
| Subpool := Context_Subpool; |
| end if; |
| |
| -- Ensure proper ownership and chaining of the subpool |
| |
| if Subpool.Owner /= |
| Root_Storage_Pool_With_Subpools'Class (Pool)'Unchecked_Access |
| or else Subpool.Node = null |
| or else Subpool.Node.Prev = null |
| or else Subpool.Node.Next = null |
| then |
| raise Program_Error with "incorrect owner of subpool"; |
| end if; |
| |
| Master := Subpool.Master'Unchecked_Access; |
| |
| -- Allocation on a simple pool. In this scenario there is a master for |
| -- each access-to-controlled type. No context subpool should be present. |
| |
| else |
| -- If the master is missing, then the expansion of the access type |
| -- failed to create one. This is a compiler bug. |
| |
| pragma Assert |
| (Context_Master /= null, "missing master in pool allocation"); |
| |
| -- If a subpool is present, then this is the result of erroneous |
| -- allocator expansion. This is not a serious error, but it should |
| -- still be detected. |
| |
| if Context_Subpool /= null then |
| raise Program_Error |
| with "subpool not required in pool allocation"; |
| end if; |
| |
| -- If the allocation is intended to be on a subpool, but the access |
| -- type's pool does not support subpools, then this is the result of |
| -- incorrect end-user code. |
| |
| if On_Subpool then |
| raise Program_Error |
| with "pool of access type does not support subpools"; |
| end if; |
| |
| Master := Context_Master; |
| end if; |
| |
| -- Step 2: Master, Finalize_Address-related runtime checks and size |
| -- calculations. |
| |
| -- Allocation of a descendant from [Limited_]Controlled, a class-wide |
| -- object or a record with controlled components. |
| |
| if Is_Controlled then |
| |
| -- Synchronization: |
| -- Read - allocation, finalization |
| -- Write - finalization |
| |
| Lock_Taken := True; |
| Lock_Task.all; |
| |
| -- Do not allow the allocation of controlled objects while the |
| -- associated master is being finalized. |
| |
| if Finalization_Started (Master.all) then |
| raise Program_Error with "allocation after finalization started"; |
| end if; |
| |
| -- Check whether primitive Finalize_Address is available. If it is |
| -- not, then either the expansion of the designated type failed or |
| -- the expansion of the allocator failed. This is a compiler bug. |
| |
| pragma Assert |
| (Fin_Address /= null, "primitive Finalize_Address not available"); |
| |
| -- The size must account for the hidden header preceding the object. |
| -- Account for possible padding space before the header due to a |
| -- larger alignment. |
| |
| Header_And_Padding := Header_Size_With_Padding (Alignment); |
| |
| N_Size := Storage_Size + Header_And_Padding; |
| |
| -- Non-controlled allocation |
| |
| else |
| N_Size := Storage_Size; |
| end if; |
| |
| -- Step 3: Allocation of object |
| |
| -- For descendants of Root_Storage_Pool_With_Subpools, dispatch to the |
| -- implementation of Allocate_From_Subpool. |
| |
| if Is_Subpool_Allocation then |
| Allocate_From_Subpool |
| (Root_Storage_Pool_With_Subpools'Class (Pool), |
| N_Addr, N_Size, Alignment, Subpool); |
| |
| -- For descendants of Root_Storage_Pool, dispatch to the implementation |
| -- of Allocate. |
| |
| else |
| Allocate (Pool, N_Addr, N_Size, Alignment); |
| end if; |
| |
| -- Step 4: Attachment |
| |
| if Is_Controlled then |
| |
| -- Note that we already did "Lock_Task.all;" in Step 2 above |
| |
| -- Map the allocated memory into a FM_Node record. This converts the |
| -- top of the allocated bits into a list header. If there is padding |
| -- due to larger alignment, the header is placed right next to the |
| -- object: |
| |
| -- N_Addr N_Ptr |
| -- | | |
| -- V V |
| -- +-------+---------------+----------------------+ |
| -- |Padding| Header | Object | |
| -- +-------+---------------+----------------------+ |
| -- ^ ^ ^ |
| -- | +- Header_Size -+ |
| -- | | |
| -- +- Header_And_Padding --+ |
| |
| N_Ptr := |
| Address_To_FM_Node_Ptr (N_Addr + Header_And_Padding - Header_Size); |
| |
| -- Prepend the allocated object to the finalization master |
| |
| -- Synchronization: |
| -- Write - allocation, deallocation, finalization |
| |
| Attach_Unprotected (N_Ptr, Objects (Master.all)); |
| |
| -- Move the address from the hidden list header to the start of the |
| -- object. This operation effectively hides the list header. |
| |
| Addr := N_Addr + Header_And_Padding; |
| |
| -- Homogeneous masters service the following: |
| |
| -- 1) Allocations on / Deallocations from regular pools |
| -- 2) Named access types |
| -- 3) Most cases of anonymous access types usage |
| |
| -- Synchronization: |
| -- Read - allocation, finalization |
| -- Write - outside |
| |
| if Master.Is_Homogeneous then |
| |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, outside |
| |
| Set_Finalize_Address_Unprotected (Master.all, Fin_Address); |
| |
| -- Heterogeneous masters service the following: |
| |
| -- 1) Allocations on / Deallocations from subpools |
| -- 2) Certain cases of anonymous access types usage |
| |
| else |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, deallocation |
| |
| Set_Heterogeneous_Finalize_Address_Unprotected (Addr, Fin_Address); |
| Finalize_Address_Table_In_Use := True; |
| end if; |
| |
| Unlock_Task.all; |
| Lock_Taken := False; |
| |
| -- Non-controlled allocation |
| |
| else |
| Addr := N_Addr; |
| end if; |
| |
| exception |
| when others => |
| |
| -- Unlock the task in case the allocation step failed and reraise the |
| -- exception. |
| |
| if Lock_Taken then |
| Unlock_Task.all; |
| end if; |
| |
| raise; |
| end Allocate_Any_Controlled; |
| |
| ------------ |
| -- Attach -- |
| ------------ |
| |
| procedure Attach (N : not null SP_Node_Ptr; L : not null SP_Node_Ptr) is |
| begin |
| -- Ensure that the node has not been attached already |
| |
| pragma Assert (N.Prev = null and then N.Next = null); |
| |
| Lock_Task.all; |
| |
| L.Next.Prev := N; |
| N.Next := L.Next; |
| L.Next := N; |
| N.Prev := L; |
| |
| Unlock_Task.all; |
| |
| -- Note: No need to unlock in case of an exception because the above |
| -- code can never raise one. |
| end Attach; |
| |
| ------------------------------- |
| -- Deallocate_Any_Controlled -- |
| ------------------------------- |
| |
| procedure Deallocate_Any_Controlled |
| (Pool : in out Root_Storage_Pool'Class; |
| Addr : System.Address; |
| Storage_Size : System.Storage_Elements.Storage_Count; |
| Alignment : System.Storage_Elements.Storage_Count; |
| Is_Controlled : Boolean) |
| is |
| N_Addr : Address; |
| N_Ptr : FM_Node_Ptr; |
| N_Size : Storage_Count; |
| |
| Header_And_Padding : Storage_Offset; |
| -- This offset includes the size of a FM_Node plus any additional |
| -- padding due to a larger alignment. |
| |
| begin |
| -- Step 1: Detachment |
| |
| if Is_Controlled then |
| Lock_Task.all; |
| |
| begin |
| -- Destroy the relation pair object - Finalize_Address since it is |
| -- no longer needed. |
| |
| if Finalize_Address_Table_In_Use then |
| |
| -- Synchronization: |
| -- Read - finalization |
| -- Write - allocation, deallocation |
| |
| Delete_Finalize_Address_Unprotected (Addr); |
| end if; |
| |
| -- Account for possible padding space before the header due to a |
| -- larger alignment. |
| |
| Header_And_Padding := Header_Size_With_Padding (Alignment); |
| |
| -- N_Addr N_Ptr Addr (from input) |
| -- | | | |
| -- V V V |
| -- +-------+---------------+----------------------+ |
| -- |Padding| Header | Object | |
| -- +-------+---------------+----------------------+ |
| -- ^ ^ ^ |
| -- | +- Header_Size -+ |
| -- | | |
| -- +- Header_And_Padding --+ |
| |
| -- Convert the bits preceding the object into a list header |
| |
| N_Ptr := Address_To_FM_Node_Ptr (Addr - Header_Size); |
| |
| -- Detach the object from the related finalization master. This |
| -- action does not need to know the prior context used during |
| -- allocation. |
| |
| -- Synchronization: |
| -- Write - allocation, deallocation, finalization |
| |
| Detach_Unprotected (N_Ptr); |
| |
| -- Move the address from the object to the beginning of the list |
| -- header. |
| |
| N_Addr := Addr - Header_And_Padding; |
| |
| -- The size of the deallocated object must include the size of the |
| -- hidden list header. |
| |
| N_Size := Storage_Size + Header_And_Padding; |
| |
| Unlock_Task.all; |
| |
| exception |
| when others => |
| |
| -- Unlock the task in case the computations performed above |
| -- fail for some reason. |
| |
| Unlock_Task.all; |
| raise; |
| end; |
| else |
| N_Addr := Addr; |
| N_Size := Storage_Size; |
| end if; |
| |
| -- Step 2: Deallocation |
| |
| -- Dispatch to the proper implementation of Deallocate. This action |
| -- covers both Root_Storage_Pool and Root_Storage_Pool_With_Subpools |
| -- implementations. |
| |
| Deallocate (Pool, N_Addr, N_Size, Alignment); |
| end Deallocate_Any_Controlled; |
| |
| ------------------------------ |
| -- Default_Subpool_For_Pool -- |
| ------------------------------ |
| |
| function Default_Subpool_For_Pool |
| (Pool : in out Root_Storage_Pool_With_Subpools) |
| return not null Subpool_Handle |
| is |
| pragma Unreferenced (Pool); |
| begin |
| return raise Program_Error with |
| "default Default_Subpool_For_Pool called; must be overridden"; |
| end Default_Subpool_For_Pool; |
| |
| ------------ |
| -- Detach -- |
| ------------ |
| |
| procedure Detach (N : not null SP_Node_Ptr) is |
| begin |
| -- Ensure that the node is attached to some list |
| |
| pragma Assert (N.Next /= null and then N.Prev /= null); |
| |
| Lock_Task.all; |
| |
| N.Prev.Next := N.Next; |
| N.Next.Prev := N.Prev; |
| N.Prev := null; |
| N.Next := null; |
| |
| Unlock_Task.all; |
| |
| -- Note: No need to unlock in case of an exception because the above |
| -- code can never raise one. |
| end Detach; |
| |
| -------------- |
| -- Finalize -- |
| -------------- |
| |
| overriding procedure Finalize (Controller : in out Pool_Controller) is |
| begin |
| Finalize_Pool (Controller.Enclosing_Pool.all); |
| end Finalize; |
| |
| ------------------- |
| -- Finalize_Pool -- |
| ------------------- |
| |
| procedure Finalize_Pool (Pool : in out Root_Storage_Pool_With_Subpools) is |
| Curr_Ptr : SP_Node_Ptr; |
| Ex_Occur : Exception_Occurrence; |
| Raised : Boolean := False; |
| |
| function Is_Empty_List (L : not null SP_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 SP_Node_Ptr) return Boolean is |
| begin |
| return L.Next = L and then L.Prev = L; |
| end Is_Empty_List; |
| |
| -- Start of processing for Finalize_Pool |
| |
| begin |
| -- It is possible for multiple tasks to cause the finalization of a |
| -- common pool. Allow only one task to finalize the contents. |
| |
| if Pool.Finalization_Started then |
| return; |
| end if; |
| |
| -- Lock the pool to prevent the creation of additional subpools while |
| -- the available ones are finalized. The pool remains locked because |
| -- either it is about to be deallocated or the associated access type |
| -- is about to go out of scope. |
| |
| Pool.Finalization_Started := True; |
| |
| while not Is_Empty_List (Pool.Subpools'Unchecked_Access) loop |
| Curr_Ptr := Pool.Subpools.Next; |
| |
| -- Perform the following actions: |
| |
| -- 1) Finalize all objects chained on the subpool's master |
| -- 2) Remove the subpool from the owner's list of subpools |
| -- 3) Deallocate the doubly linked list node associated with the |
| -- subpool. |
| -- 4) Call Deallocate_Subpool |
| |
| begin |
| Finalize_And_Deallocate (Curr_Ptr.Subpool); |
| |
| exception |
| when Fin_Occur : others => |
| if not Raised then |
| Raised := True; |
| Save_Occurrence (Ex_Occur, Fin_Occur); |
| end if; |
| end; |
| end loop; |
| |
| -- If the finalization of a particular master failed, reraise the |
| -- exception now. |
| |
| if Raised then |
| Reraise_Occurrence (Ex_Occur); |
| end if; |
| end Finalize_Pool; |
| |
| ------------------------------ |
| -- Header_Size_With_Padding -- |
| ------------------------------ |
| |
| function Header_Size_With_Padding |
| (Alignment : System.Storage_Elements.Storage_Count) |
| return System.Storage_Elements.Storage_Count |
| is |
| Size : constant Storage_Count := Header_Size; |
| |
| begin |
| if Size mod Alignment = 0 then |
| return Size; |
| |
| -- Add enough padding to reach the nearest multiple of the alignment |
| -- rounding up. |
| |
| else |
| return ((Size + Alignment - 1) / Alignment) * Alignment; |
| end if; |
| end Header_Size_With_Padding; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| overriding procedure Initialize (Controller : in out Pool_Controller) is |
| begin |
| Initialize_Pool (Controller.Enclosing_Pool.all); |
| end Initialize; |
| |
| --------------------- |
| -- Initialize_Pool -- |
| --------------------- |
| |
| procedure Initialize_Pool (Pool : in out Root_Storage_Pool_With_Subpools) is |
| begin |
| -- The dummy head must point to itself in both directions |
| |
| Pool.Subpools.Next := Pool.Subpools'Unchecked_Access; |
| Pool.Subpools.Prev := Pool.Subpools'Unchecked_Access; |
| end Initialize_Pool; |
| |
| --------------------- |
| -- Pool_Of_Subpool -- |
| --------------------- |
| |
| function Pool_Of_Subpool |
| (Subpool : not null Subpool_Handle) |
| return access Root_Storage_Pool_With_Subpools'Class |
| is |
| begin |
| return Subpool.Owner; |
| end Pool_Of_Subpool; |
| |
| ---------------- |
| -- Print_Pool -- |
| ---------------- |
| |
| procedure Print_Pool (Pool : Root_Storage_Pool_With_Subpools) is |
| Head : constant SP_Node_Ptr := Pool.Subpools'Unrestricted_Access; |
| Head_Seen : Boolean := False; |
| SP_Ptr : SP_Node_Ptr; |
| |
| begin |
| -- Output the contents of the pool |
| |
| -- Pool : 0x123456789 |
| -- Subpools : 0x123456789 |
| -- Fin_Start : TRUE <or> FALSE |
| -- Controller: OK <or> NOK |
| |
| Put ("Pool : "); |
| Put_Line (Address_Image (Pool'Address)); |
| |
| Put ("Subpools : "); |
| Put_Line (Address_Image (Pool.Subpools'Address)); |
| |
| Put ("Fin_Start : "); |
| Put_Line (Pool.Finalization_Started'Img); |
| |
| Put ("Controlled: "); |
| if Pool.Controller.Enclosing_Pool = Pool'Unrestricted_Access then |
| Put_Line ("OK"); |
| else |
| Put_Line ("NOK (ERROR)"); |
| end if; |
| |
| SP_Ptr := Head; |
| while SP_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 SP_Ptr = Head then |
| exit when Head_Seen; |
| |
| Head_Seen := True; |
| end if; |
| |
| -- The current element is null. This should never happend since the |
| -- list is circular. |
| |
| if SP_Ptr.Prev = null then |
| Put_Line ("null (ERROR)"); |
| |
| -- The current element points back to the correct element |
| |
| elsif SP_Ptr.Prev.Next = SP_Ptr then |
| Put_Line ("^"); |
| |
| -- The current element points to an erroneous element |
| |
| else |
| Put_Line ("? (ERROR)"); |
| end if; |
| |
| -- Output the contents of the node |
| |
| Put ("|Header: "); |
| Put (Address_Image (SP_Ptr.all'Address)); |
| if SP_Ptr = Head then |
| Put_Line (" (dummy head)"); |
| else |
| Put_Line (""); |
| end if; |
| |
| Put ("| Prev: "); |
| |
| if SP_Ptr.Prev = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (SP_Ptr.Prev.all'Address)); |
| end if; |
| |
| Put ("| Next: "); |
| |
| if SP_Ptr.Next = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (SP_Ptr.Next.all'Address)); |
| end if; |
| |
| Put ("| Subp: "); |
| |
| if SP_Ptr.Subpool = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (SP_Ptr.Subpool.all'Address)); |
| end if; |
| |
| SP_Ptr := SP_Ptr.Next; |
| end loop; |
| end Print_Pool; |
| |
| ------------------- |
| -- Print_Subpool -- |
| ------------------- |
| |
| procedure Print_Subpool (Subpool : Subpool_Handle) is |
| begin |
| if Subpool = null then |
| Put_Line ("null"); |
| return; |
| end if; |
| |
| -- Output the contents of a subpool |
| |
| -- Owner : 0x123456789 |
| -- Master: 0x123456789 |
| -- Node : 0x123456789 |
| |
| Put ("Owner : "); |
| if Subpool.Owner = null then |
| Put_Line ("null"); |
| else |
| Put_Line (Address_Image (Subpool.Owner'Address)); |
| end if; |
| |
| Put ("Master: "); |
| Put_Line (Address_Image (Subpool.Master'Address)); |
| |
| Put ("Node : "); |
| if Subpool.Node = null then |
| Put ("null"); |
| |
| if Subpool.Owner = null then |
| Put_Line (" OK"); |
| else |
| Put_Line (" (ERROR)"); |
| end if; |
| else |
| Put_Line (Address_Image (Subpool.Node'Address)); |
| end if; |
| |
| Print_Master (Subpool.Master); |
| end Print_Subpool; |
| |
| ------------------------- |
| -- Set_Pool_Of_Subpool -- |
| ------------------------- |
| |
| procedure Set_Pool_Of_Subpool |
| (Subpool : not null Subpool_Handle; |
| To : in out Root_Storage_Pool_With_Subpools'Class) |
| is |
| N_Ptr : SP_Node_Ptr; |
| |
| begin |
| -- If the subpool is already owned, raise Program_Error. This is a |
| -- direct violation of the RM rules. |
| |
| if Subpool.Owner /= null then |
| raise Program_Error with "subpool already belongs to a pool"; |
| end if; |
| |
| -- Prevent the creation of a new subpool while the owner is being |
| -- finalized. This is a serious error. |
| |
| if To.Finalization_Started then |
| raise Program_Error |
| with "subpool creation after finalization started"; |
| end if; |
| |
| Subpool.Owner := To'Unchecked_Access; |
| |
| -- Create a subpool node and decorate it. Since this node is not |
| -- allocated on the owner's pool, it must be explicitly destroyed by |
| -- Finalize_And_Detach. |
| |
| N_Ptr := new SP_Node; |
| N_Ptr.Subpool := Subpool; |
| Subpool.Node := N_Ptr; |
| |
| Attach (N_Ptr, To.Subpools'Unchecked_Access); |
| |
| -- Mark the subpool's master as being a heterogeneous collection of |
| -- controlled objects. |
| |
| Set_Is_Heterogeneous (Subpool.Master); |
| end Set_Pool_Of_Subpool; |
| |
| end System.Storage_Pools.Subpools; |