Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
0f00454
Delegate class template.
dok-net Nov 28, 2019
4b6020c
Solves problems with lambdas and std::bind
dok-net Nov 28, 2019
4363ce9
MSVC compiler picked up missing std::forward
dok-net Nov 28, 2019
d133e30
Circular include issue fixed
dok-net Nov 29, 2019
c56fa87
Use function declaration syntax in template type specification
dok-net Nov 29, 2019
48ed02b
Use terse function notation - match std::function, the optional 1st a…
dok-net Nov 29, 2019
bc8a0e6
Performance improvement for Delegate specialization without additiona…
dok-net Dec 4, 2019
05e1811
Updated Delegate and new MultiDelegate template.
dok-net Dec 7, 2019
a5ee065
MultiDelegate template argument to switch queue/event behavior.
dok-net Dec 8, 2019
79c3816
Fix deletion of argument value caused by forwarding to Delegate.
dok-net Dec 8, 2019
edbeeab
Readied MultiDelegate for ESP32 ISR safeness.
dok-net Dec 8, 2019
cda54ff
Completed port of Delegate and MultiDelegate to Non-Arduino platforms
dok-net Dec 9, 2019
c6fb8bc
Add missing LGPL license header.
dok-net Dec 9, 2019
da5f0d4
Directly derive from std::function for DelegateImpl<void, R>, too.
dok-net Dec 10, 2019
15bbc1c
Fixed queue and event multiplexer modes (ISQUEUE template argument) f…
dok-net Dec 11, 2019
e1c3d22
Signed-unsigned mismatch caught by ESP2866 Arduino CI.
dok-net Dec 12, 2019
534d016
Performance and ease of use, more comprehensible ctor and operator=: …
dok-net Dec 13, 2019
ee86f2e
Interface full lockdown: mark implementation details private instead …
dok-net Dec 14, 2019
0db6c95
For use of Delegate as callback in functions that take C-fun ptr and …
dok-net Dec 15, 2019
c5d738c
Optimize casting to fun ptr and argument for "attachInterruptArg"-lik…
dok-net Dec 16, 2019
18f497e
Bug fix: must reinterpret_cast for R() as R(void*) cast (extra argume…
dok-net Dec 16, 2019
e6e60e7
using operator= is required to suppress auto generation.
dok-net Dec 17, 2019
d855cbd
Delegate to fptr helpers must be in IRAM
dok-net Dec 17, 2019
6b3e87b
Delegate refactoring for same ordering of member functions between sp…
dok-net Dec 20, 2019
af76e5f
Bug fix Delegate, custom type converter.
dok-net Dec 22, 2019
2b4a08d
Use explicit initialization in ctor for kind member.
dok-net Dec 23, 2019
c19456c
Fix ctor overloads competing with ctor templates.
dok-net Dec 23, 2019
6bac522
Assignment operator template caused ambiguity error in MSVC build - d…
dok-net Dec 24, 2019
ae7b4f6
Delegate template programming for > 3 platforms is interesting.
dok-net Jan 18, 2020
c25965d
unsigned/int instead of (u)int32_t gives lower memory footprint on 8b…
dok-net Jan 17, 2020
188c3f0
Refactor uses of unsigned for buffer sizes and item counts to size_t.
dok-net Jan 19, 2020
07e52a1
It was found that internal linkage suppresses the effect of the IRAM_…
dok-net Jan 21, 2020
e78885c
std::bind is IRAM cache save, lambda is not.
dok-net Jan 22, 2020
e3888ca
Enhancements to MultiDelegate: event multiplexer mode no longer has a…
dok-net Feb 12, 2020
680abe1
Added forward iterator and begin() and end() functions to MultiDelegate.
dok-net Feb 12, 2020
1f193ab
Change to make MultiDelegate invoke and erase work off iterators.
dok-net Feb 13, 2020
57a87ca
Constness fix for iterator dereference
dok-net Feb 13, 2020
960c8f5
constness on return type warning fix.
dok-net Feb 13, 2020
3cd6b91
Conform to C++ 17 and later.
dok-net Feb 25, 2020
64b06ec
Resolve g++ 9's c++17 deprecation warnings.
dok-net Feb 25, 2020
3b7f2c7
gcc 9.2 is very strict about template inheritance of ctor and assignm…
dok-net Feb 28, 2020
6e17563
Fix both ambiguous operator= and infinite ctor recursion.
dok-net Mar 1, 2020
df02bac
don't cast to rvalue ref if also move'ing
dok-net Mar 23, 2020
81ceebd
Add assignment operators.
dok-net Jan 4, 2021
4fa0dbb
Example as performance test for Delegate vs. std::function
dok-net Jan 27, 2021
6b6dbee
Revert "Add assignment operators."
dok-net Jan 27, 2021
099fc86
Re-apply std::forwards and minor identifier rename.
dok-net Jan 27, 2021
0fd063c
More hints for proper use of Delegate to optimize performance.
dok-net Jan 27, 2021
66d1704
Apply astyle
dok-net Jan 27, 2021
6aa1102
Added preprocessor define NODELEGATE. Use it for side-by-side compari…
dok-net Jan 28, 2021
9644606
Field padding improved
dok-net Jan 30, 2021
8e75ef9
Remove the now-deprecated ICACHE_RAM_ATTR.
dok-net Mar 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Enhancements to MultiDelegate: event multiplexer mode no longer has a…
…uto-remove. Invoke returns result of last Delegate call.

Caveat: breaks use of event multiplexer with auto-remove; WIP: add iterators.
  • Loading branch information
dok-net committed Jun 28, 2021
commit e3888ca345b5db64be410cc770e284a1b7ac782e
248 changes: 157 additions & 91 deletions cores/esp8266/MultiDelegate.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate
class
Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
Copyright (c) 2019-2020 Dirk O. Kaar. All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -52,7 +52,7 @@ namespace
{
static R execute(Delegate& del, P... args)
{
return del(std::forward<P...>(args...)) ? !ISQUEUE : ISQUEUE;
return del(std::forward<P...>(args...));
}
};

Expand All @@ -62,7 +62,7 @@ namespace
static bool execute(Delegate& del, P... args)
{
del(std::forward<P...>(args...));
return !ISQUEUE;
return true;
}
};

Expand All @@ -71,7 +71,7 @@ namespace
{
static R execute(Delegate& del)
{
return del() ? !ISQUEUE : ISQUEUE;
return del();
}
};

Expand All @@ -81,7 +81,7 @@ namespace
static bool execute(Delegate& del)
{
del();
return !ISQUEUE;
return true;
}
};

Expand All @@ -92,7 +92,7 @@ namespace delegate
namespace detail
{

template< typename Delegate, typename R = void, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P>
template< typename Delegate, typename R, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P>
class MultiDelegatePImpl
{
public:
Expand Down Expand Up @@ -216,6 +216,42 @@ namespace delegate
unused = node;
}

void traverse(Node_t*& prev, Node_t*& current, bool remove = true)
{
if (remove && ISQUEUE)
{
// remove callback from stack
#ifdef ARDUINO
InterruptLock lockAllInterruptsInThisScope;
#else
std::lock_guard<std::mutex> lock(mutex_unused);
#endif

auto to_recycle = current;

// removing last
if (last == current)
last = prev;

current = current->mNext;
if (prev)
{
prev->mNext = current;
}
else
{
first = current;
}

recycle_node_unsafe(to_recycle);
}
else
{
prev = current;
current = current->mNext;
}
}

#ifndef ARDUINO
std::mutex mutex_unused;
#endif
Expand Down Expand Up @@ -277,7 +313,7 @@ namespace delegate

auto to_recycle = current;

// removing rLast
// removing last
if (last == current)
last = prev;

Expand Down Expand Up @@ -306,72 +342,45 @@ namespace delegate
return false;
}

void operator()(P... args)
operator bool() const
{
return first;
}

R operator()(P... args)
{
auto current = first;
if (!current)
return;
return {};

static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return;
if (fence.load()) return {};
fence.store(true);
#else
if (fence.exchange(true)) return;
if (fence.exchange(true)) return {};
#endif

Node_t* prev = nullptr;
// prevent execution of new callbacks during this run
auto stop = last;

R result;
bool done;
do
{
done = current == stop;
if (!CallP<Delegate, R, ISQUEUE, P...>::execute(current->mDelegate, args...))
{
// remove callback from stack
#ifdef ARDUINO
InterruptLock lockAllInterruptsInThisScope;
#else
std::lock_guard<std::mutex> lock(mutex_unused);
#endif

auto to_recycle = current;

// removing rLast
if (last == current)
last = prev;

current = current->mNext;
if (prev)
{
prev->mNext = current;
}
else
{
first = current;
}

if (ISQUEUE)
recycle_node_unsafe(to_recycle);
else
delete to_recycle;
}
else
{
prev = current;
current = current->mNext;
}

result = CallP<Delegate, R, ISQUEUE, P...>::execute(current->mDelegate, args...);
traverse(prev, current, result);
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (current && !done);

fence.store(false);
return result;
}
};

Expand All @@ -392,72 +401,40 @@ namespace delegate
public:
using MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegatePImpl;

void operator()()
R operator()()
{
auto current = first;
if (!current)
return;
return {};

static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return;
if (fence.load()) return {};
fence.store(true);
#else
if (fence.exchange(true)) return;
if (fence.exchange(true)) return {};
#endif

Node_t* prev = nullptr;
// prevent execution of new callbacks during this run
auto stop = last;

R result;
bool done;
do
{
done = current == stop;
if (!Call<Delegate, R, ISQUEUE>::execute(current->mDelegate))
{
// remove callback from stack
#ifdef ARDUINO
InterruptLock lockAllInterruptsInThisScope;
#else
std::lock_guard<std::mutex> lock(mutex_unused);
#endif

auto to_recycle = current;

// removing rLast
if (last == current)
last = prev;

current = current->mNext;
if (prev)
{
prev->mNext = current;
}
else
{
first = current;
}

if (ISQUEUE)
recycle_node_unsafe(to_recycle);
else
delete to_recycle;
}
else
{
prev = current;
current = current->mNext;
}

result = Call<Delegate, R, ISQUEUE>::execute(current->mDelegate);
this->traverse(prev, current, result);
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (current && !done);

fence.store(false);
return result;
}
};

Expand All @@ -477,7 +454,96 @@ namespace delegate
using MultiDelegateImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
};

template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P>
class MultiDelegate<Delegate, void(P...), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>
{
public:
using MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>::MultiDelegatePImpl;
using typename MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>::Node_t;
using MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>::first;
using MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>::last;

void operator()(P... args)
{
auto current = first;
if (!current)
return;

static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return;
fence.store(true);
#else
if (fence.exchange(true)) return;
#endif

Node_t* prev = nullptr;
// prevent execution of new callbacks during this run
auto stop = last;

bool done;
do
{
done = current == stop;
CallP<Delegate, void, ISQUEUE, P...>::execute(current->mDelegate, args...);
this->traverse(prev, current);
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (current && !done);

fence.store(false);
}
};

template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY>
class MultiDelegate<Delegate, void(), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>
{
public:
using MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
using typename MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>::Node_t;
using MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>::first;
using MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>::last;

void operator()()
{
auto current = first;
if (!current)
return;

static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return;
fence.store(true);
#else
if (fence.exchange(true)) return;
#endif

Node_t* prev = nullptr;
// prevent execution of new callbacks during this run
auto stop = last;

bool done;
do
{
done = current == stop;
Call<Delegate, void, ISQUEUE>::execute(current->mDelegate);
this->traverse(prev, current);
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (current && !done);

fence.store(false);
}
};

}

}

/**
Expand All @@ -492,10 +558,10 @@ It is designed to be used with Delegate, the efficient runtime wrapper for C fun
If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue
removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until
explicitly removed.
If the result type of the function call operator of Delegate is non-void, the type-conversion to bool
of that result determines if the item is immediately removed or kept after each call: a Multidelegate
queue removes an item only if true is returned, but a Multidelegate event multiplexer removes event
handlers that return false.
If the result type of the function call operator of Delegate is non-void, in a MultiDelegate queue
the type-conversion to bool of that result determines if the item is immediately removed or kept
after each call: if true is returned, the item is removed. A Multidelegate event multiplexer keeps event
handlers until they are explicitly removed.
@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically
allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate
instance during its own lifetime for efficiency.
Expand Down