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.

380 lines
9.7KB

  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. 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. 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. bool 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;
  133. if (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. bool rtsafe_memory_pool_create(RtMemPool_Handle* handlePtr,
  150. const char* poolName,
  151. size_t dataSize,
  152. size_t minPreallocated,
  153. size_t maxPreallocated)
  154. {
  155. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated, false);
  156. }
  157. // ------------------------------------------------------------------------------------------------
  158. bool rtsafe_memory_pool_create_safe(RtMemPool_Handle* handlePtr,
  159. const char* poolName,
  160. size_t dataSize,
  161. size_t minPreallocated,
  162. size_t maxPreallocated)
  163. {
  164. return rtsafe_memory_pool_create2(handlePtr, poolName, dataSize, minPreallocated, maxPreallocated, true);
  165. }
  166. // ------------------------------------------------------------------------------------------------
  167. void rtsafe_memory_pool_destroy(RtMemPool_Handle handle)
  168. {
  169. assert(handle);
  170. k_list_head* nodePtr;
  171. RtMemPool* poolPtr = (RtMemPool*)handle;
  172. // caller should deallocate all chunks prior releasing pool itself
  173. if (poolPtr->usedCount != 0)
  174. {
  175. assert(0);
  176. }
  177. while (poolPtr->unusedCount != 0)
  178. {
  179. assert(! list_empty(&poolPtr->unused));
  180. nodePtr = poolPtr->unused.next;
  181. list_del(nodePtr);
  182. poolPtr->unusedCount--;
  183. free(nodePtr);
  184. }
  185. assert(list_empty(&poolPtr->unused));
  186. if (poolPtr->enforceThreadSafety)
  187. {
  188. while (! list_empty(&poolPtr->pending))
  189. {
  190. nodePtr = poolPtr->pending.next;
  191. list_del(nodePtr);
  192. free(nodePtr);
  193. }
  194. int ret = pthread_mutex_destroy(&poolPtr->mutex);
  195. #ifdef DEBUG
  196. assert(ret == 0);
  197. #else
  198. // unused
  199. (void)ret;
  200. #endif
  201. }
  202. free(poolPtr);
  203. }
  204. // ------------------------------------------------------------------------------------------------
  205. // find entry in unused list, fail if it is empty
  206. void* rtsafe_memory_pool_allocate_atomic(RtMemPool_Handle handle)
  207. {
  208. assert(handle);
  209. k_list_head* nodePtr;
  210. RtMemPool* poolPtr = (RtMemPool*)handle;
  211. if (list_empty(&poolPtr->unused))
  212. {
  213. return NULL;
  214. }
  215. nodePtr = poolPtr->unused.next;
  216. list_del(nodePtr);
  217. poolPtr->unusedCount--;
  218. poolPtr->usedCount++;
  219. list_add_tail(nodePtr, &poolPtr->used);
  220. if (poolPtr->enforceThreadSafety && pthread_mutex_trylock(&poolPtr->mutex) == 0)
  221. {
  222. while (poolPtr->unusedCount < poolPtr->minPreallocated && ! list_empty(&poolPtr->pending))
  223. {
  224. nodePtr = poolPtr->pending.next;
  225. list_del(nodePtr);
  226. list_add_tail(nodePtr, &poolPtr->unused);
  227. poolPtr->unusedCount++;
  228. }
  229. poolPtr->unusedCount2 = poolPtr->unusedCount;
  230. pthread_mutex_unlock(&poolPtr->mutex);
  231. }
  232. return (nodePtr + 1);
  233. }
  234. // ------------------------------------------------------------------------------------------------
  235. void* rtsafe_memory_pool_allocate_sleepy(RtMemPool_Handle handle)
  236. {
  237. assert(handle);
  238. void* data;
  239. RtMemPool* poolPtr = (RtMemPool*)handle;
  240. do {
  241. rtsafe_memory_pool_sleepy(poolPtr);
  242. data = rtsafe_memory_pool_allocate_atomic((RtMemPool_Handle)poolPtr);
  243. }
  244. while (data == NULL);
  245. return data;
  246. }
  247. // ------------------------------------------------------------------------------------------------
  248. // move from used to unused list
  249. void rtsafe_memory_pool_deallocate(RtMemPool_Handle handle, void* memoryPtr)
  250. {
  251. assert(handle);
  252. k_list_head* nodePtr;
  253. RtMemPool* poolPtr = (RtMemPool*)handle;
  254. list_del((k_list_head*)memoryPtr - 1);
  255. list_add_tail((k_list_head*)memoryPtr - 1, &poolPtr->unused);
  256. poolPtr->usedCount--;
  257. poolPtr->unusedCount++;
  258. if (poolPtr->enforceThreadSafety && pthread_mutex_trylock(&poolPtr->mutex) == 0)
  259. {
  260. while (poolPtr->unusedCount > poolPtr->maxPreallocated)
  261. {
  262. assert(! list_empty(&poolPtr->unused));
  263. nodePtr = poolPtr->unused.next;
  264. list_del(nodePtr);
  265. list_add_tail(nodePtr, &poolPtr->pending);
  266. poolPtr->unusedCount--;
  267. }
  268. poolPtr->unusedCount2 = poolPtr->unusedCount;
  269. pthread_mutex_unlock(&poolPtr->mutex);
  270. }
  271. }
  272. #ifdef WANT_LV2
  273. # include "lv2/lv2_rtmempool.h"
  274. void lv2_rtmempool_init(LV2_RtMemPool_Pool* poolPtr)
  275. {
  276. poolPtr->create = rtsafe_memory_pool_create;
  277. poolPtr->destroy = rtsafe_memory_pool_destroy;
  278. poolPtr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
  279. poolPtr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
  280. poolPtr->deallocate = rtsafe_memory_pool_deallocate;
  281. }
  282. #endif