Files | |
file | mem.h |
Memory-related utility functions. | |
file | pmem.c |
Platform-specific implementations of memory-related functions. | |
file | mem.c |
Memory-related utility functions. | |
Data Structures | |
struct | SysMemFns |
Defines a structure to hold a set of system memory allocation functions. More... | |
Macros | |
#define | MemAllocAlign(_x) (((_x) + 7) & (~7)) |
Define byte alignment of allocation via MemAlloc. | |
Typedefs | |
typedef void *() | SysAllocFn(size_t cbSize) |
Type of a callback function for allocating system memory. More... | |
typedef void() | SysFreeFn(void *pbMem) |
Type of a callback function for de-allocating system memory. More... | |
typedef struct SysMemFns | SysMemFns |
Defines a structure to hold a set of system memory allocation functions. | |
Enumerations | |
enum | MEM_ALLOC_values { MEM_ALLOC_RETURN_NULL = 0 , MEM_ALLOC_EXIT_PROCESS = 1 , MEM_ALLOC_RIP_LOWMEM = 2 } |
Functions | |
void | MemLogInit (const char *mps_log, unsigned long mps_telemetry) |
Set up the memory system to log to a file. More... | |
HqBool | MemInit (size_t *RIP_maxAddressSpaceInBytes, size_t *RIP_workingSizeInBytes, void *pMemory, SysMemFns *pSysMemFns) |
Initialise the memory arena and create the memory pool for use in skin. More... | |
void | MemFinish (int32 fError) |
Release the arena of managed memory. More... | |
void * | MemAlloc (size_t cbSize, HqBool fZero, int actionOnFail) |
Allocate a block of memory for use by the skin layer. More... | |
void * | MemRealloc (void *ptr, size_t cbSize) |
Reallocate a block of memory that was allocated by a prior call to MemAlloc(). More... | |
void | MemFree (void *pbMem) |
Free a block of memory that was allocated by a prior call to MemAlloc(). More... | |
void * | SysAlloc (size_t cbSize) |
Allocate a block of system memory for use by the skin layer. More... | |
void | SysFree (void *pbMem) |
Free a block of memory that was allocated by a prior call to SysAlloc(). More... | |
mps_arena_t | MemGetArena (void) |
Returns the single arena of managed memory that is shared between the core RIP and the skin layer. | |
void | GetMemorySizesInBytes (size_t *addressSpace, size_t *physicalMemory) |
Obtain the best possible measure, in bytes, of the virtual address space and the amount of physical RAM installed on the host machine. More... | |
uint32 | GetPeakMemoryUsageInMegabytes (void) |
Obtain the best possible measure, in megabytes, of the maximum amount of memory that this process has ever used. More... | |
size_t | GetPeakArenaSize (void) |
Obtain the peak arena size in bytes. More... | |
size_t | GetArenaCommitLimit (void) |
Obtain the commit limit of the arena in bytes. More... | |
int32 | GetCurrentProcessHandleCount (void) |
Obtain current number of open handles this process has. More... | |
The Harlequin RIP sub-allocates memory from an arena, which is shared by the core RIP and the skin. The hosting application must create this arena with before booting the RIP, and destroy it after shutting the RIP down. The creation and destruction of the memory arena are performed using the MemInit() and MemFinish() functions, however you should not call these functions directly: they are normally called in the correct sequence by the SwLeDo() function, via SwLeSDKStart() and SwLeSDKEnd().
Once allocated, the RIP memory arena is managed using a sophisticated scheme known as the Memory Pool System (MPS). The MPS interface itself is not defined or described here. See Configuring memory for information about how the MPS arena should be configured for use in your application.
There are two ways in which the RIP's memory arena can be allocated, depending on the arguments passed to MemInit() by SwLeSDKStart() or SwLeDo(). If the arena is allocated manually, then the host application simply passes a pointer to the base address of the pre-allocated block, along with an indication of its total size. MPS then takes over all internal management of this block, but does not allocate or release the block itself. (In MPS terminology, it is a "client arena"). This is the most common pattern for embedded applications. For host-based applications, it is more common to allocate the arena automatically. In this case, no base pointer is passed to MemInit(). MPS takes care of the initial memory reservation, as well as all of its internal management.
Regardless of how it is allocated, a key characteristic of the RIP arena is that it is constrained. It is not allowed to expand beyond the maximum size specified to MemInit(). The hosting application is thus able to limit the memory in which the RIP will run.
Another important characteristic of the RIP's memory arena is that it contains a skin pool, which is automatically created and managed with MPS. This pool allows the skin to use some portion of the total RIP arena for itself, while the rest is used by the core. This feature is important, because it allows the total size constraint to remain effective for the skin and core in combination.
To allocate and free memory within the skin pool, use the MemAlloc() and MemFree() functions. Almost all memory allocations within the skin should be handled with these functions. In addition, there is a MemRealloc() function, whose use in skin programming is discouraged: re-allocators are notoriously prone to misuse. The MemRealloc() function has mainly been provided to help the integration of third-party libraries, allowing their memory management to use the skin pool.
There are also functions for allocating memory from an MPS pool managed by the RIP. These are SwAlloc(), SwRealloc(), and SwFree(). These functions can only be used while the RIP is active, and so are not appropriate for use cases where allocations may be made before the booting the RIP or after shutting the RIP down, or allocations that need to persist across RIP invocations. The SwAlloc() and SwRealloc() functions have one advantage over MemAlloc() and MemRealloc(), which is that they will automatically invoke the RIP's low-memory recovery mechanisms if the thread making the SwAlloc() call originated inside the RIP, and is calling out to the skin when the allocation is made. Allocations (especially large allocations) made when the RIP is close to using all of the MPS arena may fail when calling MemAlloc(), whereas SwAlloc() may be able to recover cache memory, compress buffers, purge memory to disk, or use other mitigating actions to make the allocation succeed.
For the most part, the RIP memory arena services all memory requirements, whether they come from the skin or the core. However, this interface also supports the existence of a second, conceptually-distinct region of memory. This region is broadly known as "system" memory. System memory can be viewed as whatever remains for use by the hosting process, once the RIP's main arena has been allocated. System memory is completely out of bounds to the core RIP, and entirely hidden from the MPS. System memory, where it exists, is managed by the operating system on the host platform (or embedded device). Beyond that broad characterization, this interface does not define exactly what system memory is. In a host-based RIP application, it could simply be "leftover" memory being handled with the low-level C malloc()
and free()
functions, or perhaps with the C++ new
and delete
operators. Alternatively, it might be a region ringfenced for a specific purpose, such as holding a page of raster data. To allocate and free such system memory, use the SysAlloc() and SysFree() functions. (There is deliberately no SysRealloc()
function). These functions have default implementations on all supported platforms, but you may also provide your own implementations when calling SwLeDo() or SwLeSDKStart().
When writing code in the skin layers:
Skin memory management is summarised in the following diagram.
Some utility and inquiry functions are provided by the SDK library.
typedef void*() SysAllocFn(size_t cbSize) |
Type of a callback function for allocating system memory.
Functions of this type are passed in the SysMemFns structure to MemInit(), and used by SysAlloc().
typedef void() SysFreeFn(void *pbMem) |
enum MEM_ALLOC_values |
Actions to take on failing to allocate memory.
Enumerator | |
---|---|
MEM_ALLOC_RETURN_NULL | Return |
MEM_ALLOC_EXIT_PROCESS | Exit the process on failing to allocate memory. This value is equivalent to |
MEM_ALLOC_RIP_LOWMEM | Provoke the core RIP's low memory handling on failing to allocate memory, then retry the allocation. This value may be ORed with either MEM_ALLOC_RETURN_NULL or MEM_ALLOC_EXIT_PROCESS to determine what to do if the allocation retry fails. This flag must only be used when the RIP has been booted and not yet shutdown. |
size_t GetArenaCommitLimit | ( | void | ) |
Obtain the commit limit of the arena in bytes.
int32 GetCurrentProcessHandleCount | ( | void | ) |
Obtain current number of open handles this process has.
void GetMemorySizesInBytes | ( | size_t * | addressSpace, |
size_t * | physicalMemory | ||
) |
Obtain the best possible measure, in bytes, of the virtual address space and the amount of physical RAM installed on the host machine.
addressSpace | A pointer to a variable where the address space size usable will be stored. |
physicalMemory | A pointer to a variable where the amount of physical memory will be stored. |
size_t GetPeakArenaSize | ( | void | ) |
Obtain the peak arena size in bytes.
uint32 GetPeakMemoryUsageInMegabytes | ( | void | ) |
Obtain the best possible measure, in megabytes, of the maximum amount of memory that this process has ever used.
void* MemAlloc | ( | size_t | cbSize, |
HqBool | fZero, | ||
int | actionOnFail | ||
) |
Allocate a block of memory for use by the skin layer.
Always use MemAlloc() and MemFree() to manage allocations in the skin where possible. These functions allow the memory to be managed more effectively, and also allow the skin code to be subjected to the same heap limitation controls as the core RIP. Direct use of malloc can cause the skin to overflow any such limitations that might be in place.
cbSize | The amount of memory required, measured in bytes. |
fZero | Pass TRUE if you require the memory block to be initialized with zeros, otherwise pass FALSE . |
actionOnFail | Controls failing behaviour. This will be a combination of the MEM_ALLOC_values values. If passed MEM_ALLOC_EXIT_PROCESS (or TRUE ), the entire host application will be terminated if the allocation fails. Otherwise, a failure will result in a NULL return value. It is usually better to pass MEM_ALLOC_RETURN_NULL (or FALSE ), and write additional code to handle failed allocations. |
NULL
if the allocation failed. void MemFinish | ( | int32 | fError | ) |
Release the arena of managed memory.
A call to this function should be one of the final acts in shutting down the host application. Once this function has been called, neither the RIP nor the skin layer will be permitted to use the shared arena of managed memory.
fError | Indicates whether the application is shutting down in an error state. Pass zero if the application is shutting down successfully. Pass any non-zero to indicate an error state. |
void MemFree | ( | void * | pbMem | ) |
Free a block of memory that was allocated by a prior call to MemAlloc().
pbMem | The memory block to be freed. |
HqBool MemInit | ( | size_t * | RIP_maxAddressSpaceInBytes, |
size_t * | RIP_workingSizeInBytes, | ||
void * | pMemory, | ||
SysMemFns * | pSysMemFns | ||
) |
Initialise the memory arena and create the memory pool for use in skin.
The core RIP and the demonstration skin will share the same memory arena. However, the skin will have its own pool within that arena. It is possible to apply a constraint to the amount of address space and memory that the core RIP and the skin are permitted to use in combination. Use the RIP_maxAddressSpaceInBytes parameter to limit address space and RIP_workingSizeInBytes parameter to limit memory committed. The working space size will be limited to the maximum address space allowed.
It is also possible for the RIP to work within a region of memory that has been pre-allocated by the host application. Use the pMemory argument to do this.
This function must be called before booting the RIP, and before any use of the other skin memory-management functions defined in this file.
[in,out] | RIP_maxAddressSpaceInBytes | A pointer to the size of maximum address space the RIP can use, measured in bytes. If the value referenced is greater than zero, it limits the address space available to map memory pages into. If the value referenced is zero, the RIP will pick a size based on the address space available on the operating system and the working space size. On a successful exit, this is set to the actual size of address space used for the RIP. |
[in,out] | RIP_workingSizeInBytes | A pointer to the size of working memory permitted for the RIP, measured in bytes. If the value referenced is greater than zero, it sets the maximum amount of physical memory the RIP will commit. If the value is zero, the function will calculate a default size suitable for testing the RIP running as the sole application using all the memory resources of the machine. On a successful exit, this is set to the actual size of working memory applied for the RIP. |
pMemory | Points to a caller-allocated memory block that the RIP should use. If this is non-NULL , the RIP will work within this memory block, rather than allocating its own memory. The size of the block is defined by RIP_workingSizeInBytes, which must not be zero. If this pointer is NULL , then the RIP will allocate and manage its own memory, suitably constrained by RIP_maxAddressSpaceInBytes and RIP_workingSizeInBytes. | |
pSysMemFns | Points to a structure providing the suite of system memory handling functions for use by SysAlloc(), and SysFree(). If this is NULL the default functions are used, which are implemented using the OS malloc() and free() . |
TRUE | The memory subsystem was initialised correctly. The values pointed to by RIP_maxAddressSpaceInBytes and RIP_workingSizeInBytes have been updated to reflect the actual values used, taking into account the machine and operating system characteristics. |
FALSE | The memory subsystem failed to initialise. This will usually be due to specifying either too little memory, or requesting more address space or memory than the process hosting the SDK has remaining. The values in RIP_maxAddressSpaceInBytes and RIP_workingSizeInBytes are not affected. |
void MemLogInit | ( | const char * | mps_log, |
unsigned long | mps_telemetry | ||
) |
Set up the memory system to log to a file.
[in] | mps_log | Filename for MPS memory logging. If the MPS library linked in the RIP supports logging, all of the enabled telemetry will be written to this file. |
mps_telemetry | MPS telemetry options. If the MPS library linked in the RIP supports logging, this option controls which events are logged to the file. |
void* MemRealloc | ( | void * | ptr, |
size_t | cbSize | ||
) |
Reallocate a block of memory that was allocated by a prior call to MemAlloc().
ptr | The memory block to be reallocated. |
cbSize | The amount of memory required, measured in bytes. |
void* SysAlloc | ( | size_t | cbSize | ) |
Allocate a block of system memory for use by the skin layer.
Uses the function passed via the SysMemFns structure to MemInit(), or malloc()
if no structure was passed.
Allocate memory when it is not possible or appropriate to use MemAlloc().
cbSize | The amount of memory required, measured in bytes. |
NULL
if the allocation failed. void SysFree | ( | void * | pbMem | ) |
Free a block of memory that was allocated by a prior call to SysAlloc().
Uses the function passed via the SysMemFns structure to MemInit(), or free()
if no structure was passed.
pbMem | The memory block to be freed. |