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