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.

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