Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
6.0KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. Allocator.cpp - RT-Safe Memory Allocator
  4. Copyright (C) 2016 Mark McCurry
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. */
  10. #include <cstddef>
  11. #include <cstdlib>
  12. #include <cassert>
  13. #include <utility>
  14. #include <cstdio>
  15. #include "tlsf/tlsf.h"
  16. #include "Allocator.h"
  17. namespace zyncarla {
  18. //Used for dummy allocations
  19. DummyAllocator DummyAlloc;
  20. //recursive type class to avoid void *v = *(void**)v style casting
  21. struct next_t
  22. {
  23. next_t *next;
  24. size_t pool_size;
  25. };
  26. void *data(next_t *n)
  27. {
  28. return n+sizeof(next_t);
  29. }
  30. struct AllocatorImpl
  31. {
  32. void *tlsf = 0;
  33. //singly linked list of memory pools
  34. //XXX this may violate alignment on some platforms if malloc doesn't return
  35. //nice values
  36. next_t *pools = 0;
  37. unsigned long long totalAlloced = 0;
  38. };
  39. Allocator::Allocator(void) : transaction_active()
  40. {
  41. impl = new AllocatorImpl;
  42. size_t default_size = 10*1024*1024;
  43. impl->pools = (next_t*)malloc(default_size);
  44. impl->pools->next = 0x0;
  45. impl->pools->pool_size = default_size;
  46. size_t off = tlsf_size() + tlsf_pool_overhead() + sizeof(next_t);
  47. //printf("Generated Memory Pool with '%p'\n", impl->pools);
  48. impl->tlsf = tlsf_create_with_pool(((char*)impl->pools)+off, default_size-2*off);
  49. //printf("Allocator(%p)\n", impl);
  50. }
  51. Allocator::~Allocator(void)
  52. {
  53. next_t *n = impl->pools;
  54. while(n) {
  55. next_t *nn = n->next;
  56. free(n);
  57. n = nn;
  58. }
  59. delete impl;
  60. }
  61. void *AllocatorClass::alloc_mem(size_t mem_size)
  62. {
  63. impl->totalAlloced += mem_size;
  64. void *mem = tlsf_malloc(impl->tlsf, mem_size);
  65. //printf("Allocator.malloc(%p, %d) = %p\n", impl, mem_size, mem);
  66. //void *mem = malloc(mem_size);
  67. //printf("Allocator result = %p\n", mem);
  68. return mem;
  69. }
  70. void AllocatorClass::dealloc_mem(void *memory)
  71. {
  72. //printf("dealloc_mem(%d)\n", tlsf_block_size(memory));
  73. tlsf_free(impl->tlsf, memory);
  74. //free(memory);
  75. }
  76. bool AllocatorClass::lowMemory(unsigned n, size_t chunk_size) const
  77. {
  78. //This should stay on the stack
  79. void *buf[n];
  80. for(unsigned i=0; i<n; ++i)
  81. buf[i] = tlsf_malloc(impl->tlsf, chunk_size);
  82. bool outOfMem = false;
  83. for(unsigned i=0; i<n; ++i)
  84. outOfMem |= (buf[i] == nullptr);
  85. for(unsigned i=0; i<n; ++i)
  86. if(buf[i])
  87. tlsf_free(impl->tlsf, buf[i]);
  88. return outOfMem;
  89. }
  90. void AllocatorClass::addMemory(void *v, size_t mem_size)
  91. {
  92. next_t *n = impl->pools;
  93. while(n->next) n = n->next;
  94. n->next = (next_t*)v;
  95. n->next->next = 0x0;
  96. n->next->pool_size = mem_size;
  97. //printf("Inserting '%p'\n", v);
  98. off_t off = sizeof(next_t) + tlsf_pool_overhead();
  99. void *result =
  100. tlsf_add_pool(impl->tlsf, ((char*)n->next)+off,
  101. //0x0eadbeef);
  102. mem_size-off-sizeof(size_t));
  103. if(!result)
  104. printf("FAILED TO INSERT MEMORY POOL\n");
  105. };//{(void)mem_size;};
  106. #ifndef INCLUDED_tlsfbits
  107. //From tlsf internals
  108. typedef struct block_header_t
  109. {
  110. /* Points to the previous physical block. */
  111. struct block_header_t* prev_phys_block;
  112. /* The size of this block, excluding the block header. */
  113. size_t size;
  114. /* Next and previous free blocks. */
  115. struct block_header_t* next_free;
  116. struct block_header_t* prev_free;
  117. } block_header_t;
  118. static const size_t block_header_free_bit = 1 << 0;
  119. #endif
  120. void Allocator::beginTransaction() {
  121. // TODO: log about unsupported nested transaction when a RT compliant
  122. // logging is available and transaction_active == true
  123. transaction_active = true;
  124. transaction_alloc_index = 0;
  125. }
  126. void Allocator::endTransaction() {
  127. // TODO: log about invalid end of transaction when a RT copmliant logging
  128. // is available and transaction_active == false
  129. transaction_active = false;
  130. }
  131. bool Allocator::memFree(void *pool) const
  132. {
  133. size_t bh_shift = sizeof(next_t)+sizeof(size_t);
  134. //Assume that memory is free to start with
  135. bool isFree = true;
  136. //Get the block header from the pool
  137. block_header_t &bh = *(block_header_t*)((char*)pool+bh_shift);
  138. //The first block must be free
  139. if((bh.size&block_header_free_bit) == 0)
  140. isFree = false;
  141. block_header_t &bhn = *(block_header_t*)
  142. (((char*)&bh)+((bh.size&~0x3)+bh_shift-2*sizeof(size_t)));
  143. //The next block must be 'non-free' and zero length
  144. if((bhn.size&block_header_free_bit) != 0)
  145. isFree = false;
  146. if((bhn.size&~0x3) != 0)
  147. isFree = false;
  148. return isFree;
  149. }
  150. int Allocator::memPools() const
  151. {
  152. int i = 1;
  153. next_t *n = impl->pools;
  154. while(n->next) {
  155. i++;
  156. n = n->next;
  157. }
  158. return i;
  159. }
  160. int Allocator::freePools() const
  161. {
  162. int i = 0;
  163. next_t *n = impl->pools->next;
  164. while(n) {
  165. if(memFree(n))
  166. i++;
  167. n = n->next;
  168. }
  169. return i;
  170. }
  171. unsigned long long Allocator::totalAlloced() const
  172. {
  173. return impl->totalAlloced;
  174. }
  175. void Allocator::rollbackTransaction() {
  176. // if a transaction is active
  177. if (transaction_active) {
  178. // deallocate all allocated memory within this transaction
  179. for (size_t temp_idx = 0;
  180. temp_idx < transaction_alloc_index; ++temp_idx) {
  181. dealloc_mem(transaction_alloc_content[temp_idx]);
  182. }
  183. }
  184. transaction_active = false;
  185. }
  186. /*
  187. * Notes on tlsf internals
  188. * - TLSF consists of blocks linked by block headers and these form a doubly
  189. * linked list of free segments
  190. * - Original memory is [control_t pool]
  191. * base sentinal
  192. * Pools are [block_t block_t blocks ...]
  193. * Blocks are [memory block_t](??)
  194. * - These are stored in the control_t structure in an order dependent on the
  195. * size that they are
  196. * it's a bit unclear how collisions are handled here, but the basic premise
  197. * makes sense
  198. * - Additional structure is added before the start of each pool to define the
  199. * pool size and the next pool in the list as this information is not
  200. * accessible in O(good) time
  201. */
  202. }