Soldered 125kHz RFID board Arduino library 1.0.0
Library for Soldered 125kHz RFID board.
Loading...
Searching...
No Matches
MultiDelegate.h
Go to the documentation of this file.
1/*
2MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate
3class
4Copyright (c) 2019-2020 Dirk O. Kaar. All rights reserved.
5
6This library is free software; you can redistribute it and/or
7modify it under the terms of the GNU Lesser General Public
8License as published by the Free Software Foundation; either
9version 2.1 of the License, or (at your option) any later version.
10
11This library is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14Lesser General Public License for more details.
15
16You should have received a copy of the GNU Lesser General Public
17License along with this library; if not, write to the Free Software
18Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#ifndef __MULTIDELEGATE_H
22#define __MULTIDELEGATE_H
23
24#include <iterator>
25#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
26#include <atomic>
27#else
29#endif
30
31#if defined(ESP8266)
32#include <interrupts.h>
33using esp8266::InterruptLock;
34#elif defined(ARDUINO)
36public:
38 noInterrupts();
39 }
41 interrupts();
42 }
43};
44#else
45#include <mutex>
46#endif
47
48namespace
49{
50
51 template< typename Delegate, typename R, bool ISQUEUE = false, typename... P>
52 struct CallP
53 {
54 static R execute(Delegate& del, P... args)
55 {
56 return del(std::forward<P...>(args...));
57 }
58 };
59
60 template< typename Delegate, bool ISQUEUE, typename... P>
61 struct CallP<Delegate, void, ISQUEUE, P...>
62 {
63 static bool execute(Delegate& del, P... args)
64 {
65 del(std::forward<P...>(args...));
66 return true;
67 }
68 };
69
70 template< typename Delegate, typename R, bool ISQUEUE = false>
71 struct Call
72 {
73 static R execute(Delegate& del)
74 {
75 return del();
76 }
77 };
78
79 template< typename Delegate, bool ISQUEUE>
80 struct Call<Delegate, void, ISQUEUE>
81 {
82 static bool execute(Delegate& del)
83 {
84 del();
85 return true;
86 }
87 };
88
89}
90
91namespace delegate
92{
93 namespace detail
94 {
95
96 template< typename Delegate, typename R, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P>
98 {
99 public:
102 {
103 *this = nullptr;
104 }
105
108
110 {
111 first = md.first;
112 last = md.last;
113 unused = md.unused;
114 nodeCount = md.nodeCount;
115 md.first = nullptr;
116 md.last = nullptr;
117 md.unused = nullptr;
118 md.nodeCount = 0;
119 }
120
122 {
123 add(del);
124 }
125
127 {
128 add(std::move(del));
129 }
130
132 {
133 first = md.first;
134 last = md.last;
135 unused = md.unused;
136 nodeCount = md.nodeCount;
137 md.first = nullptr;
138 md.last = nullptr;
139 md.unused = nullptr;
140 md.nodeCount = 0;
141 return *this;
142 }
143
145 {
146 if (last)
147 last->mNext = unused;
148 if (first)
149 unused = first;
150 while (unused)
151 {
152 auto to_delete = unused;
154 delete(to_delete);
155 }
156 return *this;
157 }
158
160 {
161 add(del);
162 return *this;
163 }
164
166 {
167 add(std::move(del));
168 return *this;
169 }
170
171 protected:
172 struct Node_t
173 {
175 {
176 mDelegate = nullptr; // special overload in Delegate
177 }
178 Node_t* mNext = nullptr;
180 };
181
182 Node_t* first = nullptr;
183 Node_t* last = nullptr;
184 Node_t* unused = nullptr;
185 size_t nodeCount = 0;
186
187 // Returns a pointer to an unused Node_t,
188 // or if none are available allocates a new one,
189 // or nullptr if limit is reached
191 {
192 Node_t* result = nullptr;
193 // try to get an item from unused items list
194 if (unused)
195 {
196 result = unused;
198 }
199 // if no unused items, and count not too high, allocate a new one
200 else if (nodeCount < QUEUE_CAPACITY)
201 {
202#if defined(ESP8266) || defined(ESP32)
203 result = new (std::nothrow) Node_t;
204#else
205 result = new Node_t;
206#endif
207 if (result)
208 ++nodeCount;
209 }
210 return result;
211 }
212
214 {
215 node->mDelegate = nullptr; // special overload in Delegate
216 node->mNext = unused;
217 unused = node;
218 }
219
220#ifndef ARDUINO
221 std::mutex mutex_unused;
222#endif
223 public:
224 class iterator : public std::iterator<std::forward_iterator_tag, Delegate>
225 {
226 public:
227 Node_t* current = nullptr;
228 Node_t* prev = nullptr;
229 const Node_t* stop = nullptr;
230
232 iterator() = default;
233 iterator(const iterator&) = default;
234 iterator& operator=(const iterator&) = default;
236 operator bool() const
237 {
238 return current && stop;
239 }
240 bool operator==(const iterator& rhs) const
241 {
242 return current == rhs.current;
243 }
244 bool operator!=(const iterator& rhs) const
245 {
246 return !operator==(rhs);
247 }
249 {
250 return current->mDelegate;
251 }
253 {
254 return &current->mDelegate;
255 }
256 iterator& operator++() // prefix
257 {
258 if (current && stop != current)
259 {
260 prev = current;
262 }
263 else
264 current = nullptr; // end
265 return *this;
266 }
267 iterator& operator++(int) // postfix
268 {
269 iterator tmp(*this);
270 operator++();
271 return tmp;
272 }
273 };
274
276 {
277 return iterator(*this);
278 }
279 iterator end() const
280 {
281 return iterator();
282 }
283
284 const Delegate* IRAM_ATTR add(const Delegate& del)
285 {
286 return add(Delegate(del));
287 }
288
289 const Delegate* IRAM_ATTR add(Delegate&& del)
290 {
291 if (!del)
292 return nullptr;
293
294#ifdef ARDUINO
295 InterruptLock lockAllInterruptsInThisScope;
296#else
297 std::lock_guard<std::mutex> lock(mutex_unused);
298#endif
299
300 Node_t* item = ISQUEUE ? get_node_unsafe() :
301#if defined(ESP8266) || defined(ESP32)
302 new (std::nothrow) Node_t;
303#else
304 new Node_t;
305#endif
306 if (!item)
307 return nullptr;
308
309 item->mDelegate = std::move(del);
310 item->mNext = nullptr;
311
312 if (last)
313 last->mNext = item;
314 else
315 first = item;
316 last = item;
317
318 return &item->mDelegate;
319 }
320
322 {
323 if (!it)
324 return end();
325#ifdef ARDUINO
326 InterruptLock lockAllInterruptsInThisScope;
327#else
328 std::lock_guard<std::mutex> lock(mutex_unused);
329#endif
330 auto to_recycle = it.current;
331
332 if (last == it.current)
333 last = it.prev;
334 it.current = it.current->mNext;
335 if (it.prev)
336 {
337 it.prev->mNext = it.current;
338 }
339 else
340 {
341 first = it.current;
342 }
343 if (ISQUEUE)
344 recycle_node_unsafe(to_recycle);
345 else
346 delete to_recycle;
347 return it;
348 }
349
350 bool erase(const Delegate* const del)
351 {
352 auto it = begin();
353 while (it)
354 {
355 if (del == &(*it))
356 {
357 erase(it);
358 return true;
359 }
360 ++it;
361 }
362 return false;
363 }
364
365 operator bool() const
366 {
367 return first;
368 }
369
370 R operator()(P... args)
371 {
372 auto it = begin();
373 if (!it)
374 return {};
375
376 static std::atomic<bool> fence(false);
377 // prevent recursive calls
378#if defined(ARDUINO) && !defined(ESP32)
379 if (fence.load()) return {};
380 fence.store(true);
381#else
382 if (fence.exchange(true)) return {};
383#endif
384
385 R result;
386 do
387 {
388 result = CallP<Delegate, R, ISQUEUE, P...>::execute(*it, args...);
389 if (result && ISQUEUE)
390 it = erase(it);
391 else
392 ++it;
393#if defined(ESP8266) || defined(ESP32)
394 // running callbacks might last too long for watchdog etc.
395 optimistic_yield(10000);
396#endif
397 } while (it);
398
399 fence.store(false);
400 return result;
401 }
402 };
403
404 template< typename Delegate, typename R = void, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32>
405 class MultiDelegateImpl : public MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>
406 {
407 public:
408 using MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegatePImpl;
409
411 {
412 auto it = this->begin();
413 if (!it)
414 return {};
415
416 static std::atomic<bool> fence(false);
417 // prevent recursive calls
418#if defined(ARDUINO) && !defined(ESP32)
419 if (fence.load()) return {};
420 fence.store(true);
421#else
422 if (fence.exchange(true)) return {};
423#endif
424
425 R result;
426 do
427 {
428 result = Call<Delegate, R, ISQUEUE>::execute(*it);
429 if (result && ISQUEUE)
430 it = this->erase(it);
431 else
432 ++it;
433#if defined(ESP8266) || defined(ESP32)
434 // running callbacks might last too long for watchdog etc.
435 optimistic_yield(10000);
436#endif
437 } while (it);
438
439 fence.store(false);
440 return result;
441 }
442 };
443
444 template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> class MultiDelegate;
445
446 template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P>
447 class MultiDelegate<Delegate, R(P...), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY, P...>
448 {
449 public:
450 using MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY, P...>::MultiDelegatePImpl;
451 };
452
453 template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY>
454 class MultiDelegate<Delegate, R(), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegateImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>
455 {
456 public:
457 using MultiDelegateImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
458 };
459
460 template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P>
461 class MultiDelegate<Delegate, void(P...), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>
462 {
463 public:
464 using MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>::MultiDelegatePImpl;
465
466 void operator()(P... args)
467 {
468 auto it = this->begin();
469 if (!it)
470 return;
471
472 static std::atomic<bool> fence(false);
473 // prevent recursive calls
474#if defined(ARDUINO) && !defined(ESP32)
475 if (fence.load()) return;
476 fence.store(true);
477#else
478 if (fence.exchange(true)) return;
479#endif
480
481 do
482 {
483 CallP<Delegate, void, ISQUEUE, P...>::execute(*it, args...);
484 if (ISQUEUE)
485 it = this->erase(it);
486 else
487 ++it;
488#if defined(ESP8266) || defined(ESP32)
489 // running callbacks might last too long for watchdog etc.
490 optimistic_yield(10000);
491#endif
492 } while (it);
493
494 fence.store(false);
495 }
496 };
497
498 template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY>
499 class MultiDelegate<Delegate, void(), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>
500 {
501 public:
502 using MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
503
505 {
506 auto it = this->begin();
507 if (!it)
508 return;
509
510 static std::atomic<bool> fence(false);
511 // prevent recursive calls
512#if defined(ARDUINO) && !defined(ESP32)
513 if (fence.load()) return;
514 fence.store(true);
515#else
516 if (fence.exchange(true)) return;
517#endif
518
519 do
520 {
521 Call<Delegate, void, ISQUEUE>::execute(*it);
522 if (ISQUEUE)
523 it = this->erase(it);
524 else
525 ++it;
526#if defined(ESP8266) || defined(ESP32)
527 // running callbacks might last too long for watchdog etc.
528 optimistic_yield(10000);
529#endif
530 } while (it);
531
532 fence.store(false);
533 }
534 };
535
536 }
537
538}
539
560template< typename Delegate, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32>
561class MultiDelegate : public delegate::detail::MultiDelegate<Delegate, typename Delegate::target_type, ISQUEUE, QUEUE_CAPACITY>
562{
563public:
564 using delegate::detail::MultiDelegate<Delegate, typename Delegate::target_type, ISQUEUE, QUEUE_CAPACITY>::MultiDelegate;
565};
566
567#endif // __MULTIDELEGATE_H
Definition Delegate.h:2049
Definition MultiDelegate.h:35
InterruptLock()
Definition MultiDelegate.h:37
~InterruptLock()
Definition MultiDelegate.h:40
Definition MultiDelegate.h:562
Definition Delegate.h:1657
Definition MultiDelegate.h:444
Definition MultiDelegate.h:406
R operator()()
Definition MultiDelegate.h:410
iterator(MultiDelegatePImpl &md)
Definition MultiDelegate.h:231
Delegate & operator*() const
Definition MultiDelegate.h:248
Node_t * prev
Definition MultiDelegate.h:228
Node_t * current
Definition MultiDelegate.h:227
Delegate * operator->() const
Definition MultiDelegate.h:252
iterator & operator++()
Definition MultiDelegate.h:256
iterator & operator=(const iterator &)=default
const Node_t * stop
Definition MultiDelegate.h:229
bool operator!=(const iterator &rhs) const
Definition MultiDelegate.h:244
iterator & operator++(int)
Definition MultiDelegate.h:267
bool operator==(const iterator &rhs) const
Definition MultiDelegate.h:240
iterator & operator=(iterator &&)=default
Definition MultiDelegate.h:98
Node_t * unused
Definition MultiDelegate.h:184
iterator end() const
Definition MultiDelegate.h:279
MultiDelegatePImpl & operator+=(Delegate &&del)
Definition MultiDelegate.h:165
MultiDelegatePImpl & operator+=(const Delegate &del)
Definition MultiDelegate.h:159
iterator begin()
Definition MultiDelegate.h:275
MultiDelegatePImpl(Delegate &&del)
Definition MultiDelegate.h:126
const Delegate *IRAM_ATTR add(Delegate &&del)
Definition MultiDelegate.h:289
Node_t *IRAM_ATTR get_node_unsafe()
Definition MultiDelegate.h:190
MultiDelegatePImpl(const Delegate &del)
Definition MultiDelegate.h:121
Node_t * last
Definition MultiDelegate.h:183
MultiDelegatePImpl(MultiDelegatePImpl &&md)
Definition MultiDelegate.h:109
size_t nodeCount
Definition MultiDelegate.h:185
~MultiDelegatePImpl()
Definition MultiDelegate.h:101
void recycle_node_unsafe(Node_t *node)
Definition MultiDelegate.h:213
MultiDelegatePImpl & operator=(std::nullptr_t)
Definition MultiDelegate.h:144
MultiDelegatePImpl & operator=(const MultiDelegatePImpl &)=delete
bool erase(const Delegate *const del)
Definition MultiDelegate.h:350
MultiDelegatePImpl(const MultiDelegatePImpl &)=delete
Node_t * first
Definition MultiDelegate.h:182
MultiDelegatePImpl & operator=(MultiDelegatePImpl &&md)
Definition MultiDelegate.h:131
iterator erase(iterator it)
Definition MultiDelegate.h:321
std::mutex mutex_unused
Definition MultiDelegate.h:221
R operator()(P... args)
Definition MultiDelegate.h:370
const Delegate *IRAM_ATTR add(const Delegate &del)
Definition MultiDelegate.h:284
Definition ghostl.h:40
T load(std::memory_order=std::memory_order_seq_cst) const volatile noexcept
Definition ghostl.h:47
void store(T desired, std::memory_order=std::memory_order_seq_cst) volatile noexcept
Definition ghostl.h:46
Definition Delegate.h:51
T && move(T &t) noexcept
Definition ghostl.h:50
decltype(nullptr) nullptr_t
Definition ghostl.h:80
T && forward(typename identity< T >::type &t) noexcept
Definition ghostl.h:88
Definition MultiDelegate.h:173
Node_t * mNext
Definition MultiDelegate.h:178
Delegate mDelegate
Definition MultiDelegate.h:179
~Node_t()
Definition MultiDelegate.h:174