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.

323 lines
8.9KB

  1. /*
  2. * RealTime Memory Pool, heavily based on work by Nedko Arnaudov
  3. * Copyright (C) 2006-2009 Nedko Arnaudov <nedko@arnaudov.name>
  4. * Copyright (C) 2013-2016 Filipe Coelho <falktx@falktx.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation; either version 2 of
  9. * the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * For a full copy of the GNU General Public License see the GPL.txt file
  17. */
  18. #include "list.h"
  19. #include "rtmempool.h"
  20. #include "rtmempool-lv2.h"
  21. #include <assert.h>
  22. #include <pthread.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #define RTMEMPOOL_THREAD_SAFETY 0
  27. #if RTMEMPOOL_THREAD_SAFETY
  28. #define rtmempool_mutex_lock(m) pthread_mutex_lock(m)
  29. #define rtmempool_mutex_unlock(m) pthread_mutex_unlock(m)
  30. #else
  31. #define rtmempool_mutex_lock(...)
  32. #define rtmempool_mutex_unlock(...)
  33. #endif
  34. // ------------------------------------------------------------------------------------------------
  35. typedef struct list_head k_list_head;
  36. // ------------------------------------------------------------------------------------------------
  37. typedef struct _RtMemPool
  38. {
  39. char name[RTSAFE_MEMORY_POOL_NAME_MAX];
  40. size_t dataSize;
  41. size_t minPreallocated;
  42. size_t maxPreallocated;
  43. k_list_head used;
  44. unsigned int usedCount;
  45. k_list_head unused;
  46. unsigned int unusedCount;
  47. pthread_mutex_t mutex;
  48. } RtMemPool;
  49. // ------------------------------------------------------------------------------------------------
  50. // adjust unused list size
  51. static void rtsafe_memory_pool_sleepy(RtMemPool* poolPtr, bool* overMaxOrMallocFailed)
  52. {
  53. k_list_head* nodePtr;
  54. k_list_head unused;
  55. unsigned int unusedCount;
  56. INIT_LIST_HEAD(&unused);
  57. unusedCount = 0;
  58. while (poolPtr->unusedCount + unusedCount < poolPtr->minPreallocated)
  59. {
  60. if (poolPtr->usedCount + poolPtr->unusedCount + unusedCount >= poolPtr->maxPreallocated)
  61. {
  62. *overMaxOrMallocFailed = true;
  63. break;
  64. }
  65. nodePtr = malloc(sizeof(k_list_head) + poolPtr->dataSize);
  66. if (nodePtr == NULL)
  67. {
  68. *overMaxOrMallocFailed = true;
  69. break;
  70. }
  71. list_add_tail(nodePtr, &unused);
  72. ++unusedCount;
  73. }
  74. rtmempool_mutex_lock(&poolPtr->mutex);
  75. poolPtr->unusedCount += unusedCount;
  76. while (unusedCount != 0)
  77. {
  78. assert(! list_empty(&unused));
  79. nodePtr = unused.next;
  80. list_del(nodePtr);
  81. list_add_tail(nodePtr, &poolPtr->unused);
  82. --unusedCount;
  83. }
  84. rtmempool_mutex_unlock(&poolPtr->mutex);
  85. }
  86. // ------------------------------------------------------------------------------------------------
  87. static bool rtsafe_memory_pool_create2(RtMemPool_Handle* handlePtr,
  88. const char* poolName,
  89. size_t dataSize,
  90. size_t minPreallocated,
  91. size_t maxPreallocated)
  92. {
  93. assert(minPreallocated <= maxPreallocated);
  94. assert(poolName == NULL || strlen(poolName) < RTSAFE_MEMORY_POOL_NAME_MAX);
  95. k_list_head* nodePtr;
  96. RtMemPool* poolPtr;
  97. poolPtr = malloc(sizeof(RtMemPool));
  98. if (poolPtr == NULL)
  99. {
  100. return false;
  101. }
  102. if (poolName != NULL)
  103. {
  104. strcpy(poolPtr->name, poolName);
  105. }
  106. else
  107. {
  108. sprintf(poolPtr->name, "%p", poolPtr);
  109. }
  110. poolPtr->dataSize = dataSize;
  111. poolPtr->minPreallocated = minPreallocated;
  112. poolPtr->maxPreallocated = maxPreallocated;
  113. INIT_LIST_HEAD(&poolPtr->used);
  114. poolPtr->usedCount = 0;
  115. INIT_LIST_HEAD(&poolPtr->unused);
  116. poolPtr->unusedCount = 0;
  117. pthread_mutexattr_t atts;
  118. pthread_mutexattr_init(&atts);
  119. #ifdef __ARM_ARCH_7A__
  120. pthread_mutexattr_setprotocol(&atts, PTHREAD_PRIO_INHERIT);
  121. #endif
  122. pthread_mutex_init(&poolPtr->mutex, &atts);
  123. pthread_mutexattr_destroy(&atts);
  124. while (poolPtr->unusedCount < poolPtr->minPreallocated)
  125. {
  126. nodePtr = malloc(sizeof(k_list_head) + poolPtr->dataSize);
  127. if (nodePtr == NULL)
  128. {
  129. break;
  130. }
  131. list_add_tail(nodePtr, &poolPtr->unused);
  132. poolPtr->unusedCount++;
  133. }
  134. *handlePtr = (RtMemPool_Handle)poolPtr;
  135. return true;
  136. }
  137. // ------------------------------------------------------------------------------------------------
  138. static unsigned char rtsafe_memory_pool_create_old(const char* poolName,
  139. size_t dataSize,
  140. size_t minPreallocated,
  141. size_t maxPreallocated,
  142. RtMemPool_Handle* handlePtr)
  143. {
  144. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated);
  145. }
  146. // ------------------------------------------------------------------------------------------------
  147. bool rtsafe_memory_pool_create(RtMemPool_Handle* handlePtr,
  148. const char* poolName,
  149. size_t dataSize,
  150. size_t minPreallocated,
  151. size_t maxPreallocated)
  152. {
  153. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated);
  154. }
  155. // ------------------------------------------------------------------------------------------------
  156. void rtsafe_memory_pool_destroy(RtMemPool_Handle handle)
  157. {
  158. assert(handle);
  159. k_list_head* nodePtr;
  160. RtMemPool* poolPtr = (RtMemPool*)handle;
  161. // caller should deallocate all chunks prior releasing pool itself
  162. if (poolPtr->usedCount != 0)
  163. {
  164. fprintf(stderr, "warning: rtsafe_memory_pool_destroy called with nodes still active\n");
  165. }
  166. while (poolPtr->unusedCount != 0)
  167. {
  168. assert(! list_empty(&poolPtr->unused));
  169. nodePtr = poolPtr->unused.next;
  170. list_del(nodePtr);
  171. poolPtr->unusedCount--;
  172. free(nodePtr);
  173. }
  174. assert(list_empty(&poolPtr->unused));
  175. pthread_mutex_destroy(&poolPtr->mutex);
  176. free(poolPtr);
  177. }
  178. // ------------------------------------------------------------------------------------------------
  179. // find entry in unused list, fail if it is empty
  180. void* rtsafe_memory_pool_allocate_atomic(RtMemPool_Handle handle)
  181. {
  182. assert(handle);
  183. k_list_head* nodePtr;
  184. RtMemPool* poolPtr = (RtMemPool*)handle;
  185. rtmempool_mutex_lock(&poolPtr->mutex);
  186. if (list_empty(&poolPtr->unused))
  187. {
  188. rtmempool_mutex_unlock(&poolPtr->mutex);
  189. return NULL;
  190. }
  191. nodePtr = poolPtr->unused.next;
  192. list_del(nodePtr);
  193. poolPtr->unusedCount--;
  194. poolPtr->usedCount++;
  195. list_add_tail(nodePtr, &poolPtr->used);
  196. rtmempool_mutex_unlock(&poolPtr->mutex);
  197. return (nodePtr + 1);
  198. }
  199. // ------------------------------------------------------------------------------------------------
  200. void* rtsafe_memory_pool_allocate_sleepy(RtMemPool_Handle handle)
  201. {
  202. assert(handle);
  203. void* data;
  204. bool overMaxOrMallocFailed = false;
  205. RtMemPool* poolPtr = (RtMemPool*)handle;
  206. do {
  207. rtsafe_memory_pool_sleepy(poolPtr, &overMaxOrMallocFailed);
  208. data = rtsafe_memory_pool_allocate_atomic((RtMemPool_Handle)poolPtr);
  209. }
  210. while (data == NULL && ! overMaxOrMallocFailed);
  211. return data;
  212. }
  213. // ------------------------------------------------------------------------------------------------
  214. // move from used to unused list
  215. void rtsafe_memory_pool_deallocate(RtMemPool_Handle handle, void* memoryPtr)
  216. {
  217. assert(handle);
  218. RtMemPool* poolPtr = (RtMemPool*)handle;
  219. rtmempool_mutex_lock(&poolPtr->mutex);
  220. list_del((k_list_head*)memoryPtr - 1);
  221. list_add_tail((k_list_head*)memoryPtr - 1, &poolPtr->unused);
  222. poolPtr->usedCount--;
  223. poolPtr->unusedCount++;
  224. rtmempool_mutex_unlock(&poolPtr->mutex);
  225. }
  226. // ------------------------------------------------------------------------------------------------
  227. // LV2 stuff
  228. void lv2_rtmempool_init(LV2_RtMemPool_Pool* poolPtr)
  229. {
  230. poolPtr->create = rtsafe_memory_pool_create;
  231. poolPtr->destroy = rtsafe_memory_pool_destroy;
  232. poolPtr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
  233. poolPtr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
  234. poolPtr->deallocate = rtsafe_memory_pool_deallocate;
  235. }
  236. void lv2_rtmempool_init_deprecated(LV2_RtMemPool_Pool_Deprecated* poolPtr)
  237. {
  238. poolPtr->create = rtsafe_memory_pool_create_old;
  239. poolPtr->destroy = rtsafe_memory_pool_destroy;
  240. poolPtr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
  241. poolPtr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
  242. poolPtr->deallocate = rtsafe_memory_pool_deallocate;
  243. }