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.

396 lines
11KB

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