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.

530 lines
12KB

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