zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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 }