1
1
/*
2
2
MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate
3
3
class
4
- Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
4
+ Copyright (c) 2019-2020 Dirk O. Kaar. All rights reserved.
5
5
6
6
This library is free software; you can redistribute it and/or
7
7
modify it under the terms of the GNU Lesser General Public
@@ -52,7 +52,7 @@ namespace
52
52
{
53
53
static R execute (Delegate& del, P... args)
54
54
{
55
- return del (std::forward<P...>(args...)) ? !ISQUEUE : ISQUEUE ;
55
+ return del (std::forward<P...>(args...));
56
56
}
57
57
};
58
58
@@ -62,7 +62,7 @@ namespace
62
62
static bool execute (Delegate& del, P... args)
63
63
{
64
64
del (std::forward<P...>(args...));
65
- return !ISQUEUE ;
65
+ return true ;
66
66
}
67
67
};
68
68
@@ -71,7 +71,7 @@ namespace
71
71
{
72
72
static R execute (Delegate& del)
73
73
{
74
- return del () ? !ISQUEUE : ISQUEUE ;
74
+ return del ();
75
75
}
76
76
};
77
77
@@ -81,7 +81,7 @@ namespace
81
81
static bool execute (Delegate& del)
82
82
{
83
83
del ();
84
- return !ISQUEUE ;
84
+ return true ;
85
85
}
86
86
};
87
87
@@ -92,7 +92,7 @@ namespace delegate
92
92
namespace detail
93
93
{
94
94
95
- template < typename Delegate, typename R = void , bool ISQUEUE = false , size_t QUEUE_CAPACITY = 32 , typename ... P>
95
+ template < typename Delegate, typename R, bool ISQUEUE = false , size_t QUEUE_CAPACITY = 32 , typename ... P>
96
96
class MultiDelegatePImpl
97
97
{
98
98
public:
@@ -216,6 +216,42 @@ namespace delegate
216
216
unused = node;
217
217
}
218
218
219
+ void traverse (Node_t*& prev, Node_t*& current, bool remove = true )
220
+ {
221
+ if (remove && ISQUEUE)
222
+ {
223
+ // remove callback from stack
224
+ #ifdef ARDUINO
225
+ InterruptLock lockAllInterruptsInThisScope;
226
+ #else
227
+ std::lock_guard<std::mutex> lock (mutex_unused);
228
+ #endif
229
+
230
+ auto to_recycle = current;
231
+
232
+ // removing last
233
+ if (last == current)
234
+ last = prev;
235
+
236
+ current = current->mNext ;
237
+ if (prev)
238
+ {
239
+ prev->mNext = current;
240
+ }
241
+ else
242
+ {
243
+ first = current;
244
+ }
245
+
246
+ recycle_node_unsafe (to_recycle);
247
+ }
248
+ else
249
+ {
250
+ prev = current;
251
+ current = current->mNext ;
252
+ }
253
+ }
254
+
219
255
#ifndef ARDUINO
220
256
std::mutex mutex_unused;
221
257
#endif
@@ -277,7 +313,7 @@ namespace delegate
277
313
278
314
auto to_recycle = current;
279
315
280
- // removing rLast
316
+ // removing last
281
317
if (last == current)
282
318
last = prev;
283
319
@@ -306,72 +342,45 @@ namespace delegate
306
342
return false ;
307
343
}
308
344
309
- void operator ()(P... args)
345
+ operator bool () const
346
+ {
347
+ return first;
348
+ }
349
+
350
+ R operator ()(P... args)
310
351
{
311
352
auto current = first;
312
353
if (!current)
313
- return ;
354
+ return {} ;
314
355
315
356
static std::atomic<bool > fence (false );
316
357
// prevent recursive calls
317
358
#if defined(ARDUINO) && !defined(ESP32)
318
- if (fence.load ()) return ;
359
+ if (fence.load ()) return {} ;
319
360
fence.store (true );
320
361
#else
321
- if (fence.exchange (true )) return ;
362
+ if (fence.exchange (true )) return {} ;
322
363
#endif
323
364
324
365
Node_t* prev = nullptr ;
325
366
// prevent execution of new callbacks during this run
326
367
auto stop = last;
327
368
369
+ R result;
328
370
bool done;
329
371
do
330
372
{
331
373
done = current == stop;
332
- if (!CallP<Delegate, R, ISQUEUE, P...>::execute (current->mDelegate , args...))
333
- {
334
- // remove callback from stack
335
- #ifdef ARDUINO
336
- InterruptLock lockAllInterruptsInThisScope;
337
- #else
338
- std::lock_guard<std::mutex> lock (mutex_unused);
339
- #endif
340
-
341
- auto to_recycle = current;
342
-
343
- // removing rLast
344
- if (last == current)
345
- last = prev;
346
-
347
- current = current->mNext ;
348
- if (prev)
349
- {
350
- prev->mNext = current;
351
- }
352
- else
353
- {
354
- first = current;
355
- }
356
-
357
- if (ISQUEUE)
358
- recycle_node_unsafe (to_recycle);
359
- else
360
- delete to_recycle;
361
- }
362
- else
363
- {
364
- prev = current;
365
- current = current->mNext ;
366
- }
367
-
374
+ result = CallP<Delegate, R, ISQUEUE, P...>::execute (current->mDelegate , args...);
375
+ traverse (prev, current, result);
368
376
#if defined(ESP8266) || defined(ESP32)
369
377
// running callbacks might last too long for watchdog etc.
370
378
optimistic_yield (10000 );
371
379
#endif
372
380
} while (current && !done);
373
381
374
382
fence.store (false );
383
+ return result;
375
384
}
376
385
};
377
386
@@ -392,72 +401,40 @@ namespace delegate
392
401
public:
393
402
using MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegatePImpl;
394
403
395
- void operator ()()
404
+ R operator ()()
396
405
{
397
406
auto current = first;
398
407
if (!current)
399
- return ;
408
+ return {} ;
400
409
401
410
static std::atomic<bool > fence (false );
402
411
// prevent recursive calls
403
412
#if defined(ARDUINO) && !defined(ESP32)
404
- if (fence.load ()) return ;
413
+ if (fence.load ()) return {} ;
405
414
fence.store (true );
406
415
#else
407
- if (fence.exchange (true )) return ;
416
+ if (fence.exchange (true )) return {} ;
408
417
#endif
409
418
410
419
Node_t* prev = nullptr ;
411
420
// prevent execution of new callbacks during this run
412
421
auto stop = last;
413
422
423
+ R result;
414
424
bool done;
415
425
do
416
426
{
417
427
done = current == stop;
418
- if (!Call<Delegate, R, ISQUEUE>::execute (current->mDelegate ))
419
- {
420
- // remove callback from stack
421
- #ifdef ARDUINO
422
- InterruptLock lockAllInterruptsInThisScope;
423
- #else
424
- std::lock_guard<std::mutex> lock (mutex_unused);
425
- #endif
426
-
427
- auto to_recycle = current;
428
-
429
- // removing rLast
430
- if (last == current)
431
- last = prev;
432
-
433
- current = current->mNext ;
434
- if (prev)
435
- {
436
- prev->mNext = current;
437
- }
438
- else
439
- {
440
- first = current;
441
- }
442
-
443
- if (ISQUEUE)
444
- recycle_node_unsafe (to_recycle);
445
- else
446
- delete to_recycle;
447
- }
448
- else
449
- {
450
- prev = current;
451
- current = current->mNext ;
452
- }
453
-
428
+ result = Call<Delegate, R, ISQUEUE>::execute (current->mDelegate );
429
+ this ->traverse (prev, current, result);
454
430
#if defined(ESP8266) || defined(ESP32)
455
431
// running callbacks might last too long for watchdog etc.
456
432
optimistic_yield (10000 );
457
433
#endif
458
434
} while (current && !done);
459
435
460
436
fence.store (false );
437
+ return result;
461
438
}
462
439
};
463
440
@@ -477,7 +454,96 @@ namespace delegate
477
454
using MultiDelegateImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
478
455
};
479
456
457
+ template < typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename ... P>
458
+ class MultiDelegate <Delegate, void (P...), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegatePImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY, P...>
459
+ {
460
+ public:
461
+ using MultiDelegatePImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY, P...>::MultiDelegatePImpl;
462
+ using typename MultiDelegatePImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY, P...>::Node_t;
463
+ using MultiDelegatePImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY, P...>::first;
464
+ using MultiDelegatePImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY, P...>::last;
465
+
466
+ void operator ()(P... args)
467
+ {
468
+ auto current = first;
469
+ if (!current)
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
+ Node_t* prev = nullptr ;
482
+ // prevent execution of new callbacks during this run
483
+ auto stop = last;
484
+
485
+ bool done;
486
+ do
487
+ {
488
+ done = current == stop;
489
+ CallP<Delegate, void , ISQUEUE, P...>::execute (current->mDelegate , args...);
490
+ this ->traverse (prev, current);
491
+ #if defined(ESP8266) || defined(ESP32)
492
+ // running callbacks might last too long for watchdog etc.
493
+ optimistic_yield (10000 );
494
+ #endif
495
+ } while (current && !done);
496
+
497
+ fence.store (false );
498
+ }
499
+ };
500
+
501
+ template < typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY>
502
+ class MultiDelegate <Delegate, void (), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegateImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY>
503
+ {
504
+ public:
505
+ using MultiDelegateImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
506
+ using typename MultiDelegateImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY>::Node_t;
507
+ using MultiDelegateImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY>::first;
508
+ using MultiDelegateImpl<Delegate, void , ISQUEUE, QUEUE_CAPACITY>::last;
509
+
510
+ void operator ()()
511
+ {
512
+ auto current = first;
513
+ if (!current)
514
+ return ;
515
+
516
+ static std::atomic<bool > fence (false );
517
+ // prevent recursive calls
518
+ #if defined(ARDUINO) && !defined(ESP32)
519
+ if (fence.load ()) return ;
520
+ fence.store (true );
521
+ #else
522
+ if (fence.exchange (true )) return ;
523
+ #endif
524
+
525
+ Node_t* prev = nullptr ;
526
+ // prevent execution of new callbacks during this run
527
+ auto stop = last;
528
+
529
+ bool done;
530
+ do
531
+ {
532
+ done = current == stop;
533
+ Call<Delegate, void , ISQUEUE>::execute (current->mDelegate );
534
+ this ->traverse (prev, current);
535
+ #if defined(ESP8266) || defined(ESP32)
536
+ // running callbacks might last too long for watchdog etc.
537
+ optimistic_yield (10000 );
538
+ #endif
539
+ } while (current && !done);
540
+
541
+ fence.store (false );
542
+ }
543
+ };
544
+
480
545
}
546
+
481
547
}
482
548
483
549
/* *
@@ -492,10 +558,10 @@ It is designed to be used with Delegate, the efficient runtime wrapper for C fun
492
558
If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue
493
559
removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until
494
560
explicitly removed.
495
- If the result type of the function call operator of Delegate is non-void, the type-conversion to bool
496
- of that result determines if the item is immediately removed or kept after each call: a Multidelegate
497
- queue removes an item only if true is returned, but a Multidelegate event multiplexer removes event
498
- handlers that return false .
561
+ If the result type of the function call operator of Delegate is non-void, in a MultiDelegate queue
562
+ the type-conversion to bool of that result determines if the item is immediately removed or kept
563
+ after each call: if true is returned, the item is removed. A Multidelegate event multiplexer keeps event
564
+ handlers until they are explicitly removed .
499
565
@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically
500
566
allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate
501
567
instance during its own lifetime for efficiency.
0 commit comments