|
|
DataMuseum.dkPresents historical artifacts from the history of: Rational R1000/400 Tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Rational R1000/400 Tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: B T
Length: 56294 (0xdbe6)
Types: TextFile
Names: »B«
└─⟦afbc8121e⟧ Bits:30000532 8mm tape, Rational 1000, MC68020_OS2000 7_2_2
└─⟦77aa8350c⟧ »DATA«
└─⟦f794ecd1d⟧
└─⟦24d1ddd49⟧
└─⟦this⟧
-- The use of this system is subject to the software license terms and
-- conditions agreed upon between Rational and the Customer.
--
-- Copyright 1988 by Rational.
--
-- RESTRICTED RIGHTS LEGEND
--
-- Use, duplication, or disclosure by the Government is subject to
-- restrictions as set forth in subdivision (b)(3)(ii) of the Rights in
-- Technical Data and Computer Software clause at 52.227-7013.
--
--
-- Rational
-- 3320 Scott Boulevard
-- Santa Clara, California 95054-3197
--
-- PROPRIETARY AND CONFIDENTIAL INFORMATION OF RATIONAL;
-- USE OR COPYING WITHOUT EXPRESS WRITTEN AUTHORIZATION
-- IS STRICTLY PROHIBITED. THIS MATERIAL IS PROTECTED AS
-- AN UNPUBLISHED WORK UNDER THE U.S. COPYRIGHT ACT OF
-- 1976. CREATED 1988, 1989. ALL RIGHTS RESERVED.
--
--
with Debug_Io;
with Semaphore;
with Exceptions;
with Heap_Management;
with Debug_Definitions;
with System;
with System_Definitions;
with Unchecked_Conversion;
-- SOME BACKGROUND.
--
-- Given the following Ada declarations:
-- type T (Variant : Natural) is
-- record
-- F1 : String (1 .. Variant);
-- F2 : ...
-- end record;
--
-- type Tp is access T;
--
-- P : Tp;
--
-- The compiler will access a variable which corresponds to the
-- collection for accessor Tp. During the elaboration of
-- type Tp, the following runtime call will be made:
--
-- local_var_tp := Allocate_Collection (Collection_Size, Extend_Collection);
--
-- If Collection_Size is 0, a default size will be used. If Extend_Collection
-- is False, then the initial collection will not be allowed to be extended.
-- A collection looks like:
--
-- local_var_tp -> Extendible (Boolean) \ \
-- Chunk_List (Collection_Link) | |
-- Units_Free (Integer) } Collection |
-- Free_List (Fragment_Link) | header |
-- Chunk_Size (Integer) | } a "chunk"
-- Lock (Semaphore) / | or a
-- <Collection_Size bytes> | collection
-- <4 bytes> /
--
-- Note that this structure differs from that used in other targets (more
-- on that later.) If a collection is allowed to be extendible, then
-- each extension results in a structure like above, and they are all
-- linked together via the Chunk_List field. A single allocation, like
-- variable P in the example, must be completely contained in a single
-- collection, or chunk - an allocation cannot span 2 or more chunks.
--
-- Other products use a simpler collection header. They do not include
-- a Units_Free field, which is presently used to speed up the allocation
-- of new objects in the collection, and they do not include a Lock field,
-- which is necessary when Ada tasks are implemented as real OS processes.
--
-- Within each chunk, there is a list (possibly empty) of free "fragments"
-- of memory. Each fragment is at least 8 bytes long and contains a
-- pointer to the next fragment in the list and a byte count of the
-- number of bytes which are free in the fragment (not including the 8 bytes
-- used to contain this fragment header). Since fragments must be at least
-- eight bytes long, the minimum size of an object which can be allocated
-- on the heap is 8 bytes. A request to allocate, say a 3 byte object,
-- would result in 8 bytes being allocated. This is so that if the 3 byte
-- object were to be deallocated later the object's allocation would be
-- large enough to contain a fragment descriptor so that the fragment could
-- be added to the list of free fragments. For each chunk, the list of
-- free fragments is rooted in the chunk's Free_List field. This is an
-- ordered list, sorted by increasing fragment address, so that the
-- coalesce algorithm can be small and have a linear time complexity.
-- For architectural efficiency reasons, each allocation is placed on an
-- 8 byte address boundary. Thus not only is an allocation made to be
-- at least 8 bytes long, it is also made to be a 8 byte multiple in
-- length. Thus a request to allocate a 13 byte object will actually
-- result in 16 bytes being allocated for the object.
--
-- OPTIMIZATION CANDIDATE:
-- The code below may operate slightly quicker if the "size of fragment"
-- field in the fragment header actually included the 8 bytes contained
-- in the fragment header. There is some amount of unnecessary adding
-- and subtracting of this constant 8 in the algorithms below.
--
-- If our sample program now contains the statement:
--
-- P := new T (13);
--
-- the following runtime call will be made:
--
-- P := Allocate_Fixed_Cell (13 + fixed_record_size, local_var_tp);
--
-- This implementation then searches each chunk in the chunk list rooted
-- in local_var_tp for a chunk whose Units_Free is greater than or equal
-- to the "13 + fixed_record_size" expression. If no such chunk is found,
-- the collection will be extended if allowed and possible. Once a possible
-- chunk is found, the free list is searched for a fragment large enough
-- to contain the allocation. If not is found, the search continues with
-- the next chunk in the list. Once a fragment is found, one of two
-- possibilities exist. If the fragment is 8 or more bytes bigger than
-- the requested allocation, then the fragment is split into two new fragments.
-- One of the new fragments is made just large enought to contain the
-- allocation and P will be made to point to it, and the other fragment will
-- be returned to the free list for the chunk. If the original fragment is
-- not 8 or more bytes larger than the requested allocation, the whole
-- fragment is used for the allocation - that is because there would not
-- be enough room in the origianl fragment to both grant the requested
-- allocation and to form a new fragment header so that the remaining bytes
-- could be returned to the free list.
--
-- For algorithmic efficiency reasons, when a fragment is split, the allocation
-- is given from the end of the fragment and the new fragment header which
-- describes the remaining unused bytes in the original fragment is kept
-- in the same location as the original fragment header. That way no
-- ajustments need be made in the linked list of fragments.
--
-- This implementation does allocations slightly differently than in some
-- of our other products. Each request is actually allocated 4 bytes
-- more than requested. The extra 4 bytes contains the length (not including
-- the 4 bytes used to hold the length) of the allocation. Note that this
-- is the adjusted allocation, not necessarily the original allocation.
-- This adjustment accounts for the 8 byte minimum and 8 byte multiple
-- requirements as described above. This length is used as an extra
-- sanity or security check if the object is disposed later. When an object
-- is to be disposed, both a pointer to the object and the unadjusted length
-- of the object is passed to the deallocator. The deallocator forms an
-- adjusted object length and checks that the length associated with the
-- actual allocated object matches this saved length. If P were to be
-- allocated simply 13 bytes, then P might contain, say, 16#7B27C# and
-- would look like:
--
-- 7B278: 00000010
-- P = 16#7b27C# -> 7B27C: xxxxxxxx
-- 7B780: xxxxxxxx
-- 7B784: xxxxxxxx
-- 7B788: xxxxxxxx
-- 7B78C: ........ (to make 8 byte multiple)
--
-- See that P points to a block of memory which can hold at least the requested
-- 13 bytes. It actually can hold 16 bytes, which is the next 8 byte multiple
-- value greater or equal to "min (13, 8)". As such, the 4 byte length value
-- stored at an address which is 4 bytes less than that pointed at by P
-- contains the value 16 (or 16#00000010#). If the program ever attempts to
-- deallocate P, the deallocator will apply the same adjustment algorithm to
-- the 13 value, yielding 16, and check that the 4 byte integer located 4
-- bytes lower than P contains 16. If not, something is seriously wrong
-- and a Storage_Error exception will be raised.
--
-- The last bit of background needed for understanding this package is to
-- know that when objects are disposed, the free list needs to be coalesced.
-- This involves merging adjacent free fragments into one larger free fragment.
-- If this is not done, it is likely that after some number of allocations
-- and deallocations that the next allocation request could fail, thinking
-- that there is no free fragment big enough to contain the requested
-- allocation, when in fact the whole heap might be free, consisting of
-- of large number of tiny fragments.
--
package body Storage_Management is
pragma Suppress (Access_Check);
pragma Suppress (Storage_Check);
use System_Definitions.Operators;
Byte : constant := System_Definitions.Byte_Size;
Byte_Length : constant := Byte * System_Definitions.Unit_Size;
Word : constant := System_Definitions.Word;
Word_Length : constant := System_Definitions.Word_Length;
Debug_Mode : Boolean renames Debug_Definitions.Debug_Storage_Management;
Safety_Check : Boolean renames Debug_Definitions.Safety_Check;
Chunk_Size_Divisor : constant := 8;
Default_Chunk_Size : constant := System_Definitions.Minimum_Block_Size;
--pragma Assert (Default_Chunk_Size mod Chunk_Size_Divisor = 0);
Boolean_Size : constant := System_Definitions.Boolean_Size;
Pointer_Size : constant := System_Definitions.Pointer_Size;
Integer_Size : constant := System_Definitions.Integer_Size;
type Fragment;
type Fragment_Link is access Fragment;
for Fragment_Link'Storage_Size use 0;
-- System_Definitions.Fake_Collection_Storage_Size;
type Fragment is
record
Next : Fragment_Link;
Size : Integer;
end record;
for Fragment use
record
Next at 0 * Word range 0 .. Word_Length - 1;
Size at 1 * Word range 0 .. Word_Length - 1;
end record;
--for Fragment'Size use 2 * Length_Word;
Fragment_Descriptor_Size : constant := Pointer_Size + Integer_Size;
-- pragma Assert (Fragment_Descriptor_Size mod Chunk_Size_Divisor = 0);
type Collection;
type Collection_Link is access Collection;
for Collection_Link'Storage_Size use
0; -- System_Definitions.Fake_Collection_Storage_Size;
type Collection is
record
Attributes : Collection_Attributes;
Free_List : Fragment_Link;
Chunk_Size : Integer;
Units_Free : Integer;
Chunk_List : Collection_Link;
Lock : Semaphore.Lock;
Extension_Size : Integer;
end record;
for Collection use
record at mod 4;
Attributes at 0 * Word range 0 .. Byte_Length - 1; -- 3 bytes filler
Chunk_List at 1 * Word range 0 .. Word_Length - 1;
Units_Free at 2 * Word range 0 .. Word_Length - 1;
Free_List at 3 * Word range 0 .. Word_Length - 1;
Chunk_Size at 4 * Word range 0 .. Word_Length - 1;
Lock at 5 * Word range 0 .. 2 * Word_Length - 1;
Extension_Size at 7 * Word range 0 .. Word_Length - 1;
end record;
--for Collection'Size use 8 * Word_Length;
Collection_Descriptor_Size : constant :=
5 * Integer_Size + 3 * Pointer_Size;
-- pragma Assert (Collection_Descriptor_Size mod Chunk_Size_Divisor = 0);
type Cell;
type Cell_Link is access Cell;
for Cell_Link'Storage_Size use
0; -- System_Definitions.Fake_Collection_Storage_Size;
type Cell is
record
Size : Integer;
end record;
for Cell use
record
Size at 0 * Word range 0 .. Word_Length - 1;
end record;
--for Cell'Size use 1 * Word_Length;
Cell_Descriptor_Size : constant := Integer_Size;
Free_Fragment_List : Fragment_Link;
pragma Import_Object (Free_Fragment_List, "__STORAGE_MGMT_FREELIST");
Storage_Lock : Semaphore.Lock;
pragma Import_Object (Storage_Lock, "__STORAGE_MGMT_SEMAPHORE");
function Address is new Unchecked_Conversion (Source => Fragment_Link,
Target => System_Address);
function Value is new Unchecked_Conversion (Source => System_Address,
Target => Fragment_Link);
function Address is new Unchecked_Conversion (Source => Collection_Link,
Target => System_Address);
function Value is new Unchecked_Conversion (Source => System_Address,
Target => Collection_Link);
function Address is new Unchecked_Conversion
(Source => Cell_Link, Target => System_Address);
function Value is new Unchecked_Conversion
(Source => System_Address, Target => Cell_Link);
-- package Debugging is
--
-- procedure Put_Fragment_List (Header : String; Start : Fragment_Link);
-- pragma Suppress (Elaboration_Check, Put_Fragment_List);
--
-- procedure Put_Collection_List
-- (Header : String; Start : Collection_Link);
-- pragma Suppress (Elaboration_Check, Put_Collection_List);
--
-- procedure Put_Cell_Info (Header : String;
-- Item : Cell_Link;
-- Clctn : Collection_Link);
-- pragma Suppress (Elaboration_Check, Put_Cell_Info);
--
-- procedure Put_Message (Msg : String);
-- pragma Suppress (Elaboration_Check, Put_Message);
--
-- end Debugging;
package Utilities is
function Align_Address (A : System_Address) return System_Address;
pragma Interface (Asm, Align_Address);
pragma Suppress (Elaboration_Check, Align_Address);
pragma Import_Function
(Align_Address, "__HEAP_ALIGN_VALUE", Mechanism => (Value));
function Align_Integer (I : Integer) return Integer;
pragma Interface (Asm, Align_Integer);
pragma Suppress (Elaboration_Check, Align_Integer);
pragma Import_Function
(Align_Integer, "__HEAP_ALIGN_VALUE", Mechanism => (Value));
end Utilities;
function Align (I : Integer) return Integer renames Utilities.Align_Integer;
function Align (A : System_Address) return System_Address
renames Utilities.Align_Address;
function "<" (L, R : Fragment_Link) return Boolean is
pragma Routine_Number (Runtime_Ids.Internal);
begin
return System."<" (Address (L), Address (R));
end "<";
function Coalescible (Collection : Collection_Link) return Boolean is
pragma Routine_Number (Runtime_Ids.Internal);
begin
return ((Collection.Attributes = Coalesce_And_No_Extend) or
(Collection.Attributes = Coalesce_And_Extend));
end Coalescible;
function Extendible (Attributes : Collection_Attributes) return Boolean is
pragma Routine_Number (Runtime_Ids.Internal);
begin
return ((Attributes = Extend_And_No_Coalesce) or
(Attributes = Coalesce_And_Extend));
end Extendible;
function Extendible (Collection : Collection_Link) return Boolean is
pragma Routine_Number (Runtime_Ids.Internal);
begin
return Extendible (Attributes => Collection.Attributes);
end Extendible;
-- function Align (I : Integer) return Integer is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- Result : Integer := I;
-- begin
-- if I mod Chunk_Size_Divisor /= 0 then
-- Result := I + (Chunk_Size_Divisor - I mod Chunk_Size_Divisor);
-- end if;
-- if Debug_Mode then
-- Debugging.Put_Message (Integer'Image (I) & " aligns to " &
-- Integer'Image (Result));
-- end if;
-- return Result;
-- end Align;
--
-- function Align (A : System_Address) return System_Address is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- begin
-- return To_Address (Align (To_Integer (A)));
-- end Align;
--
-- procedure Assert_Aligned (A : System_Address) is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- begin
-- if To_Integer (A) mod Chunk_Size_Divisor /= 0 then
-- Debugging.Put_Message ("*** Storage Management alignment error");
-- end if;
-- end Assert_Aligned;
\f
-- Allocate a segment of memory from the heap for use in creating
-- a collection.
--
-- This routine is NOT used to allocate user level objects, only
-- "chunks" of memory which contain a collection. The Units
-- parameter is assumed to have already been adjusted as necessary.
-- The allocation is merely added to the front of the free list of
-- fragments, rooted in Free_Fragment_List. The caller must remove
-- the segment from that list.
--
-- Exceptions:
-- o If unable to allocated "Units" bytes of memory, Storage_Error
-- will be raised.
--
-- Assertions:
-- o Free_Fragment_List points to a (possibly empty) free list of
-- fragments.
-- o Storage_Lock semaphore has been aquired before invoking this code.
--
-- Note: this routine is designed to be called by only Allocate_Collection
-- which has aquired Storage_Lock before calling here. This routine
-- must release Storage_Lock before raising any exceptions.
--
procedure New_Segment (Units : Integer) is
pragma Routine_Number (Runtime_Ids.Internal);
Allocated : System_Address;
New_Fragment : Fragment_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered New_Segment: units = " &
-- Integer'Image (Units));
-- end if;
Allocated := Heap_Management.Allocate (Units);
if To_Integer (Allocated) = 0 then
Semaphore.Release (Storage_Lock);
raise Storage_Error;
end if;
New_Fragment := Value (Allocated);
New_Fragment.Next := Free_Fragment_List;
New_Fragment.Size := Units - Fragment_Descriptor_Size;
Free_Fragment_List := New_Fragment;
end New_Segment;
-- Find the FIRST fragment in the free fragment list rooted in Free_List
-- which contains at least Size bytes.
--
-- Searches the fragment list for a first fit. If a large enough fragment
-- is found, one of two actions takes place:
-- o If the fragment is larger than Size bytes by at least the size
-- of a fragment descriptor, the fragment is split in two. One
-- fragment is made exactly Size bytes long and is returned. The
-- other fragment is the original fragment size minus Size bytes and
-- is left on the free fragment list. Note that the free portion is
-- left on the original free list so no pointers need to be adjusted.
-- This means that the allocated portion is taken from the end of
-- the original fragment.
-- o If the fragment is exactly large enough to contain Size, or if
-- it has fewer extra bytes than needed to contain a fragment
-- descriptor, then the whole fragment is removed from the free list
-- and returned as the allocation.
-- If no fragment on the list is large enough to contain Size bytes,
-- NULL is returned.
--
-- Exceptions:
-- none.
--
-- Assertions:
-- The list of free fragments, rooted in Free_List is non circular and
-- terminates with NULL.
--
-- Note:
-- o The size field in a fragment header does not include the byte
-- count required to contain the fragment header itself. So, for
-- instance, if a fragment were exactly the size of a fragment
-- header (this is the minimum size of any possible entry in
-- the list), the size field for that element would be zero.
--
procedure Find_First_Spare (Size : Integer;
Free_List : in out Fragment_Link;
Item_Found : out Fragment_Link) is
pragma Routine_Number (Runtime_Ids.Internal);
Next_Spare : Fragment_Link := Free_List;
Prev_Spare : Fragment_Link := null;
Units_Remain : Integer;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Find_First_Spare: size = " &
-- Integer'Image (Size));
-- end if;
while Next_Spare /= null loop
Units_Remain := (Next_Spare.Size + Fragment_Descriptor_Size) - Size;
if Units_Remain >= 0 then
if Units_Remain < Fragment_Descriptor_Size then
if Prev_Spare /= null then
Prev_Spare.Next := Next_Spare.Next;
else
Free_List := Next_Spare.Next;
end if;
else
Next_Spare.Size := Units_Remain - Fragment_Descriptor_Size;
Next_Spare := Value (Plus_Ai (Address (Next_Spare),
Units_Remain));
end if;
Item_Found := Next_Spare;
return;
else
Prev_Spare := Next_Spare;
Next_Spare := Next_Spare.Next;
end if;
end loop;
Item_Found := null;
end Find_First_Spare;
function Allocate_Collection
(Units : Integer; Extend : Boolean) return System_Address is
-- just a skin for Create_Collection
pragma Routine_Number (Runtime_Ids.Allocate_Collection);
begin
if Extend then
return Create_Collection (Units, Coalesce_And_Extend, 0);
else
return Create_Collection (Units, Coalesce_And_No_Extend, 0);
end if;
end Allocate_Collection;
-- Allocate a NEW collection.
--
-- This routine is NOT used to extend an existing collection. A collection
-- large enough to contain a Units sized element is created. If Units is
-- zero, a default size is used. Note that in this implementation, an
-- allocated element also contains an extra 4 byte attribute which
-- indicates the size of the allocated element, thus Units is made at
-- least 4 bytes larger before the actual allocation is made (i.e.
-- Cell_Descriptor_Size). Also, as a collection is being made to contain
-- at least a Units sized object, bytes must also be reserved to contain
-- a collection header (i.e. Collection_Descriptor_Size bytes). Finally,
-- for architectural efficiency reasons, all objects on the heap are
-- allocated on 8 byte boundaries, so Units may be adjusted upward by
-- up to 7 bytes to insure that the next object allocated on the heap
-- will be at a 8 byte boundary. Although it is erroneous for the
-- compiler to call this routine with a negative Units value, this
-- condition is tested herein.
--
-- Access to the global heap as a whole (vs. heap within an allocated
-- collection) and the list of collections itself is guarded by the
-- Storage_Lock semaphore. This is necessary for this implementation
-- as Ada tasks are mapped to OS processes, so these runtime routines
-- must manage these resources in critical regions.
--
-- Upon completion, a collection containing a single free fragment is
-- returned.
--
-- Exceptions:
-- Storage_Error may be raised by the underlying routines if no
-- storage is available for the collection.
--
-- Assertions:
-- o New_Segment will cause an exception to be raised if it fails.
-- o No deadlock detection is tested in the contention between the
-- task management semaphore and the Storage_Lock semaphore.
-- o Find_First_Spare ensures that the returned fragment is no longer
-- on the free fragment list.
-- o Semaphore.Release raises no exceptions.
-- o New_Segment will release Storage_Lock semaphore if it fails.
--
-- Notes:
-- o The Units_Free and Chunk_Size fields of the collection header
-- do not include the byte count required to contain the collection
-- header itself.
--
function Create_Collection
(Units : Integer;
Attributes : Collection_Attributes := Coalesce_And_Extend;
Extension_Size : Integer := 0) return System_Address is
pragma Routine_Number (Runtime_Ids.Allocate_Collection);
Actual_Units : Integer;
New_Chunk : Fragment_Link;
New_Collection : Collection_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Allocate_Collection: units = " &
-- Integer'Image (Units));
-- end if;
if Units <= Use_Default_Chunk_Size then
--
-- Negative => Start with the default size
--
Actual_Units := Default_Chunk_Size;
elsif Units = Use_Descriptor_Or_Null_Size then
if Extendible (Attributes) then
--
-- Zero and extendible => Start with zero use
--
Actual_Units := Collection_Descriptor_Size +
Align (Cell_Descriptor_Size);
else
--
-- Zero and non extensible => no collection allocated
--
return System.Address_Zero;
end if;
else
--
-- Positive => Start with passed size
--
Actual_Units := Collection_Descriptor_Size +
Align (Units + Cell_Descriptor_Size);
end if;
Semaphore.Acquire (Storage_Lock);
Find_First_Spare (Actual_Units, Free_Fragment_List, New_Chunk);
if New_Chunk = null then
New_Segment (Actual_Units);
Find_First_Spare (Actual_Units, Free_Fragment_List, New_Chunk);
end if;
Semaphore.Release (Storage_Lock);
New_Collection := Value (Address (New_Chunk));
New_Chunk := Value (Plus_Ai (Address (New_Chunk),
Collection_Descriptor_Size));
New_Collection.Attributes := Attributes;
New_Collection.Chunk_List := null;
New_Collection.Units_Free := Actual_Units - Collection_Descriptor_Size;
New_Collection.Free_List := New_Chunk;
New_Collection.Chunk_Size := Actual_Units - Collection_Descriptor_Size;
New_Collection.Lock.Next := null;
New_Collection.Lock.Key := 0;
New_Chunk.Size := Actual_Units - Collection_Descriptor_Size -
Fragment_Descriptor_Size;
New_Chunk.Next := null;
if Extension_Size > 0 then
New_Collection.Extension_Size :=
Align (Extension_Size + Cell_Descriptor_Size);
else
New_Collection.Extension_Size := Default_Chunk_Size;
end if;
-- if Debug_Mode then
-- Debugging.Put_Message ("After Allocate_Collection -->");
-- Debugging.Put_Fragment_List (Header => "Spare pool",
-- Start => Free_Fragment_List);
-- Debugging.Put_Collection_List
-- (Header => "Collection list", Start => New_Collection);
-- end if;
return Address (New_Collection);
end Create_Collection;
-- Deallocate a collection.
--
-- Deallocate a collection and any extensions associated with it
-- by placing all storage used by the collection in the global list of
-- free fragments. For program security reasons, the collection
-- pointer is NULLed after it is deallocated. If a NULL pointer
-- is given to this routine, no action is performed and no error
-- is signalled - thus it is valid to attempt to deallocate a
-- collection more than once.
--
-- Access to the global heap as a whole (vs. heap within an allocated
-- collection) and the list of collections itself is guarded by the
-- Storage_Lock semaphore. This is necessary for this implementation
-- as Ada tasks are mapped to OS processes, so these runtime routines
-- must manage these resources in critical regions.
--
-- Exceptions:
-- none.
--
-- Assertions:
-- o Addr points to a valid collection object. In particular,
-- it must no point to one of the extensions for a collection
-- but the collection object itself.
-- o No deadlock detection is tested in the contention between the
-- task management semaphore and the Storage_Lock semaphore.
-- o The list of free fragments, rooted in Free_List is non circular and
-- terminates with NULL.
--
-- Note:
-- If an exception is raised in the loop following aquisition of
-- the Storage_Lock semaphore, Storage_Lock is not released. This
-- could result in a deadlock condition later on.
--
-- Change History:
-- 891016 MDW Added code to coalesce the free fragment list. The
-- free fragment list is thus also now sorted in order
-- of increasing address.
--
procedure Deallocate_Collection (Addr : in out Address_Ref) is
pragma Routine_Number (Runtime_Ids.Deallocate_Collection);
Addr_Value : System_Address := Addr.all;
Collection_Base : Collection_Link;
Next_Collection : Collection_Link;
New_Fragment : Fragment_Link;
Prev_Fragment : Fragment_Link;
Search_Fragment : Fragment_Link;
Temp_Collection : Collection_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Deallocate_Collection");
-- end if;
if To_Integer (Addr_Value) = 0 then
-- if Debug_Mode then
-- Debugging.Put_Message ("Collection already null");
-- end if;
return;
end if;
Collection_Base := Value (Addr_Value);
Next_Collection := Collection_Base;
-- if Debug_Mode then
-- Debugging.Put_Collection_List
-- ("Deallocating Collection", Collection_Base);
-- end if;
Semaphore.Acquire (Storage_Lock);
loop
-- Iterate over a collection and its extensions.
Temp_Collection := Next_Collection.Chunk_List;
New_Fragment := Value (Address (Next_Collection));
Prev_Fragment := null;
Search_Fragment := Free_Fragment_List;
while (Search_Fragment /= null) and then
(Search_Fragment < New_Fragment) loop
-- Find the fragments which surround New_Fragment.
Prev_Fragment := Search_Fragment;
Search_Fragment := Search_Fragment.Next;
end loop;
New_Fragment.Next := Search_Fragment;
New_Fragment.Size := Next_Collection.Chunk_Size +
Collection_Descriptor_Size -
Fragment_Descriptor_Size;
if Prev_Fragment = null then
-- New_Fragment is new head of free fragment list.
Free_Fragment_List := New_Fragment;
else
-- Link New_Fragment into middle or end of free list.
Prev_Fragment.Next := New_Fragment;
if Value (Plus_Ai
(Address (Prev_Fragment),
Prev_Fragment.Size + Fragment_Descriptor_Size)) =
New_Fragment then
-- Coalesce Prev_Fragment with New_Fragment.
-- Note: Fragment_Descriptor_Size is already accounted
-- for in both fragments and so must be backed
-- out of one.
Prev_Fragment.Next := New_Fragment.Next;
Prev_Fragment.Size :=
Prev_Fragment.Size + New_Fragment.Size +
Fragment_Descriptor_Size;
New_Fragment := Prev_Fragment;
end if;
end if;
if (Search_Fragment /= null) and then
(Value (Plus_Ai (Address (New_Fragment),
New_Fragment.Size + Fragment_Descriptor_Size)) =
Search_Fragment) then
-- Coalesce New_Fragment with Search_Fragment.
-- Note that both New_Fragment and Search_Fragment have
-- accounted for Fragment_Descriptor_Size, so we must
-- discount it once when we coalesce.
New_Fragment.Next := Search_Fragment.Next;
New_Fragment.Size := New_Fragment.Size + Search_Fragment.Size +
Fragment_Descriptor_Size;
end if;
exit when Temp_Collection = null;
Next_Collection := Temp_Collection;
end loop;
Semaphore.Release (Storage_Lock);
-- if Debug_Mode then
-- Debugging.Put_Message ("After Deallocate_Collection -->");
-- Debugging.Put_Fragment_List (Header => "Spare pool",
-- Start => Free_Fragment_List);
-- end if;
Addr.all := To_Address (0);
end Deallocate_Collection;
-- Compute a size to extend a collection.
--
-- There is a non obvious optimization in this routine. The ">"
-- relational is used instead of ">=" so that when Allocate_Collection
-- is ultimately called it will do slightly less work.
--
-- Note: this code assumes that the call to create the collection
-- occurred in one of two ways. Either the original call
-- had a non zero Units (size) parameter, in which case the
-- collection is not extensible and this routine will not get
-- invoked, or the Units parameter is zero and the default
-- collection size is used for the first collection.
--
-- Exceptions:
-- none.
--
-- Assertions:
-- none.
--
function Size_For_Extension
(Object_Size : Integer; Base_Clctn_Size : Integer)
return Integer is
pragma Routine_Number (Runtime_Ids.Internal);
begin
if Object_Size > Base_Clctn_Size then
return Object_Size;
else
return Base_Clctn_Size;
end if;
end Size_For_Extension;
-- Extend a collection.
--
-- Exceptions:
-- Storage_Error is raised if collection is not extensible. The
-- actual allocation may also raise Storage_Error.
--
-- Assertions:
--
function Extend_Collection (Units : Integer; Clctn : Collection_Link)
return Collection_Link is
pragma Routine_Number (Runtime_Ids.Internal);
Extension : Collection_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Extend_Collection");
-- end if;
if not Extendible (Clctn) then
raise Storage_Error;
end if;
Extension := Value (Create_Collection
(Size_For_Extension (Units,
Clctn.Extension_Size),
Clctn.Attributes, Clctn.Extension_Size));
Extension.Chunk_List := Clctn.Chunk_List;
Clctn.Chunk_List := Extension;
-- if Debug_Mode then
-- Debugging.Put_Message ("After Extend_Collection");
-- Debugging.Put_Fragment_List (Header => "Spare pool",
-- Start => Free_Fragment_List);
-- Debugging.Put_Collection_List
-- (Header => "Collection list", Start => Clctn);
-- end if;
return Extension;
end Extend_Collection;
-- Allocate a cell large enough to contain an object of Units bytes in
-- a collection.
--
-- A collection and all of its extensions are checked to find a fragment
-- large enough to contain a Units byte length object. If not found,
-- an attempt is made to extend the collection.
--
-- Exceptions:
-- Storage_Error is raised if the object cannot be allocated in a
-- collection and the collection is not extensible, or if the
-- collection is extensible but could not be extended to contain
-- an object of Units bytes.
--
-- Assertions:
-- o Extend_Collection will raise a Storage_Error exception if
-- it is unsuccessful.
--
-- Notes:
-- It is invalid for this routine to be called with a negative Units
-- value, but the condition is checked anyway. In this implementation,
-- each allocated object has an additional 4 byte attribute which
-- names the length of the allocation. This routine automatically
-- accounts for that additional attribute field. The result of this
-- routine is a pointer to this attribute field, it is the caller's
-- responsibility to ultimately return a pointer to the user's
-- program which points beyond this attribute field.
--
function Insert_Cell
(Units : Integer; Clctn : Collection_Link) return Cell_Link is
pragma Routine_Number (Runtime_Ids.Internal);
Actual_Size : Integer := Align (Units + Cell_Descriptor_Size);
Next_Clctn : Collection_Link := Clctn;
New_Chunk : Fragment_Link;
New_Cell : Cell_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Insert_Cell: units = " &
-- Integer'Image (Units));
-- end if;
if Units < 0 or Next_Clctn = null then
raise Exceptions.Runtime_Error;
end if;
for Retry_After_Extend_Collection in Boolean loop
while Next_Clctn /= null loop
if Next_Clctn.Units_Free >= Actual_Size then
Find_First_Spare (Actual_Size,
Next_Clctn.Free_List, New_Chunk);
if New_Chunk /= null then
Next_Clctn.Units_Free :=
Next_Clctn.Units_Free - Actual_Size;
New_Cell := Value (Address (New_Chunk));
New_Cell.Size := Actual_Size - Cell_Descriptor_Size;
-- if Debug_Mode then
-- Debugging.Put_Cell_Info
-- (Header => "After Insert_Cell ->",
-- Item => New_Cell,
-- Clctn => Clctn);
-- end if;
return New_Cell;
end if;
end if;
Next_Clctn := Next_Clctn.Chunk_List;
end loop;
-- if Debug_Mode then
-- Debugging.Put_Message ("Extending collection");
-- end if;
Next_Clctn := Extend_Collection (Actual_Size, Clctn);
-- if Debug_Mode then
-- Debugging.Put_Message ("Looping for retry in Insert_Cell");
-- end if;
end loop;
raise Exceptions.Runtime_Error;
return null;
end Insert_Cell;
-- Allocate an object in a collection.
--
-- The name of this routine is misleading as it is used to allocate all
-- types of objects in a collection. A pointer to a block of memory
-- large enough to contain a Units byte length object in the collection
-- is returned. Note that an additional 4 bytes (Cell_Descriptor_Size)
-- is allocated for the object to contain the cell length attribute
-- field (see discussions above).
--
-- Each collection contains its own semaphore which is used to guard
-- the update operation for free list manipulation and object allocation.
-- This is necessary in this implementation as Ada tasks are mapped to
-- OS processes.
--
-- Exceptions:
-- Storage_Error can be raised by the underlying routines.
--
-- Assertions:
-- o Collection points to a valid collection object.
--
-- Change History:
-- 891016 MDW Added code to release the In_Clctn.Lock semaphore in
-- the event that Storage_Error is raised.
--
function Allocate_Fixed_Cell (Units : Integer; Collection : System_Address)
return System_Address is
pragma Routine_Number (Runtime_Ids.Allocate_Fixed_Cell);
In_Clctn : Collection_Link;
New_Cell : Cell_Link;
Result : System_Address;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Allocate_Fixed_Cell");
-- end if;
In_Clctn := Value (Collection);
Semaphore.Acquire (In_Clctn.Lock);
begin
New_Cell := Insert_Cell (Units, In_Clctn);
Result := Plus_Ai (Address (New_Cell), Cell_Descriptor_Size);
Semaphore.Release (In_Clctn.Lock);
exception
when Storage_Error =>
Semaphore.Release (In_Clctn.Lock);
raise;
end;
return Result;
end Allocate_Fixed_Cell;
-- Determine if a cell is contained within a chunk.
--
-- This routine is used to determine which chunk (extension) of a
-- collection contains a cell which is to be disposed.
--
-- Exceptions:
-- none.
--
-- Assertions:
-- o A_Chunk points to a valid collection object.
--
function Is_Contained_In (A_Chunk : Collection_Link; The_Cell : Cell_Link)
return Boolean is
pragma Routine_Number (Runtime_Ids.Internal);
Cell_Addr : System_Address := Address (The_Cell);
First_Byte : System_Address := Address (A_Chunk);
Last_Byte : System_Address :=
Plus_Ai (Plus_Ai (First_Byte, Collection_Descriptor_Size),
A_Chunk.Chunk_Size);
begin
return Lt (First_Byte, Cell_Addr) and then Le (Cell_Addr, Last_Byte);
end Is_Contained_In;
-- Deallocate an object in a collection.
--
-- Exceptions:
-- none.
--
-- Assertions:
-- o Clctn points to a valid collection object.
--
-- Note:
-- It is invalid to provide a Cell parameter which does not represent
-- a valid object in the Clctn collection. This routine checks that
-- Cell has a valid attribute field (see discussion above) located at
-- 4 bytes before the cell pointer and that the cell is within the
-- set of collections owned by the access type.
--
-- Change History:
-- 891016 MDW Added code to colesce the free list for the collection.
-- Free list entries for the collection are now maintained
-- in sorted order by address.
--
procedure Remove_Cell
(Units : Integer; Clctn : Collection_Link; Cell : Cell_Link) is
pragma Routine_Number (Runtime_Ids.Internal);
Free_Chunk : Fragment_Link;
Freed_Size : Integer := Align (Units + Cell_Descriptor_Size);
Next_Clctn : Collection_Link := Clctn;
Prev_Fragment : Fragment_Link;
Search_Fragment : Fragment_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Remove_Cell");
-- end if;
if Safety_Check and then Cell.Size /=
(Freed_Size - Cell_Descriptor_Size) then
-- Debugging.Put_Message ("Cell.Size = " & Integer'Image (Cell.Size));
-- Debugging.Put_Message ("Units = " & Integer'Image (Units));
raise Exceptions.Runtime_Error;
end if;
while Next_Clctn /= null loop
if Is_Contained_In (Next_Clctn, Cell) then
if Coalescible (Next_Clctn) then
Next_Clctn.Units_Free := Next_Clctn.Units_Free + Freed_Size;
Free_Chunk := Value (Address (Cell));
Prev_Fragment := null;
Search_Fragment := Next_Clctn.Free_List;
while (Search_Fragment /= null) and then
(Search_Fragment < Free_Chunk) loop
-- Find fragments surrounding Free_Chunk.
Prev_Fragment := Search_Fragment;
Search_Fragment := Search_Fragment.Next;
end loop;
Free_Chunk.Size := Freed_Size - Fragment_Descriptor_Size;
Free_Chunk.Next := Search_Fragment;
if Prev_Fragment = null then
-- Free chunk is new head of free list.
Next_Clctn.Free_List := Free_Chunk;
else
-- Insert Free_Chunk into middle or end of free list.
Prev_Fragment.Next := Free_Chunk;
if Value (Plus_Ai (Address (Prev_Fragment),
Prev_Fragment.Size +
Fragment_Descriptor_Size)) =
Free_Chunk then
-- Coalesce Prev_Fragment and Free_Chunk.
-- Note that Fragment_Descriptor_Size was accounted
-- for in both fragments and so must be backed out
-- of one.
Prev_Fragment.Next := Free_Chunk.Next;
Prev_Fragment.Size :=
Prev_Fragment.Size + Free_Chunk.Size +
Fragment_Descriptor_Size;
Free_Chunk := Prev_Fragment;
end if;
end if;
if (Search_Fragment /= null) and then
(Value (Plus_Ai (Address (Free_Chunk),
Free_Chunk.Size +
Fragment_Descriptor_Size)) =
Search_Fragment) then
-- Coalesce Free_Chunk and Search_Fragment.
-- Note that Fragment_Descriptor_Size was accounted
-- for in both fragments and so must be backed out
-- of one.
Free_Chunk.Next := Search_Fragment.Next;
Free_Chunk.Size := Free_Chunk.Size +
Search_Fragment.Size +
Fragment_Descriptor_Size;
end if;
-- if Debug_Mode then
-- Debugging.Put_Message ("After Deallocate_Cell ->");
-- Debugging.Put_Collection_List
-- (Header => "Collection list", Start => Clctn);
-- end if;
return;
else -- not coalescible, stick at head of unordered free_list
Next_Clctn.Units_Free := Next_Clctn.Units_Free + Freed_Size;
Free_Chunk := Value (Address (Cell));
Free_Chunk.Size := Freed_Size - Fragment_Descriptor_Size;
Free_Chunk.Next := Next_Clctn.Free_List;
Next_Clctn.Free_List := Free_Chunk;
return;
end if;
else
Next_Clctn := Next_Clctn.Chunk_List;
end if;
end loop;
raise Exceptions.Runtime_Error;
end Remove_Cell;
-- Deallocate an object in a collection.
--
-- This routine name is misleading as it is used to deallocate any
-- type of object. After the cell is deallocated, the pointer is
-- set to NULL. If this routine is called with a NULL cell pointer,
-- no action takes place and no error is raised, thus it is valid
-- to repeatedly deallocate a cell. As described above, each collection
-- has a semaphore used to guard the free list and allocate/deallocate
-- process.
--
-- Exceptions:
-- none.
--
-- Assertions:
-- o Collection points to a valid collection.
-- o Cell points to a valid cell in the named collection.
-- o No exceptions are raised during deallocation (which could
-- cause the In_Clctn.Lock semaphore to remain aquired and
-- possibly cause a later deadlock).
--
procedure Deallocate_Fixed_Cell (Units : Integer;
Collection : System_Address;
Cell : Address_Ref) is
pragma Routine_Number (Runtime_Ids.Deallocate_Fixed_Cell);
Cell_Addr : System_Address := Cell.all;
In_Clctn : Collection_Link;
begin
-- if Debug_Mode then
-- Debugging.Put_Message ("Entered Deallocate_Fixed_Cell");
-- end if;
if Cell_Addr = To_Address (0) then
return;
end if;
In_Clctn := Value (Collection);
Semaphore.Acquire (In_Clctn.Lock);
Remove_Cell (Units => Units,
Clctn => In_Clctn,
Cell => Value (Minus_Ai
(Cell_Addr, Cell_Descriptor_Size)));
Semaphore.Release (In_Clctn.Lock);
Cell.all := To_Address (0);
end Deallocate_Fixed_Cell;
-- Return the size of heap allocated to contain the collection(s) for
-- an access type.
--
-- Note that each collection has a semaphore used to guard the collection
-- (see discussion above).
--
-- Exceptions:
-- none.
--
-- Assertions:
-- o Cp points to a valid collection.
-- o No exceptions are raised (which could cause the Base_Collection.Lock
-- semaphore to remain aquired which could later cause a deadlock).
--
-- Note:
-- NEED TO CHECK THE PROPER USE OF SEMAPHORES IN THIS ROUTINE.
--
function Collection_Size (Cp : System_Address) return Integer is
pragma Routine_Number (Runtime_Ids.Collection_Size);
Base_Collection : Collection_Link;
Next_Collection : Collection_Link;
Total_Size : Integer;
begin
Base_Collection := Value (Cp);
Semaphore.Acquire (Base_Collection.Lock);
Total_Size := Base_Collection.Chunk_Size;
if Extendible (Base_Collection) then
Next_Collection := Base_Collection;
loop
Next_Collection := Next_Collection.Chunk_List;
exit when Next_Collection = null;
Total_Size := Total_Size + Next_Collection.Chunk_Size;
end loop;
end if;
Semaphore.Release (Base_Collection.Lock);
return Total_Size;
end Collection_Size;
-- package body Debugging is
--
-- pragma Suppress (Storage_Check);
--
-- procedure Put_Message (Msg : String) is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- begin
-- Debug_Io.New_Line;
-- Debug_Io.Put_Line (Msg);
-- end Put_Message;
--
-- procedure Put_Fragment_List (Header : String; Start : Fragment_Link) is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- Next : Fragment_Link := Start;
-- begin
-- if Header /= "" then
-- Debug_Io.Put_Line (Header);
-- end if;
--
-- while Next /= null loop
-- Debug_Io.Put_Line (" Address Size Next");
-- Debug_Io.Put
-- (To_Integer (Address (Next)), Width => 8, In_Hex => True);
-- Debug_Io.Put (": ");
-- Debug_Io.Put (Next.Size);
-- if Next.Next /= null then
-- Debug_Io.Put (" ");
-- Debug_Io.Put (To_Integer (Address (Next.Next)),
-- Width => 8,
-- In_Hex => True);
-- else
-- Debug_Io.Put (" null");
-- end if;
-- Debug_Io.New_Line;
-- Next := Next.Next;
-- end loop;
-- Debug_Io.New_Line;
-- end Put_Fragment_List;
--
-- procedure Put_Collection_List
-- (Header : String; Start : Collection_Link) is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- Next : Collection_Link := Start;
-- begin
-- if Header /= "" then
-- Debug_Io.Put_Line (Header);
-- end if;
--
-- while Next /= null loop
-- Debug_Io.Put_Line
-- (" Address SizeAC NextAC NxtFre SizFre Ext");
-- Debug_Io.Put
-- (To_Integer (Address (Next)), Width => 8, In_Hex => True);
-- Debug_Io.Put (": ");
-- Debug_Io.Put (Next.Chunk_Size);
-- Debug_Io.Put (" ");
-- if Next.Chunk_List /= null then
-- Debug_Io.Put (To_Integer (Address (Next.Chunk_List)),
-- Width => 8,
-- In_Hex => True);
-- else
-- Debug_Io.Put (" null ");
-- end if;
-- Debug_Io.Put (" ");
-- Debug_Io.Put (To_Integer (Address (Next.Free_List)),
-- Width => 8,
-- In_Hex => True);
-- Debug_Io.Put (Next.Units_Free);
-- Debug_Io.Put (" ");
-- if Next.Extendible then
-- Debug_Io.Put ('T');
-- else
-- Debug_Io.Put ('F');
-- end if;
--
-- Debug_Io.New_Line;
--
-- Put_Fragment_List (Header => "", Start => Next.Free_List);
--
-- Debug_Io.New_Line;
-- Debug_Io.New_Line;
-- Next := Next.Chunk_List;
-- end loop;
-- Debug_Io.New_Line;
-- end Put_Collection_List;
--
-- procedure Put_Cell_Info (Header : String;
-- Item : Cell_Link;
-- Clctn : Collection_Link) is
-- pragma Routine_Number (Runtime_Ids.Internal);
-- begin
-- if Header /= "" then
-- Debug_Io.Put_Line (Header);
-- end if;
--
-- Debug_Io.Put ("Allocated cell of size ");
-- Debug_Io.Put (Item.Size);
--
-- Debug_Io.Put (" at address ");
-- Debug_Io.Put
-- (To_Integer (Address (Item)), Width => 8, In_Hex => True);
-- Debug_Io.New_Line;
-- end Put_Cell_Info;
--
-- end Debugging;
begin
Free_Fragment_List := null;
Storage_Lock := (null, 0);
end Storage_Management;
pragma Export_Elaboration_Procedure ("__STORAGE_MGMT_BODY_ELAB");
pragma Runtime_Unit (Unit_Number => Runtime_Ids.Runtime_Compunit,
Elab_Routine_Number => Runtime_Ids.Internal);