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.

389 lines
10KB

  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 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 "rtmempool.h"
  19. #include "list.h"
  20. #include <assert.h>
  21. #include <pthread.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. // ------------------------------------------------------------------------------------------------
  26. typedef struct list_head k_list_head;
  27. // ------------------------------------------------------------------------------------------------
  28. typedef struct _RtMemPool
  29. {
  30. char name[RTSAFE_MEMORY_POOL_NAME_MAX];
  31. size_t dataSize;
  32. size_t minPreallocated;
  33. size_t maxPreallocated;
  34. k_list_head used;
  35. unsigned int usedCount;
  36. k_list_head unused;
  37. unsigned int unusedCount;
  38. bool enforceThreadSafety;
  39. // next members are initialized/used only if enforceThreadSafety is true
  40. pthread_mutex_t mutex;
  41. unsigned int unusedCount2;
  42. k_list_head pending;
  43. size_t usedSize;
  44. } RtMemPool;
  45. // ------------------------------------------------------------------------------------------------
  46. // additional function prototypes
  47. void rtsafe_memory_pool_sleepy(RtMemPool* poolPtr);
  48. bool rtsafe_memory_pool_create2(RtMemPool_Handle* handlePtr, const char* poolName,
  49. size_t dataSize, size_t minPreallocated, size_t maxPreallocated,
  50. int enforceThreadSafety);
  51. // ------------------------------------------------------------------------------------------------
  52. // adjust unused list size
  53. void rtsafe_memory_pool_sleepy(RtMemPool* poolPtr)
  54. {
  55. k_list_head* nodePtr;
  56. unsigned int count;
  57. if (poolPtr->enforceThreadSafety)
  58. {
  59. pthread_mutex_lock(&poolPtr->mutex);
  60. count = poolPtr->unusedCount2;
  61. assert(poolPtr->minPreallocated < poolPtr->maxPreallocated);
  62. while (count < poolPtr->minPreallocated)
  63. {
  64. nodePtr = malloc(sizeof(k_list_head) + poolPtr->dataSize);
  65. if (nodePtr == NULL)
  66. {
  67. break;
  68. }
  69. list_add_tail(nodePtr, &poolPtr->pending);
  70. count++;
  71. poolPtr->usedSize += poolPtr->dataSize;
  72. }
  73. while (count > poolPtr->maxPreallocated && ! list_empty(&poolPtr->pending))
  74. {
  75. nodePtr = poolPtr->pending.next;
  76. list_del(nodePtr);
  77. free(nodePtr);
  78. count--;
  79. poolPtr->usedSize -= poolPtr->dataSize;
  80. }
  81. pthread_mutex_unlock(&poolPtr->mutex);
  82. }
  83. else
  84. {
  85. while (poolPtr->unusedCount < poolPtr->minPreallocated)
  86. {
  87. nodePtr = malloc(sizeof(k_list_head) + poolPtr->dataSize);
  88. if (nodePtr == NULL)
  89. {
  90. return;
  91. }
  92. list_add_tail(nodePtr, &poolPtr->unused);
  93. poolPtr->unusedCount++;
  94. poolPtr->usedSize += poolPtr->dataSize;
  95. }
  96. while (poolPtr->unusedCount > poolPtr->maxPreallocated)
  97. {
  98. assert(! list_empty(&poolPtr->unused));
  99. nodePtr = poolPtr->unused.next;
  100. list_del(nodePtr);
  101. poolPtr->unusedCount--;
  102. free(nodePtr);
  103. poolPtr->usedSize -= poolPtr->dataSize;
  104. }
  105. }
  106. }
  107. // ------------------------------------------------------------------------------------------------
  108. bool rtsafe_memory_pool_create2(RtMemPool_Handle* handlePtr,
  109. const char* poolName,
  110. size_t dataSize,
  111. size_t minPreallocated,
  112. size_t maxPreallocated,
  113. int enforceThreadSafety)
  114. {
  115. assert(minPreallocated <= maxPreallocated);
  116. assert(poolName == NULL || strlen(poolName) < RTSAFE_MEMORY_POOL_NAME_MAX);
  117. RtMemPool* poolPtr;
  118. poolPtr = malloc(sizeof(RtMemPool));
  119. if (poolPtr == NULL)
  120. {
  121. return false;
  122. }
  123. if (poolName != NULL)
  124. {
  125. strcpy(poolPtr->name, poolName);
  126. }
  127. else
  128. {
  129. sprintf(poolPtr->name, "%p", poolPtr);
  130. }
  131. poolPtr->dataSize = dataSize;
  132. poolPtr->minPreallocated = minPreallocated;
  133. poolPtr->maxPreallocated = maxPreallocated;
  134. INIT_LIST_HEAD(&poolPtr->used);
  135. poolPtr->usedCount = 0;
  136. INIT_LIST_HEAD(&poolPtr->unused);
  137. poolPtr->unusedCount = 0;
  138. poolPtr->enforceThreadSafety = (enforceThreadSafety != 0);
  139. if (poolPtr->enforceThreadSafety)
  140. {
  141. if (pthread_mutex_init(&poolPtr->mutex, NULL) != 0)
  142. {
  143. free(poolPtr);
  144. return false;
  145. }
  146. INIT_LIST_HEAD(&poolPtr->pending);
  147. }
  148. poolPtr->unusedCount2 = 0;
  149. poolPtr->usedSize = 0;
  150. rtsafe_memory_pool_sleepy(poolPtr);
  151. *handlePtr = (RtMemPool_Handle)poolPtr;
  152. return true;
  153. }
  154. // ------------------------------------------------------------------------------------------------
  155. bool rtsafe_memory_pool_create(RtMemPool_Handle* handlePtr,
  156. const char* poolName,
  157. size_t dataSize,
  158. size_t minPreallocated,
  159. size_t maxPreallocated)
  160. {
  161. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated, 0);
  162. }
  163. // ------------------------------------------------------------------------------------------------
  164. bool rtsafe_memory_pool_create_safe(RtMemPool_Handle* handlePtr,
  165. const char* poolName,
  166. size_t dataSize,
  167. size_t minPreallocated,
  168. size_t maxPreallocated)
  169. {
  170. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated, 1);
  171. }
  172. // ------------------------------------------------------------------------------------------------
  173. void rtsafe_memory_pool_destroy(RtMemPool_Handle handle)
  174. {
  175. assert(handle);
  176. k_list_head* nodePtr;
  177. RtMemPool* poolPtr = (RtMemPool*)handle;
  178. // caller should deallocate all chunks prior releasing pool itself
  179. if (poolPtr->usedCount != 0)
  180. {
  181. assert(0);
  182. }
  183. while (poolPtr->unusedCount != 0)
  184. {
  185. assert(! list_empty(&poolPtr->unused));
  186. nodePtr = poolPtr->unused.next;
  187. list_del(nodePtr);
  188. poolPtr->unusedCount--;
  189. free(nodePtr);
  190. }
  191. assert(list_empty(&poolPtr->unused));
  192. if (poolPtr->enforceThreadSafety)
  193. {
  194. while (! list_empty(&poolPtr->pending))
  195. {
  196. nodePtr = poolPtr->pending.next;
  197. list_del(nodePtr);
  198. free(nodePtr);
  199. }
  200. int ret = pthread_mutex_destroy(&poolPtr->mutex);
  201. #ifdef DEBUG
  202. assert(ret == 0);
  203. #else
  204. // unused
  205. (void)ret;
  206. #endif
  207. }
  208. free(poolPtr);
  209. }
  210. // ------------------------------------------------------------------------------------------------
  211. // find entry in unused list, fail if it is empty
  212. void* rtsafe_memory_pool_allocate_atomic(RtMemPool_Handle handle)
  213. {
  214. assert(handle);
  215. k_list_head* nodePtr;
  216. RtMemPool* poolPtr = (RtMemPool*)handle;
  217. if (list_empty(&poolPtr->unused))
  218. {
  219. return NULL;
  220. }
  221. nodePtr = poolPtr->unused.next;
  222. list_del(nodePtr);
  223. poolPtr->unusedCount--;
  224. poolPtr->usedCount++;
  225. list_add_tail(nodePtr, &poolPtr->used);
  226. if (poolPtr->enforceThreadSafety && pthread_mutex_trylock(&poolPtr->mutex) == 0)
  227. {
  228. while (poolPtr->unusedCount < poolPtr->minPreallocated && ! list_empty(&poolPtr->pending))
  229. {
  230. nodePtr = poolPtr->pending.next;
  231. list_del(nodePtr);
  232. list_add_tail(nodePtr, &poolPtr->unused);
  233. poolPtr->unusedCount++;
  234. }
  235. poolPtr->unusedCount2 = poolPtr->unusedCount;
  236. pthread_mutex_unlock(&poolPtr->mutex);
  237. }
  238. return (nodePtr + 1);
  239. }
  240. // ------------------------------------------------------------------------------------------------
  241. void* rtsafe_memory_pool_allocate_sleepy(RtMemPool_Handle handle)
  242. {
  243. assert(handle);
  244. void* data;
  245. RtMemPool* poolPtr = (RtMemPool*)handle;
  246. do {
  247. rtsafe_memory_pool_sleepy(poolPtr);
  248. data = rtsafe_memory_pool_allocate_atomic((RtMemPool_Handle)poolPtr);
  249. }
  250. while (data == NULL);
  251. return data;
  252. }
  253. // ------------------------------------------------------------------------------------------------
  254. // move from used to unused list
  255. void rtsafe_memory_pool_deallocate(RtMemPool_Handle handle, void* memoryPtr)
  256. {
  257. assert(handle);
  258. k_list_head* nodePtr;
  259. RtMemPool* poolPtr = (RtMemPool*)handle;
  260. list_del((k_list_head*)memoryPtr - 1);
  261. list_add_tail((k_list_head*)memoryPtr - 1, &poolPtr->unused);
  262. poolPtr->usedCount--;
  263. poolPtr->unusedCount++;
  264. if (poolPtr->enforceThreadSafety && pthread_mutex_trylock(&poolPtr->mutex) == 0)
  265. {
  266. while (poolPtr->unusedCount > poolPtr->maxPreallocated)
  267. {
  268. assert(! list_empty(&poolPtr->unused));
  269. nodePtr = poolPtr->unused.next;
  270. list_del(nodePtr);
  271. list_add_tail(nodePtr, &poolPtr->pending);
  272. poolPtr->unusedCount--;
  273. }
  274. poolPtr->unusedCount2 = poolPtr->unusedCount;
  275. pthread_mutex_unlock(&poolPtr->mutex);
  276. }
  277. }
  278. #ifdef WANT_LV2
  279. # include "rtmempool-lv2.h"
  280. void lv2_rtmempool_init(LV2_RtMemPool_Pool* poolPtr)
  281. {
  282. poolPtr->create = rtsafe_memory_pool_create;
  283. poolPtr->destroy = rtsafe_memory_pool_destroy;
  284. poolPtr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
  285. poolPtr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
  286. poolPtr->deallocate = rtsafe_memory_pool_deallocate;
  287. }
  288. #endif