Appendix D. The MTS API

A.1 Introduction

MTS implements the standard memory allocator interfaces. This allows MTS to be used completely transparently in your application. For example, on Unix environments MTS implements the posix malloc and free interface.

This chapter provides additional interfaces to MTS to further maximize performance and functionality.

A.2 Error Handling

Warnings (type 0) are only processed in the presence of a defined callback function. All other messages are sent to stderr and an error_callback if one is registered. Error callbacks are also a convenient place to set a break- point to debug a memory related error.

Custom Error Callback

typedef void (*mts_error_callback) (int err_, const char* s, int s_length);
void mts_set_error_callback (mts_error_callback);

Values for err_:

Error Code Meaning
0 warning
1 non fatal error
2 error writing output
3 system call error
4 pool related errors
5 pool size exceeded limit
-1 bad free, non fatal by default

MTS will call _exit on the following errors

Error Code Meaning
-2 fatal error
-3 fatal error writing output
-4 fatal system call error

Large Allocation Warning

void mts_warn_large_size (unsigned long Size);

Warns of large allocations. Size must be greater than 60k otherwise its silently ignored. This was incorporated to help find sporadic large allocations that often precede crashes.

MTS Status Return Code

typedef enum MTSstatus {MTSerror, MTSok} MTSstatus;

Used as the return value from various MTS functions.

Enum Value Meaning
MTSok indicates the operation was a success.
MTSerror indicates an error occured and the operation was not successful.

A.3 Memory Mapping & Memory Defaults

Using a callback mechanism, MTS lets applications mmap the main allocator and individual pools of allocations. This gives a large amount of control over how MTS gets memory from the underlying OS. This can be used to seed MTS with another custom allocator for example.

MTS Pool Pointer

typedef struct mts_pool MTSPool;

MTSPool* is an opaque pointer to an internal pool structure

Memory Map User Defined Callback Functions

typedef void* (*mts_user_memory_callback_v2) (unsigned long size, void* user_data);
typedef void (*mts_user_unmap_memory_callback_v2) (void* sddrize, size_t length);

Signature of the user defined callback that provides MTS with memory space. It accepts a size to mmap and a user_data pointer provided by the set callback function. It returns a valid mmaped address of size bytes or MMAP_FAILED (-1). MTS takes care of page alignment for mmapped memory.

MTSstatus
mts_set_memory_callback_v3 (MTSPool* p,
                            mts_user_memory_callback_v2,
                            mts_user_unmap_memory_callback_v2,
                            void* user_data);

MTSstatus
mts_set_memory_callback_v2 (MTSPool* p,
                            mts_user_memory_callback_v2,
                            void* user_data);

When p is null the callback is set for the general allocator, otherwise the memory callback is set for a specific pool. user_data allows the callback to service calls for memory from many pools to different mmaped files.

Note: We recommend that the callback make a special effort to not call malloc recursively which could lead to an infinite loop. One approach would be to use thread local memory to guard against this.

Example:

//! Prevent infinite recursion with a thread-local variable.
__thread bool allocatingMemory;

// return a valid mmap address or MAP_FAILED
void* myMTSmemoryCallback (size_t size, void* memFile)
{
  if (memFile == NULL || allocatingMemory)
  { return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  }
  else
  { allocatingMemory = true;
    void* ret = growMemMap(size, (MemoryFile*)memFile);
    allocatingMemory = false;
    return ret;
  }
}

MTS Heap Initialization

size_t mts_set_small_heap_size (size_t size, size_t inc, unsigned long opt);
size_t mts_set_large_heap_size (size_t size, size_t inc, unsigned long opt);

Both set size calls return the size of the heap. size is the current size to set. inc is the increment size to grow the heap. The calls were created to help prefault memory to speed up application startup.

values for opt (opt values may be combined with bitwise or):

Exported Variable Name Unsigned Long Value Notes
MTSSmallHeapRecycleOff 0x0800 effects small heaps only
MTSPreFault 0x1000 touches all newly mmaped pages
MTSPreFaultPrev 0x4000 touches all previously mmaped pages

The following options are passed through to the internal MTS mmap calls

Exported Variable Name Unsigned Long Value Notes
MTSMapHuge 0x10000 MAP_HUGETLB (RH6 and higher)
MTSMapLocked 0x20000 MAP_LOCKED
MTSMapPopulate 0x40000 MAP_POPULATE

If MTSSmallHeapRecycleOff is set for the small heap call, page recycling is turned off for that heap. This is often desirable if there is prior knowledge that the heap will continously reuse the same sort of allocations. For the pages to migrate to other heaps to be later reclaimed is usually wasteful.

When setting the size of large heaps, a private heap for allocations greater than 4k is created which will be bound to the thread it is created in. This heap will persist beyond the life of the thread that creates it and become orphaned. Use this call only for threads that will exist for the life of the application, and where you know that lots of memory is needed. Otherwise, let the thread use it’s internally assigned and initialized fixed heap.

A.4 Memory Pools

MTS as of 7.9.2 incorporates two types of pool interfaces. A standard pool interface and a light weight (lw) pool interface. The original standard pool interface incorporates all the traditional allocation/deallocation MTS strategies, whereas, the lw pool interface just focuses on achieving very high speed allocation results and uses a mass memory free. This is useful, for instance, for allocations associated with parsing message buffers that are constantly being allocated and discarded. Both pool interfaces suppot the general call to free(v) where v is a pool allocation. So existing code will work seamlessly with these allocations.

Standard MTS Pool Interface

MTS early in its inception incorporated a pool interface. The main utility of MTS pools is that they aggregate a collection of allocations which can be deleted together and (unlike the default heaps) unmapped from the application. It’s important to note that individual MTS pool allocations can be freed using an ordinary call to free(), so disparate parts of the application need not be aware of their origin.

MTSPool   *MTSPoolCreateV3  (const char* name, size_t opt, void* addr, size_t size);
MTSstatus  MTSPoolDestroy   (MTSPool*); // depending on create settings, unmaps the pool

MTS Pool Options

These options control MTSPool attributes of locking, space and unmapping.

Note: that locking refers to internal MTS locking not read/write locks.

Exported Variable Name Unsigned Long Value Notes
MTSPoolNotThreaded 0x0000 Pool is not thread safe. (Default)
MTSPoolThreaded 0x0008 Pool is thread safe
MTSPoolInternal 0x0010 MTS automatically maps space as the pool grows. (Default)
MTSPoolPrivate 0x0020 Application provides mmapped memory
MTSPoolShared 0x0040 Application provides mmapped shared memory
MTSPoolAppUnmap 0x0080 Application is responsible for unmapping pool memory
MTSPoolMTSUnmap 0x0100 MTSPoolDestroy will unmap pool memory. (Default)

MTSPoolCreatV3 creates a new named pool. name is null terminated. The opt parameter is a bitmapped combination of MTSPool Options.

If the pool type is MTSPoolInternal then addr and size are ignored. MTS will dynamically size the pool based on allocation/deallocation requests and unmmap the space when MTSPoolDestroy is called.

For MTSPoolPrivate and MTSPoolShared pools, initial mmapped space with a minimum of 256KB should be provided. If the application anticipates that the initial space will become insufficient, a memory callback must be registered.

For MTSPoolShared a process can call MTSPoolCreatV3 with an already established pool in shared memory which MTS will recognize. To establish a fresh shared space for an MTS pool in shared memory the first 64k need to be set to 0.

If a process tries to use an existing shared pool that consists of more than one mmaped segment, it is the process responsibility to mmap those segments into its process space at the exact addresses, but only one call to MTSPoolCreatV3 with the initial segment address and size should be made.

When multiple processes are mapped to the same shared pool, if the pool is declared MTSPoolThreaded, the pool will be process safe as well as thread safe.

MTS Pool API

MTSstatus  MTSPoolAddSpace  (MTSPool* pool, void* addr, size_t size);

Increase the size of a Private or Shared pool.

void      *MTSPoolAlloc     (MTSPool*, size_t);

Malloc inside a pool using MTSPool* returned by CreatV3.

void      *MTSPoolRealloc   (MTSPool*, void *, size_t);

Realloc inside a pool using MTSPool* returned by CreatV3.

void       MTSPoolFree      (MTSPool*, void*);

Free memory inside a pool using MTSPool* returned by CreateV3. Note: This is equivalent to free(void*).

MTSstatus  MTSPoolFreeAll   (MTSPool*);

Free all allocations inside a pool referenced by MTSPool* returned by CreateV3.

MTSPool   *MTSPoolGet       (void*);

Identify the pool allocation that contains this block of memory.

char      *MTSPoolGetName   (MTSPool*);

Return the pool name of a pool referenced by MTSPool* returned by CreateV3.

MTSstatus  MTSPoolSetLS     (MTSPool*, void *);

Store a private pointer in the pool referenced by MTSPool*.

void      *MTSPoolGetLS     (MTSPool*);

Retreive the private pointer that was stored in the pool referenced by MTSPool*.

Light Weight Pool Interface

The lw pool interface is designed for speed. Speed of pool initialization and alloc execution. In this scheme a call to free() is a nop. All memory in the lw pool is freed by a call to Release the pool. Sizing the pool when pool Create is called will gaurantee the best performance.

The only size stat that is kept is the total amount of memory requested from the OS for this lw pool. No current used or malloc/free counts are possible.

Currently no internal locking is supported. If locking is needed, it’s the application responsibility.

void * MTSlwPoolCreate  (const char * name, int opt, size_t size);    // returns lw ptr or 0
int    MTSlwPoolDestroy (void* lwp);                                  // destroy and unmap the space

values for opt:

Exported Variable Name Unsigned Long Value Notes
MTSlwPoolRecycle 0x8000 recycle lw pools instead of destroying them

MTS Light Weight Pool API

void * MTSlwPoolAlloc   (void* lwp, size_t size);

Allocate size bytes in lw pool.

size_t MTSlwPoolRelease (void* lwp);

Release the lw pool for reuse, returns ref count.

size_t MTSlwPoolSize    (void* lwp);

Returns the lw pool size.

char * MTSlwPoolGetName (void * lwp);

Get the name stored during Create.

void   MTSlwPoolSetLS   (void* lwp, void* ptr);

Store a pointer locally in the lw pool.

void * MTSlwPoolGetLS   (void* lwp);

Retrieve a local pointer that was stored in an lw pool.

A.5 Statistics

As of version 7.10.2 are thread based statistics.

MTS provides a comprehensive array of internal statistics. Collecting statistics imposes a ~5% performance penalty. If you would like to deploy a version of MTS that doesn’t collect stats, please let us know.

The small_arena count is the total number of thread specific heaps, which includes both those that are currently associated with an active thread and those that are inactive, i.e., their thread has exited. Inactive thread heaps may still contain allocations that are accessed from active threads.

The size metrics are mmaped memory sizes whereas the byte count metric refers to how much space is currently in use.

Since the thread heaps recycle pages among themselves the D_free_pages tracks how much space is available and unused for the thread heaps.

MTS Stats Struct Layout

Name Size Description
heap_size size_t total heap size as requested from OS
byte_count size_t current byte usage
small_malloc size_t number of calls for small allocations
medium_malloc size_t number of calls for medium allocations
large_malloc size_t number of calls for large allocations
small_free size_t number of calls for small deallocations
medium_free size_t number of calls for medium deallocations
large_free size_t number of calls for large deallocations
unused6 size_t  
pools size_t number of pools
small_arenas size_t number of dynamic heaps for sizes <=4096
yield_count size_t sched_yield count for malloc/free
heap_count size_t number of fixed heaps for sizes > 4096
F_size size_t fixed heaps size
F_byte_count size_t fixed heaps currently in use
DA_size size_t active dynamic thread heaps size
DA_byte_count size_t active dynamic thread heaps current bytes
DI_size size_t inactive dynamic thread heaps size
DI_byte_count size_t inactive dynamic thread heaps current bytes
D_free_pages size_t dynamic thread heaps free pages in bytes
P_size size_t Pools size
P_byte_count size_t Pool current bytes
list_yield_count size_t dynamic thread heap inactive list lock
page_yield_count size_t dynamic thread heap free page lock

MTS Pthread Stats Struct Layout

Name Size Description
tid pid_t tid of pthread allocating the space
space size_t total space requested by this thread
small_used size_t current small allocation
middle_used size_t current middle size allocations
large_used size_t current large allocations
block_used size_t current mmaped block allocations

The sum of the *_used struct members is the current total allocations made by this pthread.

MTS Stats API

struct mts_pthread_stats* mts_collect_pthread_stats (void);

Gather all past and present pthread stats.

struct mts_pthread_stats* mts_collect_active_pthread_stats (void);

Gather only current pthread stats.

struct mts_pthread_stats* mts_collect_inactive_pthread_stats (void);

Gather only past (inactive) pthread stats.

int mts_check_consistency(int options);

Performs consistency checks on the MTS internal heap structures. Always returns 0, but may produce MTS errors that can be caught by a custom MTS error handler or sent to standard error. See A.2 Error Handling for more details on MTS error handling.

Note: There are currently no options available for this function.

const char * mts_version(void);

Return the MTS version number.

A.6 Deprecated API’s

These functions are still maintained but we recommend using replacement functions from the previous sections.

typedef void* (*mts_user_memory_callback) (size_t length);
void mts_set_memory_callback (mts_user_memory_callback);

int mts_total_heap_stats (struct mts_stats *);

unsigned long mts_heapbytecounts(void);
unsigned long mts_total_heapsize(void);

/* returns the number of virtual heaps.  */
int mts_num_heaps ();

/* sets the number of heaps, overridden by MTS_HEAP_COUNT and MTS_INIT_THREAD_HEAPS env variables */
void mts_init_thread_heaps( int np );

/* return the index of the heap that the current thread is using */
int mts_which_heap();


/* heapsize functions return the size of the virtual heap(s):
 *
 * mts_heapsizes:               sum of all heap memory gotten from the OS
 * mts_current_heapsize:        size of virtual heap this thread is using
 * mts_heapsize:                size of a particular heap indexed
 *                              0 to (mts_num_heaps() -1)
*/
unsigned long mts_heapsizes();
unsigned long mts_current_heapsize ();
unsigned long mts_heapsize ( int heap );

/* heapbytecount functions return current malloc'ed memory.
 * NOTE: more than one thread can be bound to a heap, therefor these stats
 * can reflect the allocations of more than one single thread.
 *
 * mts_heapbytecounts:          sum of all currently malloced memory
 * mts_current_heapbytecount:   malloc'ed memory in heap that thread is using
 * mts_heapbytecount:           malloc'ed memory of a particular heap indexed
 *                              0 to (mts_num_heaps() -1)
*/
unsigned long mts_heapbytecounts ();
unsigned long mts_current_heapbytecount();
unsigned long mts_heapbytecount ( int heap );