Allocator.h (6506B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 Allocator.h - 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 #pragma once 13 #include <cstdlib> 14 #include <utility> 15 #include <new> 16 17 namespace zyn { 18 19 //! Allocator Base class 20 //! subclasses must specify allocation and deallocation 21 class Allocator 22 { 23 public: 24 Allocator(void); 25 Allocator(const Allocator&) = delete; 26 virtual ~Allocator(void); 27 28 virtual void *alloc_mem(size_t mem_size) = 0; 29 virtual void dealloc_mem(void *memory) = 0; 30 31 /** 32 * High level allocator method, which return a pointer to a class 33 * or struct 34 * allocated with the specialized subclass strategy 35 * @param ts argument(s) for the constructor of the type T 36 * @return a non null pointer to a new object of type T 37 * @throw std::bad_alloc is no memory could be allocated 38 */ 39 template <typename T, typename... Ts> 40 T *alloc(Ts&&... ts) 41 { 42 void *data = alloc_mem(sizeof(T)); 43 if(!data) { 44 rollbackTransaction(); 45 throw std::bad_alloc(); 46 } 47 append_alloc_to_memory_transaction(data); 48 return new (data) T(std::forward<Ts>(ts)...); 49 } 50 51 /** 52 * High level allocator method, which return a pointer to an array of 53 * class or struct 54 * allocated with the specialized subclass strategy 55 * @param len the array length 56 * @param ts argument(s) for the constructor of the type T 57 * @return a non null pointer to an array of new object(s) of type T 58 * @throw std::bad_alloc is no memory could be allocated 59 */ 60 template <typename T, typename... Ts> 61 T *valloc(size_t len, Ts&&... ts) 62 { 63 T *data = (T*)alloc_mem(len*sizeof(T)); 64 if(!data && len != 0) { 65 rollbackTransaction(); 66 throw std::bad_alloc(); 67 } 68 append_alloc_to_memory_transaction(data); 69 for(unsigned i=0; i<len; ++i) 70 new ((void*)&data[i]) T(std::forward<Ts>(ts)...); 71 72 return data; 73 } 74 75 template <typename T> 76 void dealloc(T*&t) 77 { 78 if(t) { 79 t->~T(); 80 dealloc_mem((void*)t); 81 t = nullptr; 82 } 83 } 84 85 //Destructor Free Version 86 template <typename T> 87 void devalloc(T*&t) 88 { 89 if(t) { 90 dealloc_mem(t); 91 t = nullptr; 92 } 93 } 94 95 template <typename T> 96 void devalloc(size_t elms, T*&t) 97 { 98 if(t) { 99 for(size_t i=0; i<elms; ++i) 100 (t+i)->~T(); 101 102 dealloc_mem(t); 103 t = nullptr; 104 } 105 } 106 107 void beginTransaction(); 108 void endTransaction(); 109 110 virtual void addMemory(void *, size_t mem_size) = 0; 111 112 //Return true if the current pool cannot allocate n chunks of chunk_size 113 virtual bool lowMemory(unsigned n, size_t chunk_size) const = 0; 114 bool memFree(void *pool) const; 115 116 //returns number of pools 117 int memPools() const; 118 119 int freePools() const; 120 121 unsigned long long totalAlloced() const; 122 123 struct AllocatorImpl *impl; 124 125 private: 126 const static size_t max_transaction_length = 256; 127 128 void* transaction_alloc_content[max_transaction_length]; 129 size_t transaction_alloc_index; 130 bool transaction_active; 131 132 void rollbackTransaction(); 133 134 /** 135 * Append memory block to the list of memory blocks allocated during this 136 * transaction 137 * @param new_memory pointer to the memory pointer to freshly allocated 138 */ 139 void append_alloc_to_memory_transaction(void *new_memory) { 140 if (transaction_active) { 141 if (transaction_alloc_index < max_transaction_length) { 142 transaction_alloc_content[transaction_alloc_index++] = new_memory; 143 } 144 // TODO add log about transaction too long and memory transaction 145 // safety net being disabled 146 } 147 } 148 149 }; 150 151 //! the allocator for normal use 152 class AllocatorClass : public Allocator 153 { 154 public: 155 void *alloc_mem(size_t mem_size); 156 void dealloc_mem(void *memory); 157 void addMemory(void *, size_t mem_size); 158 bool lowMemory(unsigned n, size_t chunk_size) const; 159 using Allocator::Allocator; 160 }; 161 typedef AllocatorClass Alloc; 162 163 //! the dummy allocator, which does not allow any allocation 164 class DummyAllocator : public Allocator 165 { 166 [[ noreturn ]] void not_allowed() const { 167 throw "(de)allocation forbidden"; // TODO: std exception 168 } 169 public: 170 void *alloc_mem(size_t ) { not_allowed(); } 171 void dealloc_mem(void* ) { not_allowed(); } // TODO: more functions? 172 void addMemory(void *, size_t ) { not_allowed(); } 173 bool lowMemory(unsigned , size_t ) const { not_allowed(); } 174 using Allocator::Allocator; 175 }; 176 177 extern DummyAllocator DummyAlloc; 178 179 /** 180 * General notes on Memory Allocation Within ZynAddSubFX 181 * ----------------------------------------------------- 182 * 183 * - Parameter Objects Are never allocated within the realtime thread 184 * - Effects, notes and note subcomponents must be allocated with an allocator 185 * - 5M Chunks are used to give the allocator the memory it wants 186 * - If there are 3 chunks that are unused then 1 will be deallocated 187 * - The system will request more allocated space if 5x 1MB chunks cannot be 188 * allocated at any given time (this is likely huge overkill, but if this is 189 * satisfied, then a lot of note spamming would be needed to run out of 190 * space) 191 * 192 * - Things will get a bit weird around the effects due to how pointer swaps 193 * occur 194 * * When a new Part instance is provided it may or may not come with some 195 * instrument effects 196 * * Merging blocks is an option, but one that is not going to likely be 197 * implemented too soon, thus all effects need to be reallocated when the 198 * pointer swap occurs 199 * * The old effect is extracted from the manager 200 * * A new one is constructed with a deep copy 201 * * The old one is returned to middleware for deallocation 202 */ 203 204 }