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.

571 lines
13KB

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