# allocator.h ## Index
`tm_allocator_o`

`struct tm_allocator_i`
`inst`
`mem_scope`
`realloc()`

`tm_alloc()`
`tm_free()`
`tm_realloc()`
`struct tm_allocator_statistics_t`

`struct tm_allocator_api`
`system`
`end_of_page`
`vm`
`statistics`
`create_child()`
`destroy_child()`
`destroy_child_allowing_leaks()`
`create_leaky_root_scope()`
`create_fixed_vm()`

`tm_allocator_api_version`
## API
### `tm_allocator_o`
~~~c typedef struct tm_allocator_o tm_allocator_o; ~~~
### `struct tm_allocator_i`
Interface for allocating memory. #### `inst` ~~~c tm_allocator_o *inst; ~~~ Opaque struct storing instance data for this allocator. #### `mem_scope` ~~~c uint32_t mem_scope; ~~~ Memory scope used for tracking allocations made with this allocator. The memory scope is initialized by calling `tm_allocator_api->create_child()`. #### `realloc()` ~~~c void *(*realloc)(struct tm_allocator_i *a, void *ptr, uint64_t old_size, uint64_t new_size, const char *file, uint32_t line); ~~~ Basic memory allocator for The Machinery. The `realloc()` function provides a functionality similar to stdlib `realloc()`, but not exactly the same. Its arguments are `a`, the `tm_allocator_i` structure; `ptr`, a pointer to the block being allocated/reallocated/freed; `old_size`, the current size of the memory block; `new_size`, the new size of the block. Note that the caller of `realloc()` is responsible for keeping track of the sizes of memory blocks. This should not be an imposition, because the caller has to know the size of the memory block anyway. Otherwise, it couldn't use it without risking an access violation. The realloc function should implement the following behavior: * When `new_size` is zero, it should behave like `free()` and return `NULL`. * When `new_size` is not zero, it should behave like `realloc()`. In this case it should return NULL if and only if it can't fulfill the request. The `file` and `line` parameters are used together with the `mem_scope` member for tracking memory allocations. `file` and `line` should typically be set to the C `__FILE__` and `__LINE__` macros. If you are writing a low-level collection class, it is recommended that you don't use `__FILE__` and `__LINE__` directly but instead pass them down from the caller. That way, the memory allocations get attributed to the class using the collection class instead of the collection class itself, which is more useful. See `carray.inl` for an example of this. In addition to allocating memory, the allocator is also responsible for registering the allocation with the global memory tracker.
### `tm_alloc()`
~~~c #define tm_alloc(a, sz) #define tm_alloc_at(a, sz, file, line) ~~~ Convenience macro for allocating memory using `tm_allocator_i`.
### `tm_free()`
~~~c #define tm_free(a, p, sz) ~~~ Convenience macro for freeing memory using `tm_allocator_i`.
### `tm_realloc()`
~~~c #define tm_realloc(a, p, old_sz, new_sz) ~~~ Convenience macro for reallocating memory using `tm_allocator_i`.
### `struct tm_allocator_statistics_t`
Allocator statstics. ~~~c typedef struct tm_allocator_statistics_t { // Total number of allocations in the `system` allocator. TM_ATOMIC uint64_t system_allocation_count; // Total allocated bytes in the `system` allocator. TM_ATOMIC uint64_t system_allocated_bytes; // Total reserved address space in the `vm` and `fixed_vm` allocators. TM_ATOMIC uint64_t vm_reserved; // Total committed memory in the `vm` and `fixed_vm` allocators. TM_ATOMIC uint64_t vm_committed; // Allocations in `system` allocator since this counter was externally reset. TM_ATOMIC uint64_t system_churn_allocation_count; // Bytes allocated in the `system` allocator since this counter was externally reset. TM_ATOMIC uint64_t system_churn_allocated_bytes; // Virtual memory commited since this counter was externally reset. TM_ATOMIC uint64_t vm_churn_committed; } tm_allocator_statistics_t; ~~~
### `struct tm_allocator_api`
#### `system` ~~~c struct tm_allocator_i *system; ~~~ Allocator using the standard system `realloc()` function. #### `end_of_page` ~~~c struct tm_allocator_i *end_of_page; ~~~ Allocator used to detect buffer overflow problems. This allocator allocates whole pages from the VM for every allocation and aligns the allocated memory at the end of the page. This means that any code that tries to write beyond the end of the allocated memory triggers a page fault exception. Similarly, writing to freed memory will trigger a page fault -- unless another page has been allocated to the same location which in practice seems rare. If you are having issues with memory overwrites that are hard to pin down, you can try to switch out the regular allocator for the `end_of_page` one to see if you can detect the problem. #### `vm` ~~~c struct tm_allocator_i *vm; ~~~ Allocator that allocates memory directly from the VM. Note that these allocations will always be rounded up to the page size, so it is not a good choice for small allocations. For large allocations it can be a good choice since allocating VM pages will never cause fragmentation. Note though that since allocating from the VM involves a system call and requires the OS to clear the page (for security reasons) it can be slower than other allocators. #### `statistics` ~~~c tm_allocator_statistics_t *statistics; ~~~ Allocator system statistics. #### `create_child()` ~~~c tm_allocator_i (*create_child)(const tm_allocator_i *parent, const char *desc); ~~~ Creates a child allocator of the specified `parent` allocator with the given description. Parent and child allocators form a hierarchy that can be examined in the memory tracker. #### `destroy_child()` ~~~c void (*destroy_child)(const tm_allocator_i *child); ~~~ Destroys a child allocator created by `create_child()`. When an allocator is destroyed we verify that all the memory allocated by that allocator has been freed (to prevent memory leaks). #### `destroy_child_allowing_leaks()` ~~~c void (*destroy_child_allowing_leaks)(const tm_allocator_i *child, uint64_t max_leaked_bytes); ~~~ As `destroy_child()` but allows leaking up to `max_leaked_bytes` bytes without generating an error. This can be used if the allocator is being used by third-party code that is leaking stuff beyond our control. It should not be left permanently in the code, rather the goal should be to get the third party code cleaned up so it doesn't leak anymore. #### `create_leaky_root_scope()` ~~~c tm_allocator_i (*create_leaky_root_scope)(const tm_allocator_i *parent, const char *desc); ~~~ Creates a root allocator based on `parent` that is allowed to leak. I.e., `destroy_child()` will never be called for the returned allocator and thus we will never check whether it leaks memory or not. This should be used judiciously, because we want to have memory leak detection enabled for as many of our allocators as possible. However it can be useful for some global "singleton" systems that never gets destroyed, such as our list of plugins, etc. #### `create_fixed_vm()` ~~~c tm_allocator_i (*create_fixed_vm)(uint64_t reserve_size, uint32_t mem_scope); ~~~ Creates a fixed address VM allocator with the specified maximum size and tracking scope. The fixed address allocator can be used to make allocations that stay at the same address, even if they get realloc:ed to a bigger size. It works by using the virtual memory system to first reserve a large range of address space, and then commit memory out of this reserved space as requested (with calls to `realloc`). The `reserve_size` parameter specifies the amount of address space that is reserved. This is also the maximum amount of memory that an allocation can be realloc:ed to. (Trying to allocate more will result in an error.) The `mem_scope` specifies the tracking scope for the allocator. You can use the scope returned from an existing allocator created by `create_child()` or `create_leaky_root_scope()`. These allocators are useful when you want to allocate an array of objects and ensure that the objects in the array stay at a fixed address in memory, even as the array grows. See also: `create_leaky_root_scope()`.
### `tm_allocator_api_version`
~~~c #define tm_allocator_api_version ~~~