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.

393 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-2014 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. // ------------------------------------------------------------------------------------------------
  27. typedef struct list_head k_list_head;
  28. // ------------------------------------------------------------------------------------------------
  29. typedef struct _RtMemPool
  30. {
  31. char name[RTSAFE_MEMORY_POOL_NAME_MAX];
  32. size_t dataSize;
  33. size_t minPreallocated;
  34. size_t maxPreallocated;
  35. k_list_head used;
  36. unsigned int usedCount;
  37. k_list_head unused;
  38. unsigned int unusedCount;
  39. bool enforceThreadSafety;
  40. // next members are initialized/used only if enforceThreadSafety is true
  41. pthread_mutex_t mutex;
  42. unsigned int unusedCount2;
  43. k_list_head pending;
  44. size_t usedSize;
  45. } RtMemPool;
  46. // ------------------------------------------------------------------------------------------------
  47. // adjust unused list size
  48. static void rtsafe_memory_pool_sleepy(RtMemPool* poolPtr)
  49. {
  50. k_list_head* nodePtr;
  51. unsigned int count;
  52. if (poolPtr->enforceThreadSafety)
  53. {
  54. pthread_mutex_lock(&poolPtr->mutex);
  55. count = poolPtr->unusedCount2;
  56. assert(poolPtr->minPreallocated < poolPtr->maxPreallocated);
  57. while (count < poolPtr->minPreallocated)
  58. {
  59. nodePtr = malloc(sizeof(k_list_head) + poolPtr->dataSize);
  60. if (nodePtr == NULL)
  61. {
  62. break;
  63. }
  64. list_add_tail(nodePtr, &poolPtr->pending);
  65. count++;
  66. poolPtr->usedSize += poolPtr->dataSize;
  67. }
  68. while (count > poolPtr->maxPreallocated && ! list_empty(&poolPtr->pending))
  69. {
  70. nodePtr = poolPtr->pending.next;
  71. list_del(nodePtr);
  72. free(nodePtr);
  73. count--;
  74. poolPtr->usedSize -= poolPtr->dataSize;
  75. }
  76. pthread_mutex_unlock(&poolPtr->mutex);
  77. }
  78. else
  79. {
  80. while (poolPtr->unusedCount < poolPtr->minPreallocated)
  81. {
  82. nodePtr = malloc(sizeof(k_list_head) + poolPtr->dataSize);
  83. if (nodePtr == NULL)
  84. {
  85. return;
  86. }
  87. list_add_tail(nodePtr, &poolPtr->unused);
  88. poolPtr->unusedCount++;
  89. poolPtr->usedSize += poolPtr->dataSize;
  90. }
  91. while (poolPtr->unusedCount > poolPtr->maxPreallocated)
  92. {
  93. assert(! list_empty(&poolPtr->unused));
  94. nodePtr = poolPtr->unused.next;
  95. list_del(nodePtr);
  96. poolPtr->unusedCount--;
  97. free(nodePtr);
  98. poolPtr->usedSize -= poolPtr->dataSize;
  99. }
  100. }
  101. }
  102. // ------------------------------------------------------------------------------------------------
  103. static bool rtsafe_memory_pool_create2(RtMemPool_Handle* handlePtr,
  104. const char* poolName,
  105. size_t dataSize,
  106. size_t minPreallocated,
  107. size_t maxPreallocated,
  108. int enforceThreadSafety)
  109. {
  110. assert(minPreallocated <= maxPreallocated);
  111. assert(poolName == NULL || strlen(poolName) < RTSAFE_MEMORY_POOL_NAME_MAX);
  112. RtMemPool* poolPtr;
  113. poolPtr = malloc(sizeof(RtMemPool));
  114. if (poolPtr == NULL)
  115. {
  116. return false;
  117. }
  118. if (poolName != NULL)
  119. {
  120. strcpy(poolPtr->name, poolName);
  121. }
  122. else
  123. {
  124. sprintf(poolPtr->name, "%p", poolPtr);
  125. }
  126. poolPtr->dataSize = dataSize;
  127. poolPtr->minPreallocated = minPreallocated;
  128. poolPtr->maxPreallocated = maxPreallocated;
  129. INIT_LIST_HEAD(&poolPtr->used);
  130. poolPtr->usedCount = 0;
  131. INIT_LIST_HEAD(&poolPtr->unused);
  132. poolPtr->unusedCount = 0;
  133. poolPtr->enforceThreadSafety = (enforceThreadSafety != 0);
  134. if (poolPtr->enforceThreadSafety)
  135. {
  136. if (pthread_mutex_init(&poolPtr->mutex, NULL) != 0)
  137. {
  138. free(poolPtr);
  139. return false;
  140. }
  141. INIT_LIST_HEAD(&poolPtr->pending);
  142. }
  143. poolPtr->unusedCount2 = 0;
  144. poolPtr->usedSize = 0;
  145. rtsafe_memory_pool_sleepy(poolPtr);
  146. *handlePtr = (RtMemPool_Handle)poolPtr;
  147. return true;
  148. }
  149. // ------------------------------------------------------------------------------------------------
  150. static unsigned char rtsafe_memory_pool_create_old(const char* poolName, size_t dataSize, size_t minPreallocated, size_t maxPreallocated, RtMemPool_Handle* handlePtr)
  151. {
  152. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated, 0);
  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. void lv2_rtmempool_init(LV2_RtMemPool_Pool* poolPtr)
  279. {
  280. poolPtr->create = rtsafe_memory_pool_create;
  281. poolPtr->destroy = rtsafe_memory_pool_destroy;
  282. poolPtr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
  283. poolPtr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
  284. poolPtr->deallocate = rtsafe_memory_pool_deallocate;
  285. }
  286. void lv2_rtmempool_init_deprecated(LV2_RtMemPool_Pool_Deprecated* poolPtr)
  287. {
  288. poolPtr->create = rtsafe_memory_pool_create_old;
  289. poolPtr->destroy = rtsafe_memory_pool_destroy;
  290. poolPtr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
  291. poolPtr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
  292. poolPtr->deallocate = rtsafe_memory_pool_deallocate;
  293. }