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.

475 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. extern "C" {
  20. #include "rtmempool/list.h"
  21. #include "rtmempool/rtmempool.h"
  22. }
  23. #include "carla_utils.hpp"
  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.prev : fQueue.next;
  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. std::memset(&value, 0, sizeof(T));
  220. }
  221. return value;
  222. }
  223. LIST_DECLARATIONS(List)
  224. //template<typename P> friend class Pool;
  225. };
  226. // -----------------------------------------------------------------------
  227. // Realtime safe list
  228. template<typename T>
  229. class RtList : public List<T>
  230. {
  231. public:
  232. // -------------------------------------------------------------------
  233. // RtMemPool C++ class
  234. class Pool
  235. {
  236. public:
  237. Pool(const size_t minPreallocated, const size_t maxPreallocated)
  238. : fHandle(nullptr),
  239. kDataSize(sizeof(typename List<T>::Data))
  240. {
  241. rtsafe_memory_pool_create(&fHandle, nullptr, kDataSize, minPreallocated, maxPreallocated);
  242. CARLA_ASSERT(fHandle != nullptr);
  243. }
  244. ~Pool()
  245. {
  246. if (fHandle != nullptr)
  247. rtsafe_memory_pool_destroy(fHandle);
  248. }
  249. void* allocate_atomic()
  250. {
  251. return rtsafe_memory_pool_allocate_atomic(fHandle);
  252. }
  253. void* allocate_sleepy()
  254. {
  255. return rtsafe_memory_pool_allocate_sleepy(fHandle);
  256. }
  257. void deallocate(void* const dataPtr)
  258. {
  259. rtsafe_memory_pool_deallocate(fHandle, dataPtr);
  260. }
  261. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  262. {
  263. if (fHandle != nullptr)
  264. {
  265. rtsafe_memory_pool_destroy(fHandle);
  266. fHandle = nullptr;
  267. }
  268. rtsafe_memory_pool_create(&fHandle, nullptr, kDataSize, minPreallocated, maxPreallocated);
  269. CARLA_ASSERT(fHandle != nullptr);
  270. }
  271. private:
  272. RtMemPool_Handle fHandle;
  273. const size_t kDataSize;
  274. };
  275. // -------------------------------------------------------------------
  276. // Now the actual list code
  277. RtList(Pool* const memPool)
  278. : kMemPool(memPool)
  279. {
  280. CARLA_ASSERT(kMemPool != nullptr);
  281. }
  282. ~RtList()
  283. {
  284. }
  285. void append_sleepy(const T& value)
  286. {
  287. if (typename List<T>::Data* const data = _allocate_sleepy())
  288. {
  289. std::memcpy(&data->value, &value, sizeof(T));
  290. list_add_tail(&data->siblings, &this->fQueue);
  291. this->fCount++;
  292. }
  293. }
  294. void insert_sleepy(const T& value)
  295. {
  296. if (typename List<T>::Data* const data = _allocate_sleepy())
  297. {
  298. std::memcpy(&data->value, &value, sizeof(T));
  299. list_add(&data->siblings, &this->fQueue);
  300. this->fCount++;
  301. }
  302. }
  303. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  304. {
  305. this->clear();
  306. kMemPool->resize(minPreallocated, maxPreallocated);
  307. }
  308. void splice(RtList& list, const bool init = false)
  309. {
  310. CARLA_ASSERT(kMemPool == list.kMemPool);
  311. List<T>::splice(list, init);
  312. }
  313. private:
  314. Pool* const kMemPool;
  315. typename List<T>::Data* _allocate()
  316. {
  317. return _allocate_atomic();
  318. }
  319. typename List<T>::Data* _allocate_atomic()
  320. {
  321. return (typename List<T>::Data*)kMemPool->allocate_atomic();
  322. }
  323. typename List<T>::Data* _allocate_sleepy()
  324. {
  325. return (typename List<T>::Data*)kMemPool->allocate_sleepy();
  326. }
  327. void _deallocate(typename List<T>::Data* const dataPtr)
  328. {
  329. kMemPool->deallocate(dataPtr);
  330. }
  331. LIST_DECLARATIONS(RtList)
  332. };
  333. // -----------------------------------------------------------------------
  334. // Non-Realtime list, using malloc/free methods
  335. template<typename T>
  336. class NonRtList : public List<T>
  337. {
  338. public:
  339. NonRtList()
  340. {
  341. }
  342. ~NonRtList()
  343. {
  344. }
  345. private:
  346. typename List<T>::Data* _allocate()
  347. {
  348. return (typename List<T>::Data*)malloc(this->fDataSize);
  349. }
  350. void _deallocate(typename List<T>::Data* const dataPtr)
  351. {
  352. free(dataPtr);
  353. }
  354. LIST_DECLARATIONS(NonRtList)
  355. };
  356. // -----------------------------------------------------------------------
  357. // Non-Realtime list, using new/delete methods
  358. template<typename T>
  359. class NonRtListNew : public List<T>
  360. {
  361. public:
  362. NonRtListNew()
  363. {
  364. }
  365. ~NonRtListNew()
  366. {
  367. }
  368. private:
  369. typename List<T>::Data* _allocate()
  370. {
  371. return new typename List<T>::Data;
  372. }
  373. void _deallocate(typename List<T>::Data* const dataPtr)
  374. {
  375. delete dataPtr;
  376. }
  377. LIST_DECLARATIONS(NonRtListNew)
  378. };
  379. // -----------------------------------------------------------------------
  380. #endif // __RT_LIST_HPP__