Harlequin RIP SDK
SDK memory handling functions

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...
 

Detailed Description

Memory Management in the RIP

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.

RIP Memory and the Skin Pool

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.

How To Allocate from Skin Code

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.

System Memory

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().

In Summary

When writing code in the skin layers:

Skin memory management is summarised in the following diagram.

Utility and inquiry functions

Some utility and inquiry functions are provided by the SDK library.

Typedef Documentation

◆ SysAllocFn

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().

◆ SysFreeFn

typedef void() SysFreeFn(void *pbMem)

Type of a callback function for de-allocating system memory.

Functions of this type are passed in the SysMemFns structure to MemInit(), and used by SysFree().

Enumeration Type Documentation

◆ MEM_ALLOC_values

Actions to take on failing to allocate memory.

Enumerator
MEM_ALLOC_RETURN_NULL 

Return NULL immediately on failing to allocate memory. This value is equivalent to FALSE for the previous fExitOnFail parameter to MemAlloc().

MEM_ALLOC_EXIT_PROCESS 

Exit the process on failing to allocate memory. This value is equivalent to TRUE for the previous fExitOnFail parameter to MemAlloc().

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.

Function Documentation

◆ GetArenaCommitLimit()

size_t GetArenaCommitLimit ( void  )

Obtain the commit limit of the arena in bytes.

Returns
The commit limit of the arena (the amount of memory that the arena manages).

◆ GetCurrentProcessHandleCount()

int32 GetCurrentProcessHandleCount ( void  )

Obtain current number of open handles this process has.

Returns
Number of handles open by this process. -1 if the number of handles could not be obtained which should not be treated as an error because the OS may not support the collection of this information.

◆ GetMemorySizesInBytes()

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.

Parameters
addressSpaceA pointer to a variable where the address space size usable will be stored.
physicalMemoryA pointer to a variable where the amount of physical memory will be stored.

◆ GetPeakArenaSize()

size_t GetPeakArenaSize ( void  )

Obtain the peak arena size in bytes.

Returns
The value.

◆ GetPeakMemoryUsageInMegabytes()

uint32 GetPeakMemoryUsageInMegabytes ( void  )

Obtain the best possible measure, in megabytes, of the maximum amount of memory that this process has ever used.

Returns
The value in megabytes, or zero if the machine configuration can't be determined or an error occured.

◆ MemAlloc()

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.

Parameters
cbSizeThe amount of memory required, measured in bytes.
fZeroPass TRUE if you require the memory block to be initialized with zeros, otherwise pass FALSE.
actionOnFailControls 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.
Returns
A pointer to the allocated memory block, or NULL if the allocation failed.

◆ MemFinish()

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.

Parameters
fErrorIndicates 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.

◆ MemFree()

void MemFree ( void *  pbMem)

Free a block of memory that was allocated by a prior call to MemAlloc().

Parameters
pbMemThe memory block to be freed.

◆ MemInit()

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.

Parameters
[in,out]RIP_maxAddressSpaceInBytesA 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_workingSizeInBytesA 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.
pMemoryPoints 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.
pSysMemFnsPoints 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().
Return values
TRUEThe 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.
FALSEThe 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.

◆ MemLogInit()

void MemLogInit ( const char *  mps_log,
unsigned long  mps_telemetry 
)

Set up the memory system to log to a file.

Parameters
[in]mps_logFilename 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_telemetryMPS telemetry options. If the MPS library linked in the RIP supports logging, this option controls which events are logged to the file.
Note
This function must be called before MemInit() (and SwLeSDKStart(), which calls MemInit()).

◆ MemRealloc()

void* MemRealloc ( void *  ptr,
size_t  cbSize 
)

Reallocate a block of memory that was allocated by a prior call to MemAlloc().

Parameters
ptrThe memory block to be reallocated.
cbSizeThe amount of memory required, measured in bytes.

◆ SysAlloc()

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().

Parameters
cbSizeThe amount of memory required, measured in bytes.
Returns
A pointer to the allocated memory block, or NULL if the allocation failed.

◆ SysFree()

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.

Parameters
pbMemThe memory block to be freed.