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.

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