@@ -21,13 +21,42 @@ struct scheduled_fn_t
21
21
scheduled_fn_t () : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { }
22
22
};
23
23
24
+ // anonymous namespace provides compilation-unit internal linkage
24
25
namespace {
25
26
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
+
26
55
#ifndef ESP8266
27
56
std::mutex schedulerMutex;
28
57
#endif
29
58
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)
31
60
{
32
61
auto repeat_ms = (repeat_us + 500 ) / 1000 ;
33
62
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
49
78
{
50
79
if (repeat_us >= TICKER_MIN_US)
51
80
{
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;
54
87
if (!schedule_function ([ticker, fn = std::move (fn), repeat_us, policy]()
55
88
{
56
89
ticker_scheduled (ticker, fn, repeat_us, policy);
0 commit comments