previousupnext

4.7 API Conformance

This section presents notes on each LeapHeap routine, and highlights any differences in behaviour from the corresponding Windows NT API routine. The LeapHeap routines are conventionally named 'LeapAlloc', 'LeapFree' etc., a one letter difference from their NT analogues 'HeapAlloc', 'HeapFree' etc. (the names exported by the LeapHeap DLL are shorter than this for technical reasons). All LeapHeap routines are non-blocking.

The routine descriptions will be most useful for developers and those who have source code for the applications they are interested in. Specifications of the NT routines are available as part of the Microsoft Developer Network Library (MSDN) in the section Platform SDK - Win32 API - Reference.

LeapAlloc

Calls for obsolescent memory types are directed to the relict heap.

Otherwise, rounds the requested size up to the nearest 8-byte boundary. If larger than 4 KB, adds four bytes to the size before rounding to allow for a header field. Then allocates from the compartment area or big-block area according to the size. If it cannot allocate, it returns the failure code.

The effect of a request for a memory block larger than 2 GB is undefined.

LeapAlloc ignores the HEAP_GENERATE_EXCEPTIONS and the HEAP_NO_SERIALIZE flag.

LeapAlloc implements the HEAP_ZERO_MEMORY flag by clearing all the allocated memory, whether the same size as requested or larger.


LeapCompact

First of all applies the original HeapCompact function to the relict heap, ignoring the result.

Next decommits memory in the compartment area where it can find committed memory pages wholly overlain by free cells. For each such page it seizes the corresponding bits in the cell bitmap of the relevant compartment or compartments, just as if allocating the cells. If it succeeds it can decommit the page, update the commit bitmap, and release the cell bitmap bits as if freeing the memory cells.

LeapCompact does not decommit memory in the big-block area.

The return value of LeapCompact does not accurately report the largest committed free block in the LeapHeap, but a free block of the size it reports is guaranteed to exist; this non-compliance is for performance reasons. If no free block is detected (as opposed to existing), the return value is zero and SetLastError is called with a parameter of NO_ERROR. LeapCompact does not otherwise return the failure code.

LeapCompact ignores the HEAP_NO_SERIALIZE flag.


LeapCreate

This function never creates a heap. If heap identifier tagging is not in force it simply returns the next positive integer in sequence, starting with 'one' for the first call of LeapCreate. If tagging is in force it returns the first unused heap identifier in the range 1 to 255 (8-bit tags) or 1 to 65535 (16-bit tags). A heap identifier is considered unused if it has not been returned by a previous call of LeapCreate, or else if it has since been put back in the unused pool by a call of LeapDestroy. If there is no unused heap identifier, LeapCreate returns the next number in sequence above the limit (255 or 65535), and this number can never be re-used, nor can allocations made with it be freed by LeapDestroy.

LeapCreate ignores the HEAP_GENERATE_EXCEPTIONS and HEAP_NO_SERIALIZE flags.


LeapDestroy

This function has no effect unless heap identifier tagging is in force.

If it is, but the heap identifier passed as parameter is out of range of the tag (e.g. 1 to 255 for 8-bit tags), LeapDestroy still has no effect.

If on the other hand the identifier is in range, it scans the cell tagmap for matching tags. It updates the cell bitmap for each such match, just as if LeapFree had been called for that memory allocation. LeapDestroy then processes the big-block area, freeing all blocks tagged as belonging to the relevant heap. Next it returns the heap identifier to the pool for reuse.

As an option, settable through the registry interface (see section 6 The Registry Interface), LeapDestroy may then call LeapCompact to decommit memory released by 'destroying' the heap.


LeapFree

If the address of the memory block to be freed lies outside the LeapHeap compartment and big-block areas, LeapFree forwards the request to the relict heap by calling HeapFree.

Otherwise LeapFree determines whether the memory is in the compartment or big-block area. If the former, it uses the address to work out which compartment the block is in, and the cell offset within the compartment, and is thereby able to update the cell bitmap. If the allocation is in the big-block area, it follows the back-pointer of the block to the block descriptor and marks that as free.

LeapFree ignores the HEAP_NO_SERIALIZE flag.


LeapReAlloc

If the address of the memory block to be re-allocated lies outside the bounds of the LeapHeap, LeapReAlloc calls HeapReAlloc to make the relict heap handle the request.

Otherwise it determines the size of the existing allocation by finding out which compartment it lies within, or from the block descriptors if in the big-block area. LeapReAlloc is then in a position to decide whether the allocation must be moved to a larger memory block, could be moved to a smaller memory block, or should remain where it is. If the block is to be moved, it performs (rather inefficiently) a LeapAlloc - copy - LeapFree sequence.

If the reallocation is to a smaller block, the routine copies a number of bytes equal to the requested new size, rounded up to a multiple of four. Note that such a reallocation is capable of ending up in a larger block; it depends what happens within the nested LeapAlloc call.

The effect of a request to resize to more than 2 GB is undefined.

LeapReAlloc ignores the HEAP_GENERATE_EXCEPTIONS and HEAP_NO_SERIALIZE flags.

LeapReAlloc implements the HEAP_REALLOC_IN_PLACE_ONLY flag, reporting failure if a block would otherwise have been moved to a larger space, and not moving the block if it would otherwise have been moved to a smaller space.

LeapReAlloc implements the HEAP_ZERO_MEMORY flag, by clearing any memory between the old size of the allocation and the new (if larger). If this flag is used, all previous calls of LeapAlloc and LeapReAlloc for the allocation should also have used it, otherwise the content of those memory offsets between the previous requested size of the allocation and the previous allocated size is not guaranteed to be zero.

If it fails, LeapReAlloc simply returns the failure code (zero); it does not provide extended error information by means of the SetLastError API routine.


LeapSize

If the address passed as parameter lies outside the LeapHeap, LeapSize calls HeapSize so that the relict heap deals with the call.

Otherwise LeapSize uses the address to work out which compartment the allocation lies within and returns the cell size of that compartment. If the memory lies instead in the big-block area, the size is calculated after following the back-pointer to the relevant descriptor and its successor.

LeapSize ignores the HEAP_NO_SERIALIZE flag.


LeapValidate

If the request is to validate a specific memory block, and that block lies within the LeapHeap boundaries, LeapValidate returns 'valid' without performing any checks. Otherwise the task is delegated to the relict heap with a call of HeapValidate.

When on the other hand the request is to validate an entire heap, the behaviour depends on a user parameter settable in the LeapHeap registry section.

If validation is turned off, the task is delegated to the relict heap if the heap parameter is the process heap; if the request concerns some other heap identifier LeapValidate simply returns 'valid'. Always returning 'valid' is arguably conformant with the HeapValidate specification, which says that if the heap or heap entry is sound, the function returns 'valid' and if not it returns either 'valid' or 'invalid'. This is not just pedantry; if in the native heap an allocation is freed to a lookaside list (explained in section 5 Performance), subsequent calls to HeapValidate for it return 'valid'.

If the validation option is turned on in the registry, LeapValidate verifies that each bit in the higher-tier bitmaps of every compartment reflects correctly the full or not-full condition of the corresponding word in the tier below. Within the big-block area, LeapValidate checks that in-use descriptors match the set bits in the cascading bitmap, that the addresses of memory blocks held in the descriptor list are ordered and aligned correctly, and that back-pointers are correct for all the memory blocks. The validating thread must be the only thread working the heap for this verification to be reliable. If an inconsistency is found, the function returns 'invalid'.

LeapValidate ignores the HEAP_NO_SERIALIZE flag.

LeapValidate should not be turned on if concurrent calls of LeapAlloc, LeapFree, LeapCompact, LeapDestroy or LeapReAlloc are possible.