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.

468 lines
10KB

  1. /*
  2. * High-level, real-time safe, templated, C++ doubly-linked list
  3. * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the GPL.txt file
  16. */
  17. #ifndef __RT_LIST_HPP__
  18. #define __RT_LIST_HPP__
  19. #include "CarlaUtils.hpp"
  20. extern "C" {
  21. #include "rtmempool/list.h"
  22. #include "rtmempool/rtmempool.h"
  23. }
  24. // Declare non copyable and prevent heap allocation
  25. #define LIST_DECLARATIONS(className) \
  26. className(const className&); \
  27. className& operator= (const className&); \
  28. static void* operator new (size_t) { return nullptr; } \
  29. static void operator delete (void*) {}
  30. typedef struct list_head k_list_head;
  31. // -----------------------------------------------------------------------
  32. // Abstract List class
  33. // _allocate() and _deallocate are virtual calls provided by subclasses
  34. template<typename T>
  35. class List
  36. {
  37. protected:
  38. List()
  39. : kDataSize(sizeof(Data)),
  40. fCount(0)
  41. {
  42. _init();
  43. }
  44. public:
  45. virtual ~List()
  46. {
  47. CARLA_ASSERT(fCount == 0);
  48. }
  49. void clear()
  50. {
  51. if (fCount != 0)
  52. {
  53. k_list_head* entry;
  54. list_for_each(entry, &fQueue)
  55. {
  56. if (Data* data = list_entry(entry, Data, siblings))
  57. _deallocate(data);
  58. }
  59. }
  60. _init();
  61. }
  62. size_t count() const
  63. {
  64. return fCount;
  65. }
  66. bool isEmpty() const
  67. {
  68. return (fCount == 0);
  69. }
  70. bool append(const T& value)
  71. {
  72. if (Data* const data = _allocate())
  73. {
  74. std::memcpy(&data->value, &value, sizeof(T));
  75. list_add_tail(&data->siblings, &fQueue);
  76. fCount++;
  77. return true;
  78. }
  79. return false;
  80. }
  81. bool insert(const T& value)
  82. {
  83. if (Data* const data = _allocate())
  84. {
  85. std::memcpy(&data->value, &value, sizeof(T));
  86. list_add(&data->siblings, &fQueue);
  87. fCount++;
  88. return true;
  89. }
  90. return false;
  91. }
  92. T& getAt(const size_t index, const bool remove = false)
  93. {
  94. if (fCount == 0 || index >= fCount)
  95. return _retEmpty();
  96. size_t i = 0;
  97. Data* data = nullptr;
  98. k_list_head* entry;
  99. list_for_each(entry, &fQueue)
  100. {
  101. if (index != i++)
  102. continue;
  103. data = list_entry(entry, Data, siblings);
  104. if (remove)
  105. {
  106. fCount--;
  107. list_del(entry);
  108. if (data != nullptr)
  109. _deallocate(data);
  110. }
  111. break;
  112. }
  113. CARLA_ASSERT(data != nullptr);
  114. return (data != nullptr) ? data->value : _retEmpty();
  115. }
  116. T& getFirst(const bool remove = false)
  117. {
  118. return _getFirstOrLast(true, remove);
  119. }
  120. T& getLast(const bool remove = false)
  121. {
  122. return _getFirstOrLast(false, remove);
  123. }
  124. bool removeOne(const T& value)
  125. {
  126. Data* data = nullptr;
  127. k_list_head* entry;
  128. list_for_each(entry, &fQueue)
  129. {
  130. data = list_entry(entry, Data, siblings);
  131. CARLA_ASSERT(data != nullptr);
  132. if (data != nullptr && data->value == value)
  133. {
  134. fCount--;
  135. list_del(entry);
  136. _deallocate(data);
  137. break;
  138. }
  139. }
  140. return (data != nullptr);
  141. }
  142. void removeAll(const T& value)
  143. {
  144. Data* data;
  145. k_list_head* entry;
  146. k_list_head* tmp;
  147. list_for_each_safe(entry, tmp, &fQueue)
  148. {
  149. data = list_entry(entry, Data, siblings);
  150. CARLA_ASSERT(data != nullptr);
  151. if (data != nullptr && data->value == value)
  152. {
  153. fCount--;
  154. list_del(entry);
  155. _deallocate(data);
  156. }
  157. }
  158. }
  159. virtual void splice(List& list, const bool init = false)
  160. {
  161. if (init)
  162. {
  163. list_splice_init(&fQueue, &list.fQueue);
  164. list.fCount += fCount;
  165. fCount = 0;
  166. }
  167. else
  168. {
  169. list_splice(&fQueue, &list.fQueue);
  170. list.fCount += fCount;
  171. }
  172. }
  173. protected:
  174. const size_t kDataSize;
  175. size_t fCount;
  176. k_list_head fQueue;
  177. struct Data {
  178. T value;
  179. k_list_head siblings;
  180. };
  181. virtual Data* _allocate() = 0;
  182. virtual void _deallocate(Data* const dataPtr) = 0;
  183. private:
  184. void _init()
  185. {
  186. fCount = 0;
  187. INIT_LIST_HEAD(&fQueue);
  188. }
  189. T& _getFirstOrLast(const bool first, const bool remove)
  190. {
  191. if (fCount == 0)
  192. return _retEmpty();
  193. k_list_head* const entry = first ? fQueue.next : fQueue.prev;
  194. Data* const data = list_entry(entry, Data, siblings);
  195. CARLA_ASSERT(data != nullptr);
  196. if (data == nullptr)
  197. return _retEmpty();
  198. T& ret = data->value;
  199. if (data != nullptr && remove)
  200. {
  201. fCount--;
  202. list_del(entry);
  203. _deallocate(data);
  204. }
  205. return ret;
  206. }
  207. T& _retEmpty()
  208. {
  209. // FIXME ?
  210. static T value;
  211. static bool reset = true;
  212. if (reset)
  213. {
  214. reset = false;
  215. carla_zeroMem(&value, sizeof(T));
  216. }
  217. return value;
  218. }
  219. LIST_DECLARATIONS(List)
  220. };
  221. // -----------------------------------------------------------------------
  222. // Realtime safe list
  223. template<typename T>
  224. class RtList : public List<T>
  225. {
  226. public:
  227. // -------------------------------------------------------------------
  228. // RtMemPool C++ class
  229. class Pool
  230. {
  231. public:
  232. Pool(const size_t minPreallocated, const size_t maxPreallocated)
  233. : fHandle(nullptr),
  234. kDataSize(sizeof(typename List<T>::Data))
  235. {
  236. rtsafe_memory_pool_create(&fHandle, nullptr, kDataSize, minPreallocated, maxPreallocated);
  237. CARLA_ASSERT(fHandle != nullptr);
  238. }
  239. ~Pool()
  240. {
  241. if (fHandle != nullptr)
  242. rtsafe_memory_pool_destroy(fHandle);
  243. }
  244. void* allocate_atomic()
  245. {
  246. return rtsafe_memory_pool_allocate_atomic(fHandle);
  247. }
  248. void* allocate_sleepy()
  249. {
  250. return rtsafe_memory_pool_allocate_sleepy(fHandle);
  251. }
  252. void deallocate(void* const dataPtr)
  253. {
  254. rtsafe_memory_pool_deallocate(fHandle, dataPtr);
  255. }
  256. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  257. {
  258. if (fHandle != nullptr)
  259. {
  260. rtsafe_memory_pool_destroy(fHandle);
  261. fHandle = nullptr;
  262. }
  263. rtsafe_memory_pool_create(&fHandle, nullptr, kDataSize, minPreallocated, maxPreallocated);
  264. CARLA_ASSERT(fHandle != nullptr);
  265. }
  266. private:
  267. RtMemPool_Handle fHandle;
  268. const size_t kDataSize;
  269. };
  270. // -------------------------------------------------------------------
  271. // Now the actual list code
  272. RtList(Pool* const memPool)
  273. : kMemPool(memPool)
  274. {
  275. CARLA_ASSERT(kMemPool != nullptr);
  276. }
  277. ~RtList()
  278. {
  279. }
  280. void append_sleepy(const T& value)
  281. {
  282. if (typename List<T>::Data* const data = _allocate_sleepy())
  283. {
  284. std::memcpy(&data->value, &value, sizeof(T));
  285. list_add_tail(&data->siblings, &this->fQueue);
  286. this->fCount++;
  287. }
  288. }
  289. void insert_sleepy(const T& value)
  290. {
  291. if (typename List<T>::Data* const data = _allocate_sleepy())
  292. {
  293. std::memcpy(&data->value, &value, sizeof(T));
  294. list_add(&data->siblings, &this->fQueue);
  295. this->fCount++;
  296. }
  297. }
  298. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  299. {
  300. this->clear();
  301. kMemPool->resize(minPreallocated, maxPreallocated);
  302. }
  303. void splice(RtList& list, const bool init = false)
  304. {
  305. CARLA_ASSERT(kMemPool == list.kMemPool);
  306. List<T>::splice(list, init);
  307. }
  308. private:
  309. Pool* const kMemPool;
  310. typename List<T>::Data* _allocate()
  311. {
  312. return _allocate_atomic();
  313. }
  314. typename List<T>::Data* _allocate_atomic()
  315. {
  316. return (typename List<T>::Data*)kMemPool->allocate_atomic();
  317. }
  318. typename List<T>::Data* _allocate_sleepy()
  319. {
  320. return (typename List<T>::Data*)kMemPool->allocate_sleepy();
  321. }
  322. void _deallocate(typename List<T>::Data* const dataPtr)
  323. {
  324. kMemPool->deallocate(dataPtr);
  325. }
  326. LIST_DECLARATIONS(RtList)
  327. };
  328. // -----------------------------------------------------------------------
  329. // Non-Realtime list, using malloc/free methods
  330. template<typename T>
  331. class NonRtList : public List<T>
  332. {
  333. public:
  334. NonRtList()
  335. {
  336. }
  337. ~NonRtList()
  338. {
  339. }
  340. private:
  341. typename List<T>::Data* _allocate()
  342. {
  343. return (typename List<T>::Data*)malloc(this->kDataSize);
  344. }
  345. void _deallocate(typename List<T>::Data* const dataPtr)
  346. {
  347. free(dataPtr);
  348. }
  349. LIST_DECLARATIONS(NonRtList)
  350. };
  351. // -----------------------------------------------------------------------
  352. // Non-Realtime list, using new/delete methods
  353. template<typename T>
  354. class NonRtListNew : public List<T>
  355. {
  356. public:
  357. NonRtListNew()
  358. {
  359. }
  360. ~NonRtListNew()
  361. {
  362. }
  363. private:
  364. typename List<T>::Data* _allocate()
  365. {
  366. return new typename List<T>::Data;
  367. }
  368. void _deallocate(typename List<T>::Data* const dataPtr)
  369. {
  370. delete dataPtr;
  371. }
  372. LIST_DECLARATIONS(NonRtListNew)
  373. };
  374. // -----------------------------------------------------------------------
  375. #endif // __RT_LIST_HPP__