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.

201 lines
6.3KB

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