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.

205 lines
6.4KB

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