![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Memory Management This chapter describes the Apex implementation that addresses Ada memory management requirements. Dynamic memory allocation/deallocation support available to application programs is discussed, including:
The Ada language has many explicit and implicit memory requirements. Explicit requirements include object declarations and the new allocator. Implicit requirements include queues and blocks for tasking control and intermediate storage for initializations. This chapter describes how Apex provides services to meet these requirements and shows how these services can be customized for your application.
Memory requirements are met from one of three areas: static data, heap, stacks. Use of static data is restricted to what can be allocated at compile time. Stacks are typically restricted to local usage because stack offsets must be known, and except for the main task's stack, the maximum size of a stack is fixed when the stack is created. The heap is the most flexible, the locations and sizes of objects allocated from the heap do not need to be known until runtime and the heap can be dynamically expanded. It can be used for any memory requirement, but excessive heap usage can degrade performance.
Managing Heap StorageHistorically, a program's dynamically allocated memory was managed with a data structure called a heap. Apex's memory management is multilayered and involves several different data structures, some of these are heaps in the traditional sense, others are not. In this section we describe Apex's memory management:
- Memory Management Architecture
- Memory Management Configuration
- User Defined Storage Pools
- Memory Management Instrumentation
Memory Management Architecture
The ultimate supplier of the memory used to satisfy an Ada new allocation request is the operating system, however Ada language requirements and performance considerations lead us to the introduction of two additional layers: Collection Callouts and Storage Pools. Consider how Apex might support the new and Unchecked_Deallocation operations in this example:
type T is ... -- some type declaration type Access_T is access T; procedure Free is new Unchecked_Deallocation (T, Access_T); X : Access_T := new T;
One possible memory management implementation is to have the compiler emit calls directly to the operating system's heap routines: malloc for the new operation and free for Unchecked_Deallocation. But with such an implementation, the enforcement of the required Ada semantics would be more complex and it would be difficult to optimize allocation policies for different applications. Apex introduces collections and storage pools to address these issues. see Figure 4. Collections provide support for language features such as controlled object finalization and allocation limit enforcement. Storage pools provide a standard interface to the heap management facilities resident in the Apex runtime, operating system, and/or application.
Figure 4 Memory Management Architecture
![]()
Every access type is associated with a collection and each collection is associated with a storage pool. These associations are managed by the memory management system, however, as an optimization, the association between an access type and collection is sometimes implied rather than explicitly recorded.
The collections allocator is assigned its own distinct collection, while derived access types are associated with the collection of their base type. Apex provides an optimization for the case where an access type will never be utilized to dynamically allocate an object. Such access types are implicitly associated with a special null collection (and null storage pool). Consider this example:
type T; type Access_T is access T; for Access_T'Storage_Size use Zero;
where Zero is a static expression that evaluates to 0. Since there is no possibility that the access type can ever be used to dynamically allocate an object, Apex can reduce resource usage by suppressing the creation and assignment of a collection. An access type without a collection will raise Storage_Error if an allocation is attempted. This optimization is only possible if the Storage_Size argument (Zero in the example above) is a static expression.
If the Storage_Size argument is non-static, a collection is created for the access type even if the argument evaluates to zero at runtime. Whenever allocations are made from a collection the storage limit is checked. If the specified number of allocated storage elements would be exceeded, Storage_Error is raised.
In addition to enforcing Storage_Size constraints, collections provide support for the automatic reclamation of allocated, but unreachable, objects. When the base access type associated with a collection is no longer visible, any objects remaining in the collection are unreachable and can be deallocated. A typical scenario for this is when a (base) access type is declared within a subprogram; the collection is created when the subprogram is called and can be discarded when the subprogram returns. The automatic reclamation of all memory associated with the collection improves resource utilization while relieving the application of having to explicitly deallocate any objects that are still allocated at subprogram exit. (And, as discussed in Storage Pools, these automatic deallocations can be performed very efficiently).
Note that in instances where unchecked programming is being used there could still be references to allocated objects after the access type has gone out of scope. In such cases the base access type should be declared in a scope that won't be exited, for example, a library package, so that the automatic reclamation will not occur.
Storage Pools
Storage pools, as defined in Ada95 RM 13.11, provide a standard interface to memory management services. Every collection has an associated storage pool and since every access type is, at least implicitly, associated with a collection, each access type is assigned to a storage pool. Multiple collections can be associated with the same storage pool and it is possible that all of the allocations for every access type come from the same storage pool. It is also possible to configure memory management so that different access types use different storage pools. Apex allows you to configure the kind of storage pools that your application will use and to control the assignment of collections to storage pools.
The storage pool associated with an access type can be determined by querying the type's Storage_Pool attribute. For any subtype S, S'Storage_Pool denotes the storage pool to which S has been assigned. The size of the type's storage pool can also be determined with an attribute: S'Storage_Size denotes the size of S's storage pool (in storage elements).
Note: The size reported reflects the total storage available when no allocations have been made from the pool; it does not report the amount of currently unallocated memory available in a non-empty pool. Also, since multiple access types can be assigned to the same storage pool, the memory available is not necessarily dedicated to objects of a particular type.
The Root_Storage_Pool type, defined in System.Storage_Pools, defines operations for allocation, deallocation, and pool size determination. This type can be extended and many implementations are possible. Apex provides storage pool types that are interfaces to external heap management services (for example, malloc/free) as well as storage pools that implement their own, internal, heap management. The Apex supplied storage pool types (as well as the Root_Storage_Pool) can be extended to create custom storage pools that address application specific memory management requirements, see User Defined Storage Pools.
Two predefined storage pool configurations are provided with Apex, each of which defines a different set of standard storage pools. The default configuration, for all products except Apex Embedded for Rational Exec and Apex Embedded for Tornado, is called Externally Managed Heap. It utilizes only one storage pool object for all application allocations/deallocations, the External_Pool, whose default implementation provides an interface to the malloc/free heap services of the operating system. In this configuration all allocation/deallocation requests from the collection layer are serviced by malloc/free.
The Apex compiler generates calls to the Collection layer to implement new allocations and Unchecked_Deallocations. While these calls are present in the compiled application code, the remainder of the storage management implementation is usually built separately from the application. The Collection and External_Pool code is retrieved from Apex supplied runtime archives and linked with the application program. The operating system interface usually consists of additional OS supplied libraries that are linked with the application. The exception to this scenario occurs when the application provides its own External_Pool type.
Figure 5 Externally Managed Heap Architecture
![]()
The alternate configuration called Apex Managed Heap (see Figure 6), utilizes a hierarchy of storage pools layered over the External_Pool. This is the default configuration in Apex Embedded for Rational Exec and Apex Embedded for Tornado. In this configuration, a Default_Pool provides the heap support for application allocations/deallocations. The External_Pool is only accessed when more memory needs to be added to the Default_Pool's heap. In addition to the Default_Pool, other storage pools may be created to provide dedicated storage for some collections, see Memory Management Configuration.
Figure 6 Apex Managed Heap Architecture
![]()
Memory Management Configuration
Memory management can be configured at the access type, program, and system (multiple program) levels. The configuration is specified with:
and enable a developer to make such decisions as the amount of storage allotted for an access type, where that storage should come from, and how that storage should be managed. Configuration parameters that only apply to a specific access type are set with a storage clause (Storage_Size or Storage_Pool) or a Collection_Policy pragma, program-wide parameters are set with pragma Main, and parameters that might apply to multiple programs are set in the user configuration package V_Usr_Conf.
Storage Clauses
A non-derived access type may have either a Storage_Size or a Storage_Pool specified for it (but not both, see Ada95 RM 13.11). The Storage_Size clause specifies the number of storage elements to be allotted for the designated access type.
If Externally Managed Heap is configured, the specified storage limit is recorded as a collection attribute and that value is checked each time an allocation is requested (with Storage_Error raised if the limit would be exceeded).
If Apex Managed Heap is configured, the runtime will choose a standard storage pool type for the collection and create a pool object, dedicating (reserving) the specified amount of memory exclusively for this storage pool.
If an allocation request cannot be satisfied by the pool, Storage_Error is raised (the amount of memory dedicated to the pool cannot be extended).
A Storage_Pool clause enables a user to designate that a specific storage pool object be used for an access type. The designated storage pool could be an instance of one of the Rational supplied pool types (see User Defined Storage Pools), an instance of an extension of the Root_Storage_Pool (or one of its Rational extensions), or one of the runtime's standard storage pool objects. For example:
type T; type Access_T is access T; type T2; type Access_T2 is access T2; for Access_T2'Storage_Pool use Access_T'Storage_Pool;
would insure that Access_T and Access_T2 shared the same storage pool (in this case a standard storage pool chosen by the runtime for Access_T).
Pragmas
Pragma Collection_Policy controls memory allocation for the collection designated by an access type. The memory management semantics associated with the pragma are a superset of those associated with the Storage_Size clause. The difference between the clause and the pragma is that the use of the clause will result in a fixed size collection while the pragma allows for extensible collections. The use of Collection_Policy pragmas and Storage_Size and Storage_Pool clauses are mutually exclusive.
See pragma Collection_Policy in the Ada Compiler Reference for a more detailed description.
Pragma Main has the following memory management related arguments:
- Heap_Size: An integer expression that specifies the maximum size of the heap in storage elements. If Heap_Size is specified the resource limit for the process's data segment will be set accordingly. If Heap_Size is not specified the limit will remain at the process's default. When the limit would be exceeded the operating system rejects the requested allocation and a Storage_Error exception is raised. The Heap_Size parameter can also be specified at runtime with the APEX_HEAP_SIZE session switch.
This parameter is not supported in the Apex Embedded for Rational Exec and Apex for Tornado products. In Apex Embedded for Rational Exec, the heap size is configured in the Rational Exec kernel (in the configuration package V_Krn_Conf).
- Heap_Extend: An integer expression that specifies the number of storage elements by which the heap is to be extended when needed. This argument is ignored if Externally Managed Heap is configured.
- Collection_Initial_Size: An integer expression giving the default size in storage elements of a reclaimable collection's storage pool when it's created. This argument is only relevant when Apex Managed Heap is configured and Private_Storage_Pools are enabled. This argument can be overridden for individual access types by using a Collection_Policy pragma or a Storage_Size or Storage_Pool clause.
- Collection_Extension_Size: An integer expression giving the minimum number of storage units by which a reclaimable, extensible collection's storage pool will be extended. This argument is applicable in the same situations as the related Collection_Initial_Size argument.
See pragma Main in the Ada Compiler Reference for a more detailed description of this pragma.
The default V_Usr_Conf package that you are using is either:
For native development
$APEX_BASE/ada/usr_conf.ss/${APEX_ARCH_OS} .language_variant.$APEX_PROD_VERSION .rel/v_usr_conf.2.ada
For cross development
$APEX_BASE/ada/usr_conf.ss/target_family. compiler_variant.board_variant.language_variant .$APEX_PROD_VERSION.rel/v_usr_conf2.ada
Note: The meta names used in this path are set when Apex is invoked.The environment variable values can be displayed using Tools > Session > Environment.
Configuration Packages
The package V_Usr_Conf, located in the usr_conf.ss subsystem, contains a number of configuration items related to memory management. The specification of the package provides instructions for customizing memory management and other runtime services. Configuration parameters are set in the package body, the package specification should not be modified.
The Apex Embedded for Rational Exec and Apex for Tornado products provide additional storage management configuration capabilities in their kernel configuration packages, V_Krn_Conf, located in their krn_conf.ss subsystems. Note that these products have separate V_Usr_Conf and V_Krn_Conf packages for each supported target board.
Note: Some of the parameters in the configuration package can be overridden by pragma Main arguments, as well as by storage clauses and Collection_Policy pragmas.
There are four groups of memory management related configuration items in the V_Usr_Conf package body:
- Configuration_Table
- Mem_Alloc_Conf_Table and Small_Block_Sizes_Table
- Storage Pool Configuration
- Storage_Management_Callout Routines
Configuration_Table
The Configuration_Table contains many user configurable parameters for controlling the behavior of the Apex runtime. The following parameters are related to memory management:
- Heap_Max specifies the maximum number of storage units to which the heap can grow. This corresponds to the maximum amount of memory (for data) that can be allocated from the underlying operating system. The enforcement of this limit is only supported when the operating system supports setting the process's data segment limit (for example, with rlimit). This value can be overridden with pragma Main's Heap_Size argument.
This parameter is not supported in the Apex Embedded for Rational Exec and Apex for Tornado products. In Apex Embedded for Rational Exec, the heap size is configured in the Rational Exec kernel (in the configuration package V_Krn_Conf).
- Heap_Extend specifies the minimum number of storage units requested when the Default_Pool is extended. This parameter is only relevant when Apex Managed Heap is configured. This parameter is ignored if Externally Managed Heap (see Malloc_Based_Allocation) is configured. When an allocation is requested that exceeds the amount of memory available in the Default_Pool, the pool is extended by the maximum of the requested amount and the Heap_Extend value. This value can be overridden with pragma Main's Heap_Extend argument.
- Mem_Alloc_Mutex_Attr_Address points to the mutex attributes that will be used when initializing the mutex used for mutual exclusion during memory allocation/deallocation (at both the collection and storage pool layers). Set this parameter to No_Addr to use the default mutex attributes. Otherwise, set it to the address of an Ada_Krn_Defs.Mutex_Attr_T record initialized in V_Init_Attr. This configuration parameter is only relevant to tasking programs and is independent of any mutual exclusion that might be provided by the operating system (for example, thread-safe malloc).
- Mem_Alloc_Conf_Table_Address points to the Mem_Alloc_Conf_Table which defines additional memory management configuration parameters.
Mem_Alloc_Conf_Table and Small_Block_Sizes_Table
The Mem_Alloc_Conf_Table contains several parameters that are relevant to both the Externally Managed Heap and the Apex Managed Heap configurations, as well as additional parameters that are only meaningful in the Apex Managed Heap configuration.
Mem_Alloc_Conf_Table Parameters Applicable To All Configurations
- Malloc_Based_Allocation specifies, when True, that the storage pool configuration Externally Managed Heap should be used. In this configuration the heap is externally managed (for example, the operating system manages the heap) and the default processing for allocation/deallocation requests is to call malloc/free. When this parameter is set to False, the Apex Managed Heap configuration is selected and the default processing for allocation/deallocation requests is to call the Default_Pool's routines (that implement an Apex managed heap). The default setting for this parameter (that is, to use the Externally Managed Heap configuration) is True for all Apex products except Apex Embedded for Rational Exec and Apex for Tornado.
- External_Pool_Protection specifies the mutual exclusion mechanism that should be used for the External_Pool object. The default value is set appropriately for the default External_Pool object specified in the Storage_Pool_Configuration (see below). If the External_Pool type is changed, the protection may also need to be changed. The possible values are:
- Specify None if the application is non-tasking or if the external heap management is multitasking safe, for example, malloc/free in a threaded environment. This is the default for applications that are non-tasking or use a multitasking safe external heap.
- Specify Lock_Mutex if allocations/deallocations by tasks require mutual exclusion but kernel allocations/deallocations do not, that is, if concurrent calls to the external heap management from the kernel and a task cannot interfere with each other.
- You must specify Disable_Preemption if allocation/deallocation requests (to the external heap management) by tasks could interfere with allocation/deallocation requests by the kernel (as well as each other). This is the default for multitasking applications that use a non-threaded runtime.
- Storage_Pool_Configuration specifies the routines to be used to create the runtime's standard storage pools. See Storage Pool Configuration.
Mem_Alloc_Conf_Table Parameters Applicable Only To Apex Managed Heap
The following parameters apply only in the Apex Managed Heap configuration and are only relevant to the configuration of the Default_Pool:
- Allocation_Strategy specifies the strategy to be used when choosing a block from the free list: First_Fit or Best_Fit. The default is First_Fit.
- Private_Storage_Pools specifies, when true, that a reclaimable collection should be assigned a private (rather than a shared) storage pool. A collection is reclaimable if the scope of the base access type (associated with the collection) will be exited before the program terminates, for example, if the base access type is declared within a subprogram. If a private storage pool is used for a reclaimable collection, the collection's reclamation is implemented via the deallocation of the entire pool rather than the separate deallocation of each object in the collection, resulting in faster reclamation.
- Small_Block_Sizes_Table is used to control the free block caching of the Default_Pool. The table contains a list of small block sizes. If the Small_Block_Sizes_Table is initialized to (8, 16, 128), three small block lists will be created that will link free blocks of 8 bytes, 16 bytes, and 128 bytes respectively. Allocation sizes between these values will yield an object of the larger small block size. For example, user objects of size 20 would be allocated as 128 byte small blocks. Sizes must be multiples of 2 * (Address'Size / Storage_Unit) and the sizes must be specified in ascending order.
- Small_Block_Sizes_Address points to the Small_Block_Sizes_Table.
- Num_Small_Block_Sizes specifies the number of small free block lists to be managed in the Default_Pool. The specified value must equal the number of entries in the Small_Block_Sizes_Table.
- Min_List_Length specifies the minimum list length of a small blocks list. This value determines when an allocation request to the default storage pool is prevented from coalescing blocks off of a list. Also, during deallocation, the heap manager decides whether the freed memory should go back on a small blocks list or an attempt should be made to coalesce the memory with its neighbors and be put on the regular free list. Note that the actual length is often shorter when little or no deallocation is being done.
- Min_Size specifies the minimum size object that the user intends to allocate. This determines the size at which an over-large space will be broken into a perfect fit and a new free storage block. For example, if a user asks for 1000 bytes and the next free slot has 1500 bytes and if Min_Size < 500, the user will get exactly 1000 of the free space, and the remainder (500 bytes less overhead of the header) will be put back on the free list. If Min_Size > 500, the user gets 1500 bytes. This configuration parameter is useful for controlling fragmentation. This specified value should never be larger than the smallest small block size in the Small_Block_Sizes_Table.
Storage Pool Configuration
Rational's standard storage pool types are:
- External_Pool
- Default_Pool
- Null_Pool
- Heterogeneous_Pool
- Homogeneous_Pool
- Kernel_Pool
The Apex runtime creates single instances of the External_Pool, Null_Pool, and Kernel_Pool types. These pool objects are created during the initialization of the runtime. If Apex Managed Heap is configured, a single instance of the Default_Pool is also created during initialization and during the execution of a program multiple instances of Heterogeneous_Pool and Homogeneous_Pool objects could be created and destroyed. The Default_Pool, Heterogeneous_Pool, and Homogeneous_Pool types are not used if Externally Managed Heap is configured.
Figure 8 shows how the Externally Managed Heap Architecture can be configured for your application. Within the V_Usr_Conf package you can change the External_Pool type or you can modify the behavior or the default External_Pool by changing the bodies of the pool's callout routines.
Figure 7 Externally Managed Heap Configuration
![]()
The External_Pool and Kernel_Pool objects provide the lowest level memory management interface for the application and the kernel respectively. Usually, these objects are nothing more than interfaces to an external heap management implementation, for example, malloc/free supplied by the operating system.
The exception is Apex Embedded for Rational Exec, where the default configuration is to have the External_Pool be the application's interface to the kernel's storage management services which are provided by the Kernel_Pool.
The Default_Pool object is the default heap manager for the application if Apex Managed Heap is configured. All collections are assigned to this pool except for ones that have a non-default initial size (specified with a Storage_Size clause or Collection_Policy pragma) or have a storage pool explicitly assigned via a Storage_Pool clause. Some reclaimable collections may also be excluded from the Default_Pool if Private_Storage_Pools are enabled. The Default_Pool is layered over the External_Pool. The External_Pool is called when the Default_Pool needs more memory.
The Null_Pool object does not support allocations, that is, any attempt to allocate memory from the pool results in the raising of a Storage_Error exception. A collection with a Storage_Size clause or a Collection_Policy pragma that specifies zero storage elements is assigned to this pool. If the size expression is statically evaluated to zero, the access type is not explicitly assigned a collection or storage pool, however the Storage_Pool attribute would still identify the Null_Pool for the access type.
Heterogeneous_Pool and Homogeneous_Pool objects are created for collections that have a non-default initial size (when Apex Managed Heap is configured). If a Storage_Size clause or Collection_Policy pragma has been specified, the collection is assigned a dedicated storage pool object of the specified initial size (if a Storage_Size clause was given the pool is not extensible). If Private_Storage_Pools are enabled, these pool objects will also be created for reclaimable collections, using the default collection parameters for initial and extension sizes (see Pragmas).
The storage pool objects described above are sufficient for most applications. Usually, the memory management configuration parameters described in this chapter provide all of the flexibility that an application might require. However, if an application's memory management requirements are not met by the pool types supplied with Apex, the application can replace them with its own storage pool types. The standard storage pool objects are replaced by supplying a subprogram to create an alternative object. The list of storage pool object creation routines is specified in the Storage_Pool_Configuration field of the Mem_Alloc_Conf_Table.
Each standard storage pool type has a creation routine specified by a entry in the Storage_Pool_Configuration. Set a table entry to the value Use_Standard_Pool_Routine to utilize the runtime's default, or disable the use of a pool by setting its entry to the value Use_No_Pool_Routine, or alternatively, provide your own storage pool creation routine. See the parameter profiles below.
User defined standard storage pool creation routines must return an object of the following (Ada95) type:
System.Storage_Pools.Rational.Storage_Pools.Runtime_Pool_Pointer;and must have the following parameter profiles:
function Create_External_Pool_Routine (Reclaimable : Boolean; Multitasking : Boolean) return Runtime_Pool_Pointer; function Create_Default_Pool_Routine (Reclaimable : Boolean; Multitasking : Boolean; Initial_size : Alloc_t; Extension_Size : Alloc_t) return Runtime_Pool_Pointer; function Create_Null_Pool_Routine return Runtime_Pool_Pointer; function Create_Heterogeneous_Pool_Routine (Reclaimable : Boolean; Multitasking : Boolean; Initial_Size : Alloc_t; Extension_Size : Alloc_t) return Runtime_Pool_Pointer; function Create_Homogeneous_Pool_Routine (Reclaimable : Boolean; Multitasking : Boolean; Initial_Size : Alloc_t; Extension_Size : Alloc_t; Cell_Size : Alloc_t) return Runtime_Pool_Pointer; function Create_Kernel_Pool_Routine (Reclaimable : Boolean) return Runtime_Pool_Pointer;
A simple alternative to completely replacing the Apex provided External_Pool and Kernel_Pool types is to modify their callout routines provided in the configuration packages, see Storage_Management_Callout Routines.
External Pool Callouts
Storage_Management_Callout Routines
External Pool Callouts provide a mechanism for easily customizing the External_Pool's external interface. This interface constitutes the lowest level of the Apex memory management system. It is where the runtime calls the operating system, or in the case of Apex Embedded for Rational Exec, the kernel, to allocate or deallocate memory. These callout subprograms are found in the body of the V_Usr_Conf package in the usr_conf.ss subsystem. An application is free to change the bodies of these subprograms. The following callouts are provided:
function V_Extern_Alloc_Callout (Size : V_I_Types.Alloc_T) return System.Address;
V_Extern_Alloc_Callout is called by the (default configured) External_Pool when it needs to allocate memory. The default implementation of this callout calls malloc, except with Apex Embedded for Rational Exec products, where the Rational Exec kernel is called.
procedure V_Extern_Free_Callout (A : System.Address);
V_Extern_Free_Callout is called by the (default configured) External_Pool when it wants to deallocate memory. The default implementation of this callout calls free, except with Apex Embedded for Rational Exec products, where the Rational Exec kernel is called.
function V_Extern_Size_Callout return V_I_Types.Allloc_T;
V_Extern_Size_Callout is called by the (default configured) External_Pool to determine the maximum amount of memory that could be allocated. The default implementation of this callout returns the value reported by rlimit during runtime initialization. The Apex Embedded for Rational Exec product returns Alloc_T'Last from this callout.
Multitask_Safe Malloc
Multitask_Safe_Malloc is an Apex provided implementation of malloc, free, and several other standard C heap management routines. This implementation is suitable for use in mixed language applications, for example, Ada with C/C++, that are multitasking but non-threaded (threaded applications already have the necessary mutual exclusion support in the operating system provided heap management). The package Multitask_Safe_Malloc is provided in the alloc subdirectory of non-threaded usr_conf.ss views. The use of this package requires that some configuration changes be made to the body of the configuration package V_Usr_Conf. The required changes are marked with the string MULTITASK_SAFE_MALLOC in the package body and principally affect the External Pool Callouts. See the Multitask_Safe_Malloc and V_Usr_Conf packages for additional configuration information, implementation details, and restrictions.
Kernel Pool Callouts
Kernel Pool Callouts provide a mechanism for customizing the Kernel_Pool's internal interface. These subprograms are called by the kernel when it needs to allocate/deallocate memory. Memory management in the kernel is supported by these callouts, where the default implementation of each callout is to make a call to the corresponding Kernel_Pool object routine.
For all products except Apex Embedded for Rational Exec and Apex for Tornado, these callout subprograms are found in the body of the V_Usr_Conf package, in the usr_conf.ss subsystem. In Apex for Tornado, the callout subprograms are located in the body of the package V_Krn_Conf in the krn_conf.ss subsystem.
Note: There are instances of this package for each target board.
The Kernel_Pool callouts (except in Apex Embedded for Rational Exec) have the following profiles:
function V_Krn_Alloc_Callout (Size : Integer) return System.Address;V_Krn_Alloc_Callout is called by the kernel when it needs to allocate memory. The default implementation of this callout calls the allocate entry of the Kernel_Pool object.
procedure V_Krn_Free_Callout (A : System.Address);
V_Krn_Free_Callout is called by the kernel when it wants to deallocate memory. The default implementation of this callout calls the deallocate entry of the Kernel_Pool object.
procedure V_Krn_Pool_Init_Callout;
V_Krn_Pool_Init_Callout is called by the kernel to initialize its underlying memory management support. The default implementation of this callout calls a routine that creates the Kernel_Pool object.
procedure V_Krn_Pool_Finalize_Callout
V_Krn_Pool_Finalize_Callout is called by the kernel to finalize its underlying memory management support. The default implementation of this callout calls the Kernel_Pool object's Finalize routine.
Figure 8 Apex Managed Heap Configuration
![]()
The Kernel_Pool callouts in Apex Embedded for Rational Exec are found in the V_Alloc_Support package within the V_Krn_Conf package in the krn_conf.ss subsystem views (each target board has its own instance of this package). Figure 8 shows how the Apex Embedded for Rational Exec version of the Apex Managed Heap Architecture can be configured for your application. You can modify the behavior of the default Kernel_Pool by changing the body of the V_Krn_Conf.V_Alloc_Support package. You can also configure the V_Usr_Conf package. This enables you to change the types of the Default_Pool, External_Pool, and Kernel_Pool and to modify the behavior of the default External_Pool by changing the bodies of its callout routines.
The Kernel_Pool callouts have the following profiles:
procedure Init(Heap_Bottom : System.Address; Heap_Size : Natural; Unaligned_Min_Size : Natural);
Init initializes memory to be used for kernel allocations. Init is called with the bottom and size of the kernel's heap after the kernel has already allocated the kernel, interrupt and main task stacks from the top of the heap area. The Unaligned_Min_Size parameter has been set to the size of the kernel's task control block. Its only a hint at what the minimum size of an allocation should be. It can be ignored.
function Alloc_New(Size : Natural; Prg : Krn_Defs.A_Krn_Pcb_T) return System.Address;
Alloc_New is called whenever the kernel needs memory for a kernel data structure, such as a task control block, a task stack, or a request for memory from the user program. The Prg parameter indicates which program the request is being made for. Prg is set to V_Krn_Aug.Program_Kernel to indicate memory being requested for a kernel data structure or a stack for a kernel task.
procedure Alloc_Free(Addr: System.Address; Prg : Krn_Defs.A_Krn_Pcb_T);
Alloc_Free is called whenever the kernel frees memory previously allocated.
User Defined Storage Pools
Ada 95 applications are not limited to the memory management configured in the Apex runtime. An application can supply its own memory management implementation via storage pools, see Storage Clauses. This mechanism enables an application to individually tailor the memory management policies for each (base) access type.
As an aid to application developers, Apex provides the type definitions for its standard storage pool types in the package System.Storage_Pools.Rational.Storage_Pools in the Ada95 lrm.ss views. These types are extensions of the Root_Storage_Pool type defined in System.Storage_Pools (see Ada95 RM 13.11 and Figure 9). A developer is free to use these extensions, extend these extensions, and/or define their own extensions of the Root_Storage_Pool type.
Figure 9 Storage Pool Types
![]()
The runtime's standard storage pool objects are instances of these types: the External_Pool and Kernel_Pool objects are External_Pool types, the Default_Pool is a Heterogeneous_Cached pool object, fixed sized pools are instances of the Homogeneous_Bounded or Heterogeneous_Bounded types, and non-fixed size, reclaimable pools are of the Heterogeneous_Unbounded type.
There are several situations where the application's explicit use of a standard storage pool type offers the best solution to a memory management problem. Two of these are: Stack Based Storage Pools and Storage Pools for Signal and Interrupt Handlers. In cases like these the use of a specific storage pool object, chosen by the application rather than the compiler/runtime, has significant advantages.
Stack Based Storage Pools
When a Storage_Size clause is given for an access type, that access type's collection is given a dedicated storage pool if Apex Managed Heap is configured (or shares the External_Pool if Externally Managed Heap is configured). However,even if the collection is given a dedicated storage pool, the memory managed by the pool (and thus the memory for new allocations) must be allocated from another pool, for example, the Default_Pool.
An alternative that is applicable to reclaimable pools is to allocate the memory for the pool from the stack. For example, if the storage pool is declared in a subprogram, the pool can be created on the subprogram's stack frame when the subprogram is called, and the storage will be automatically reclaimed when the subprogram returns. The advantages of this approach are that no other storage pools need to be used to support the stack based pool and that mutual exclusion support can be excluded from the stack based pool if its not visible to multiple tasks. This can result in significant performance improvements. Of course the downside to this approach is that the application must be able to insure that the stack will have sufficient space for the worst case pool size that could be configured (see Managing Stack Storage). Below is a simple example of a stack based storage pool:
with System.Storage_Pools.Rational.Storage_Pools; with Text_Io; procedure Example_Stack_Storage_Pool is type Message (<>); type Message_Pointer is access Message; type Message (Length : Natural) is record Next : Message_Pointer; Data : String (1 .. Length); end record; Message_Pool_Size : constant := 2048; Message_Pool : System.Storage_Pools.Rational.Storage_Pools. Heterogeneous_Bounded_Pool (Reclaimable => False, Pool_Size_In_Storage_Elements => Message_Pool_Size); for Message_Pointer'Storage_Pool use Message_Pool; List : Message_Pointer := null; Last : Natural; Buffer : String (1 .. 80); begin loop Text_Io.Get_Line (Buffer, Last); exit when Last <= 0; List := new Message'(Length => Last, Next => List, Data => Buffer (1 .. Last)); end loop; -- Procedures that process the List would be called here. -- The List is deallocated when we return from this subprogram. end Example_Stack_Storage_Pool;Storage Pools for Signal and Interrupt Handlers
The storage pool is declared in a library-level package so that it will be visible to the handler. We use a Bounded Pool because any attempt to extend an Unbounded Pool from within the handler would invoke an illegal mutex lock in the underlying pool..
with System.Storage_Pools.Rational.Storage_Pools; package Example_Interrupt_Storage_Pool is package Storage_Pools renames System.Storage_Pools.Rational.Storage_Pools; type Object is private; type Object_Pointer is access Object; Object_Pool_Size : constant := 2048; Object_Pool : Storage_Pools.Guarded_Heterogeneous_Bounded_Pool (Reclaimable => False, Pool_Size_In_Storage_Elements => Object_Pool_Size, Mutex => Storage_Pools.Default_Async_Mutex_Support); for Object_Pointer'Storage_Pool use Object_Pool; private type Object is null record; end Example_Interrupt_Storage_Pool;Note: The pool object in the example is declared in a library package and that its a fixed size pool. By locating the object in a library package we insure that the handler will have the appropriate visibility. The pool is non-extensible so that we will not encounter problems caused by insufficient mutual exclusion support if an attempt was made to extend the pool from a handler (since the underlying pool that would supply the memory for the extension is probably not signal/interrupt handler safe).
Memory Management Instrumentation
The optimal management of a resource such as memory requires careful planning, and one of the key elements of this process is the gathering of statistics about what is getting allocated/deallocated when and by whom. Much of the same data that's used for these statistics is also useful for investigating memory related errors. Rational provides several tools to assist with these analysis and debug efforts:
Apex Trace (Apex embedded only)
The Apex Tasking Logic Analyzer can provide a wealth of information about various aspects of your application's behavior, including memory usage. Allocations can be tracked at both the collection and External_Pool (malloc/free) levels. The tool can be used stand-alone or from with the Apex debugger. It is also possible to instrument application defined storage pool types so that their usage can be analyzed.
See the Using the Tasking Logic Analyzer for more information. Note that this manual is only available if you have an Apex embedded product installed.
Configuration Package Callouts
The callout routines provided in the V_Usr_Conf and V_Krn_Conf configuration packages (see Storage_Management_Callout Routines) can be easily modified to provide instrumentation and/or debugging support for the External_Pool and Kernel_Pool objects. These callouts expose the interface of the kernel to the Apex memory management system and the interface of the Apex memory management system to the external (to Apex) memory management (for example, malloc/free).
Collection Callouts
Instrumentation support for collections is provided via the package V_I_Collection_Callout which is found in the alloc subdirectory of usr_conf.ss views. Every access type in an Ada program has a collection associated with it. Instances of new allocations and unchecked_deallocations are implemented via compiler generated calls to this collection layer.
The subprograms in this package provide a mechanism for registering callout procedures. A user supplied procedure is registered by supplying its address. When an event occurs for which there is a registered callout, the user procedure is called. To unregister a callout, the registry procedure is called with a System.Null_Address parameter. Multiple callouts for the same event are not supported; the last registered procedure is the one that will be called.
Note: The use of the collection callouts requires that a usr_conf.ss view be imported. The importing of this view will have the side effect of inclusion, at link time, of the V_Usr_Conf package from the imported view rather than the default V_Usr_Conf in the runtime archive library.
User supplied callout procedures should not raise exceptions or perform any operations that might re-enter the storage management system, for example, a new allocation, and should not perform any operation that might block the currently executing task. This callout interface is intended to provide data collection services that support the analysis of memory usage in the application. With respect to the storage management system, these callouts provide a read-only interface.
The Collection_Id type used in the following declarations is an address-sized parameter.
A procedure with this profile will be called when a collection is created:
procedure Report_Creation (The_Collection : in Collection_Id);
To register such a procedure use:
procedure Register_Creation_Callback (Address_Of_Report_Creation_Routine : in Address);
A procedure with this profile will be called when a collection is destroyed:
procedure Report_Destruction (The_Collection : in Collection_Id; Deallocated_At_Destruction : in Storage_Count);
To register such a procedure use:
procedure Register_Destruction_Callback (Address_Of_Report_Destruction_Routine : in Address);
A procedure with this profile will be called when an allocation occurs:
procedure Report_Allocation (The_Address : in Address; The_Size : in Storage_Count; The_Collection : in Collection_Id);
To register such a procedure use:
procedure Register_Allocation_Callback (Address_Of_Report_Allocation_Routine : in Address);
A procedure with this profile will be called when an deallocation occurs:
procedure Report_Deallocation (The_Address : in Address; The_Size : in Storage_Count; The_Collection : in Collection_Id);
To register such a procedure use:
procedure Register_Deallocation_Callback (Address_Of_Report_Deallocation_Routine : in Address);
A procedure with this profile will be called when a collection for a controlled type is finalized:
procedure Report_Finalization (The_Collection : in Collection_Id);
To register such a procedure use:
procedure Register_Finalization_Callback (Address_Of_Report_Finalization_Routine : in Address);
Managing Stack StorageFor applications that do not use tasking, stack usage is very straightforward. The stack is simply the process stack. For applications that use tasking, stack management is more complicated. The main task still uses the process stack, however each additional task gets a stack allocated from the heap. The runtime system keeps track of the current stack pointers and stack limits for each task and unless suppressed, the compiler generates checks to trap stack overflows. While overflows provide an indication of how much stack space a task is using, it usually a cumbersome way to determine the application's stack requirements. To assist in this effort the Apex debugger provides stack usage analysis. See "Display Stack Usage and Location" in Using the Apex Debugger for more information.
Once the appropriate amount of stack storage has been determined, its configuration can be specified in several different ways:
- Storage_Size Clause and Pragma Storage_Size
- Pragma Main
- Configuration Package Parameters
- Session Switches
In addition to the task stacks used for normal processing there are other stacks used by the runtime system . Like the task stacks, these are configurable with pragmas, configuration parameters, and/or switches.
Figure 10 Task Stack
![]()
Storage_Size Clause and Pragma Storage_Size
A Storage_Size clause or a Storage_Size pragma can be used to specify a task's stack size (see Ada95 RM 13.3). The use of the clause or pragma will override the defaults given in a Pragma Main specification (as well as any configuration package or session switch defaults). The amount of stack space allocated to a task, T, can be determined by evaluating its T'Storage_Size attribute.
Pragma Main
In addition to allowing the specification of a default for task stack sizes (the Task_Stack_Size_Default argument), Pragma Main provides for the configuration of several other stack storage parameters via these arguments:
- Exception_Stack_Size: amount of additional task stack space reserved for exception handling
- Idle_Stack_Size: size of the idle task's stack
- Signal_Stack_Size: size of the stacks used for task entries bound to interrupts
- Main_Stack_Size: the size of the main task's stack
- Wait_Stack_Size: size of the additional stack space used to support Apex's fast task rendezvous optimization
- Zero_Stacks_Enabled: support for the Apex debugger's stack analysis
See pragma Main in the Ada Compiler Reference for more information.
Configuration Package Parameters
The configuration package V_Usr_Conf, which is found in usr_conf.ss views, provides the same configuration parameters described above for Pragma Main. Setting the parameters in the body of V_Usr_Conf enables them to be applied to any application program which imports the configured usr_conf.ss view. Task or program specific configurations given by a pragma Main, or Storage_Size pragma or clause, or a session switch will override the defaults given in V_Usr_Conf.
- Main_Task_Stack_Size
- Default_Task_Stack_Size
- Exception_Stack_Size
- Idle_Stack_Size
- Signal_Task_Stack_Size
- Wait_Stack_Size
- Zero_Stacks_Enabled
For additional information about stack configuration with Apex Embedded for Rational Exec, see the Configuration Guide for Rational Exec.
The default V_Usr_Conf package that you are using is:
For native development
$APEX_BASE/ada/usr_conf.ss/${APEX_ARCH_OS} .language_variant.$APEX_PROD_VERSION .rel/v_usr_conf.2.ada
For cross development
$APEX_BASE/ada/usr_conf.ss/target_family/ compiler_variant/board_variant/language_variant .$APEX_PROD_VERSION.rel/v_usr_conf2.ada
Note: The meta names used in this path are set when Apex is invoked.The environment variable values can be displayed using Tools > Session > Environment.
Session Switches
The main task's stack size and the size of signal stacks can be specified via the Apex session switches:
These switches will override the defaults specified in the V_Usr_Conf package but will not override those specified with pragma Main arguments.
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |