Skip to content

Commit f01691b

Browse files
committed
Provide own memory management for Ticker instances to conserve heap.
Insightful source code remark for future refactoring
1 parent 52d1e41 commit f01691b

File tree

1 file changed

+36
-3
lines changed

1 file changed

+36
-3
lines changed

cores/esp32/Schedule.cpp

+36-3
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,42 @@ struct scheduled_fn_t
2121
scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { }
2222
};
2323

24+
// anonymous namespace provides compilation-unit internal linkage
2425
namespace {
2526
static circular_queue_mp<scheduled_fn_t> schedule_queue(SCHEDULED_FN_MAX_COUNT);
27+
28+
class SchedulerTicker;
29+
// A local heap for Ticker instances to prevent global heap exhaustion
30+
class TickerHeap : public circular_queue_mp<SchedulerTicker*> {
31+
public:
32+
TickerHeap(const size_t capacity) : circular_queue_mp<SchedulerTicker*>(capacity)
33+
{
34+
heap = new char[capacity * sizeof(Ticker)];
35+
for (size_t i = 0; i < capacity; ++i) push(reinterpret_cast<SchedulerTicker*>(heap + i * sizeof(Ticker)));
36+
}
37+
~TickerHeap()
38+
{
39+
delete heap;
40+
}
41+
protected:
42+
char* heap;
43+
};
44+
static TickerHeap tickerHeap(SCHEDULED_FN_MAX_COUNT);
45+
46+
class SchedulerTicker : public Ticker
47+
{
48+
public:
49+
static void operator delete(void* ptr) {
50+
tickerHeap.push(static_cast<SchedulerTicker*>(ptr));
51+
}
52+
};
53+
static_assert(sizeof(SchedulerTicker) == sizeof(Ticker), "sizeof(SchedulerTicker) != sizeof(Ticker)");
54+
2655
#ifndef ESP8266
2756
std::mutex schedulerMutex;
2857
#endif
2958

30-
void ticker_scheduled(Ticker* ticker, const std::function<bool(void)>& fn, uint32_t repeat_us, schedule_e policy)
59+
void ticker_scheduled(SchedulerTicker* ticker, const std::function<bool(void)>& fn, uint32_t repeat_us, schedule_e policy)
3160
{
3261
auto repeat_ms = (repeat_us + 500) / 1000;
3362
ticker->once_ms(repeat_ms, [ticker, fn, repeat_us, policy]()
@@ -49,8 +78,12 @@ bool IRAM_ATTR schedule_function_us(std::function<bool(void)>&& fn, uint32_t rep
4978
{
5079
if (repeat_us >= TICKER_MIN_US)
5180
{
52-
auto ticker = new Ticker;
53-
if (!ticker) return false;
81+
// failure to aquire a Ticker must be returned to caller now. Specifically, allocating
82+
// Tickers from inside the scheduled function doesn't work, any exhaustion of the scheduler
83+
// can dead-lock it forever.
84+
auto tickerPlace = tickerHeap.pop();
85+
if (!tickerPlace) return false;
86+
auto ticker = new(tickerPlace) SchedulerTicker;
5487
if (!schedule_function([ticker, fn = std::move(fn), repeat_us, policy]()
5588
{
5689
ticker_scheduled(ticker, fn, repeat_us, policy);

0 commit comments

Comments
 (0)