|
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 - 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);