@@ -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
2425namespace {
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