Arduino DLNA Server
Loading...
Searching...
No Matches
ListLockFree.h
Go to the documentation of this file.
1#pragma once
2#include <stddef.h>
3
4#include <atomic>
5#include <memory>
6
7#include "basic/Allocator.h"
8
9namespace tiny_dlna {
10
27template <class T, class Alloc = DLNA_ALLOCATOR<T>>
29 public:
30 struct Node {
31 std::atomic<Node*> next{nullptr};
32 std::atomic<Node*> prior{nullptr};
34
35 Node() = default;
36 Node(const T& value) : data(value) {}
37 };
38
44 class Iterator {
45 public:
46
47 Iterator(Node* node, ListLockFree* owner_ptr = nullptr) : node(node), owner(owner_ptr) {
48 if (node == nullptr) is_eof = true;
49 }
50
52 if (node != nullptr && owner != nullptr) {
53 Node* next_node = node->next.load(std::memory_order_acquire);
54 if (next_node != nullptr && next_node != &owner->last) {
55 node = next_node;
56 is_eof = false;
57 } else {
58 node = nullptr;
59 is_eof = true;
60 }
61 }
62 return *this;
63 }
64
65 inline Iterator operator++(int) {
66 Iterator tmp = *this;
67 ++(*this);
68 return tmp;
69 }
70
72 if (node != nullptr && owner != nullptr) {
73 Node* prior_node = node->prior.load(std::memory_order_acquire);
74 if (prior_node != nullptr && prior_node != &owner->first) {
75 node = prior_node;
76 is_eof = false;
77 } else {
78 node = nullptr;
79 is_eof = true;
80 }
81 }
82 return *this;
83 }
84
85 inline Iterator operator--(int) {
86 Iterator tmp = *this;
87 --(*this);
88 return tmp;
89 }
90
91 inline Iterator operator+(int offset) {
92 return getIteratorAtOffset(offset);
93 }
94
95 inline Iterator operator-(int offset) {
96 return getIteratorAtOffset(-offset);
97 }
98
99
100 inline bool operator==(const Iterator& it) const {
101 return node == it.node && owner == it.owner;
102 }
103
104 inline bool operator!=(const Iterator& it) const {
105 return !(*this == it);
106 }
107
108
109 inline T& operator*() {
110 if (node == nullptr) throw std::out_of_range("Iterator dereference: nullptr");
111 return node->data;
112 }
113
114 inline T* operator->() {
115 if (node == nullptr) throw std::out_of_range("Iterator dereference: nullptr");
116 return &(node->data);
117 }
118
119
120 inline Node* get_node() { return node; }
121
122 inline operator bool() const { return !is_eof; }
123
124 void set_owner(ListLockFree* owner_ptr) { owner = owner_ptr; }
125
126 protected:
127 Node* node = nullptr;
128 bool is_eof = false;
129 ListLockFree* owner = nullptr;
130
132 Node* tmp = node;
133 if (offset > 0) {
134 for (int j = 0; j < offset && tmp != nullptr; j++) {
135 Node* next_node = tmp->next.load(std::memory_order_acquire);
136 if (next_node == nullptr || next_node == &owner->last) {
137 break;
138 }
139 tmp = next_node;
140 }
141 } else if (offset < 0) {
142 for (int j = 0; j < -offset && tmp != nullptr; j++) {
143 Node* prior_node = tmp->prior.load(std::memory_order_acquire);
144 if (prior_node == nullptr || prior_node == &owner->first) {
145 break;
146 }
147 tmp = prior_node;
148 }
149 }
150 Iterator it(tmp);
151 it.set_owner(owner);
152 return it;
153 }
154 };
155
156
157 using NodeAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<Node>;
158
160 ListLockFree(const Alloc& alloc = Alloc()) : node_alloc_(alloc) {
161 link();
162 }
163
166 link();
167 // Copy elements (this is not thread-safe for the source)
168 Node* current = ref.first.next.load(std::memory_order_acquire);
169 while (current != &ref.last) {
170 push_back(current->data);
171 current = current->next.load(std::memory_order_acquire);
172 }
173 }
174
176 template <size_t N>
177 ListLockFree(const T (&a)[N], const Alloc& alloc = Alloc()) : node_alloc_(alloc) {
178 link();
179 for (int i = 0; i < N; ++i) {
180 push_back(a[i]);
181 }
182 }
183
185
191 // Not implemented. A lock-free swap would require complex atomic operations.
192 return false;
193 }
194
195 bool push_back(const T& data) {
196 Node* node = createNode();
197 if (node == nullptr) return false;
198 node->data = data;
199
200 while (true) {
201 Node* old_last_prior = last.prior.load(std::memory_order_acquire);
202
203 // Try to link the new node
204 node->next.store(&last, std::memory_order_relaxed);
205 node->prior.store(old_last_prior, std::memory_order_relaxed);
206
207 // Atomically update the prior node's next pointer
208 Node* expected_next = &last;
209 if (old_last_prior->next.compare_exchange_weak(
210 expected_next, node, std::memory_order_release,
211 std::memory_order_relaxed)) {
212 // Atomically update last's prior pointer
213 Node* expected_prior = old_last_prior;
214 if (last.prior.compare_exchange_weak(expected_prior, node,
215 std::memory_order_release,
216 std::memory_order_relaxed)) {
217 record_count.fetch_add(1, std::memory_order_relaxed);
218 return true;
219 }
220
221 // Rollback the first change
222 old_last_prior->next.store(&last, std::memory_order_relaxed);
223 }
224 }
225 }
226
227 bool push_front(const T& data) {
228 Node* node = createNode();
229 if (node == nullptr) return false;
230 node->data = data;
231
232 while (true) {
233 Node* old_first_next = first.next.load(std::memory_order_acquire);
234
235 // Try to link the new node
236 node->prior.store(&first, std::memory_order_relaxed);
237 node->next.store(old_first_next, std::memory_order_relaxed);
238
239 // Atomically update the next node's prior pointer
240 Node* expected_prior = &first;
241 if (old_first_next->prior.compare_exchange_weak(
242 expected_prior, node, std::memory_order_release,
243 std::memory_order_relaxed)) {
244 // Atomically update first's next pointer
245 Node* expected_next = old_first_next;
246 if (first.next.compare_exchange_weak(expected_next, node,
247 std::memory_order_release,
248 std::memory_order_relaxed)) {
249 record_count.fetch_add(1, std::memory_order_relaxed);
250 return true;
251 }
252
253 // Rollback the first change
254 old_first_next->prior.store(&first, std::memory_order_relaxed);
255 }
256 }
257 }
258
259 bool insert(Iterator it, const T& data) {
260 Node* node = createNode();
261 if (node == nullptr) return false;
262 node->data = data;
263
264 Node* current_node = it.get_node();
265 if (current_node == nullptr) return false;
266
267 while (true) {
268 Node* prior = current_node->prior.load(std::memory_order_acquire);
269 if (prior == nullptr) return false;
270
271 // Set up new node links
272 node->prior.store(prior, std::memory_order_relaxed);
273 node->next.store(current_node, std::memory_order_relaxed);
274
275 // Try to atomically update the prior node's next pointer
276 Node* expected_next = current_node;
277 if (prior->next.compare_exchange_weak(expected_next, node,
278 std::memory_order_release,
279 std::memory_order_relaxed)) {
280 // Try to atomically update current node's prior pointer
281 Node* expected_prior = prior;
282 if (current_node->prior.compare_exchange_weak(
283 expected_prior, node, std::memory_order_release,
284 std::memory_order_relaxed)) {
285 record_count.fetch_add(1, std::memory_order_relaxed);
286 return true;
287 }
288
289 // Rollback the prior->next change
290 prior->next.store(current_node, std::memory_order_relaxed);
291 }
292 }
293 }
294
295 bool pop_front() {
296 T tmp;
297 return pop_front(tmp);
298 }
299
300 bool pop_back() {
301 T tmp;
302 return pop_back(tmp);
303 }
304
305 bool pop_front(T& data) {
306 while (true) {
307 Node* first_data = first.next.load(std::memory_order_acquire);
308 if (first_data == &last) return false; // Empty list
309
310 Node* next_node = first_data->next.load(std::memory_order_acquire);
311 data = first_data->data;
312
313 // Try to atomically update the links
314 if (first.next.compare_exchange_weak(first_data, next_node,
315 std::memory_order_release,
316 std::memory_order_relaxed)) {
317 if (next_node->prior.compare_exchange_weak(first_data, &first,
318 std::memory_order_release,
319 std::memory_order_relaxed)) {
320 deleteNode(first_data);
321 record_count.fetch_sub(1, std::memory_order_relaxed);
322 return true;
323 }
324
325 // Rollback
326 first.next.store(first_data, std::memory_order_relaxed);
327 }
328 }
329 }
330
331 bool pop_back(T& data) {
332 while (true) {
333 Node* last_data = last.prior.load(std::memory_order_acquire);
334 if (last_data == &first) return false; // Empty list
335
336 Node* prior_node = last_data->prior.load(std::memory_order_acquire);
337 data = last_data->data;
338
339 // Try to atomically update the links
340 if (last.prior.compare_exchange_weak(last_data, prior_node,
341 std::memory_order_release,
342 std::memory_order_relaxed)) {
343 if (prior_node->next.compare_exchange_weak(last_data, &last,
344 std::memory_order_release,
345 std::memory_order_relaxed)) {
346 deleteNode(last_data);
347 record_count.fetch_sub(1, std::memory_order_relaxed);
348 return true;
349 }
350
351 // Rollback
352 last.prior.store(last_data, std::memory_order_relaxed);
353 }
354 }
355 }
356
357 bool erase(Iterator it) {
358 Node* p_delete = it.get_node();
359 if (p_delete == nullptr || p_delete == &first || p_delete == &last) {
360 return false;
361 }
362
363 while (true) {
364 Node* prior_node = p_delete->prior.load(std::memory_order_acquire);
365 Node* next_node = p_delete->next.load(std::memory_order_acquire);
366
367 if (prior_node == nullptr || next_node == nullptr) return false;
368
369 // Try to atomically update the links
370 if (prior_node->next.compare_exchange_weak(p_delete, next_node,
371 std::memory_order_release,
372 std::memory_order_relaxed)) {
373 if (next_node->prior.compare_exchange_weak(p_delete, prior_node,
374 std::memory_order_release,
375 std::memory_order_relaxed)) {
376 deleteNode(p_delete);
377 record_count.fetch_sub(1, std::memory_order_relaxed);
378 return true;
379 }
380
381 // Rollback
382 prior_node->next.store(p_delete, std::memory_order_relaxed);
383 }
384 }
385 }
386
387
389 Node* first_data = first.next.load(std::memory_order_acquire);
390 Iterator it(first_data == &last ? nullptr : first_data, this);
391 return it;
392 }
393
394
396 Iterator it(nullptr, this);
397 return it;
398 }
399
400
402 Node* last_data = last.prior.load(std::memory_order_acquire);
403 Iterator it(last_data == &first ? nullptr : last_data, this);
404 return it;
405 }
406
407
409 Iterator it(nullptr, this);
410 return it;
411 }
412
413 size_t size() { return record_count.load(std::memory_order_relaxed); }
414
415 bool empty() { return size() == 0; }
416
417 bool clear() {
418 while (pop_front()) {
419 // Keep removing elements
420 }
421 return true;
422 }
423
431 inline T& operator[](int index) {
432 Node* n = first.next.load(std::memory_order_acquire);
433 for (int j = 0; j < index && n != &last; j++) {
434 n = n->next.load(std::memory_order_acquire);
435 if (n == nullptr) {
436 static T dummy{};
437 return dummy;
438 }
439 }
440 if (n == &last) {
441 static T dummy{};
442 return dummy;
443 }
444 return n->data;
445 }
446
448 T& back() {
449 Node* last_data = last.prior.load(std::memory_order_acquire);
450 return last_data != &first ? last_data->data : last.data;
451 }
452
454 T& front() {
455 Node* first_data = first.next.load(std::memory_order_acquire);
456 return first_data != &last ? first_data->data : first.data;
457 }
458
459
460protected:
461 Node first; // empty dummy first node
462 Node last; // empty dummy last node
463 std::atomic<size_t> record_count{0};
465
467 Node* raw = node_alloc_.allocate(1);
468 new (raw) Node();
469 return raw;
470 }
471
472 void deleteNode(Node* p_delete) {
473 if (!p_delete) return;
474 p_delete->~Node();
475 node_alloc_.deallocate(p_delete, 1);
476 }
477
478 void link() {
479 first.next.store(&last, std::memory_order_relaxed);
480 last.prior.store(&first, std::memory_order_relaxed);
481 }
482
486 friend class Iterator;
487};
488
489} // namespace tiny_dlna
Bidirectional iterator for ListLockFree.
Definition: ListLockFree.h:44
Iterator operator++()
Definition: ListLockFree.h:51
T * operator->()
Definition: ListLockFree.h:114
Node * get_node()
Definition: ListLockFree.h:120
bool operator!=(const Iterator &it) const
Definition: ListLockFree.h:104
Iterator(Node *node, ListLockFree *owner_ptr=nullptr)
Definition: ListLockFree.h:47
bool operator==(const Iterator &it) const
Definition: ListLockFree.h:100
ListLockFree * owner
Definition: ListLockFree.h:129
Iterator operator--(int)
Definition: ListLockFree.h:85
Iterator operator+(int offset)
Definition: ListLockFree.h:91
T & operator*()
Definition: ListLockFree.h:109
void set_owner(ListLockFree *owner_ptr)
Definition: ListLockFree.h:124
Iterator operator++(int)
Definition: ListLockFree.h:65
Node * node
Definition: ListLockFree.h:127
bool is_eof
Definition: ListLockFree.h:128
Iterator getIteratorAtOffset(int offset)
Definition: ListLockFree.h:131
Iterator operator--()
Definition: ListLockFree.h:71
Iterator operator-(int offset)
Definition: ListLockFree.h:95
Lock-free double linked list using atomic operations.
Definition: ListLockFree.h:28
Node first
Definition: ListLockFree.h:461
T & operator[](int index)
Access element at index (O(n)). Not thread-safe under concurrency. Make sure that no other thread mod...
Definition: ListLockFree.h:431
bool pop_front(T &data)
Definition: ListLockFree.h:305
~ListLockFree()
Definition: ListLockFree.h:184
ListLockFree(const ListLockFree &ref)
Copy constructor (not thread-safe for the source list)
Definition: ListLockFree.h:165
Iterator end()
Definition: ListLockFree.h:395
bool clear()
Definition: ListLockFree.h:417
ListLockFree(const T(&a)[N], const Alloc &alloc=Alloc())
Constructor using array.
Definition: ListLockFree.h:177
Iterator rbegin()
Definition: ListLockFree.h:401
typename std::allocator_traits< Alloc >::template rebind_alloc< Node > NodeAlloc
Definition: ListLockFree.h:157
size_t size()
Definition: ListLockFree.h:413
ListLockFree(const Alloc &alloc=Alloc())
Default constructor.
Definition: ListLockFree.h:160
std::atomic< size_t > record_count
Definition: ListLockFree.h:463
NodeAlloc node_alloc_
Definition: ListLockFree.h:464
bool empty()
Definition: ListLockFree.h:415
T & front()
Provides the first element.
Definition: ListLockFree.h:454
bool pop_back()
Definition: ListLockFree.h:300
void link()
Definition: ListLockFree.h:478
bool push_front(const T &data)
Definition: ListLockFree.h:227
bool swap(ListLockFree< T > &ref)
Swap contents with another list. Not implemented (returns false).
Definition: ListLockFree.h:190
Iterator begin()
Definition: ListLockFree.h:388
bool pop_front()
Definition: ListLockFree.h:295
T & back()
Provides the last element.
Definition: ListLockFree.h:448
Node last
Definition: ListLockFree.h:462
Node * createNode()
Definition: ListLockFree.h:466
bool erase(Iterator it)
Definition: ListLockFree.h:357
Iterator rend()
Definition: ListLockFree.h:408
bool insert(Iterator it, const T &data)
Definition: ListLockFree.h:259
bool pop_back(T &data)
Definition: ListLockFree.h:331
void deleteNode(Node *p_delete)
Definition: ListLockFree.h:472
bool push_back(const T &data)
Definition: ListLockFree.h:195
Definition: Allocator.h:13
Definition: ListLockFree.h:30
std::atomic< Node * > prior
Definition: ListLockFree.h:32
T data
Definition: ListLockFree.h:33
Node(const T &value)
Definition: ListLockFree.h:36
std::atomic< Node * > next
Definition: ListLockFree.h:31