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.

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