Skip to content

Commit 20ca226

Browse files
mcsprhasenradball
authored andcommitted
Longer delays for Ticker and some internal updates (esp8266#8625)
Adds max duration check. In case it is over SDK limit, enable 'repeat'ing timer with a duration proportional to the original one and count until it executes N times, only then run the callback. Code with durations less than that executes as usual. Original proposal was to not create anything or create some kind of error state... which seems counter-productive to not help out with this pretty solvable use-case. Additional updates, while refactoring the class - Stronger types for internal time management using `std::chrono::duration`. Works the same, `std::chrono::duration` handles seconds <-> milliseconds conversion, and we don't have to remember the time type in each method. (...and even allow `once()` and `attach` as overloads instead of the current `_ms`-suffix, in a future update) - `::detach()` when timer finishes. Fixes (unintentional?) side-effect that we remain `::active()`. Plus, this destroys any lambda-bound variables that will persist with the Ticker object. And, since we can't re-arm with the existing function (`Ticker::attach_ms(uint32_t just_the_time)` and etc.) - `std::variant` aka union for internal callback storage (kind-of similar to esp8266#6918). Instead of having two separate code paths, **always** attach our static function and dispatch using type info. Also helps with the issue described above, since it will call `std::function` dtor when ptr + arg is attached instead of doing nothing. - smarter copy and move, detaching existing timer on assignment and detaching the moved-in timer object in both ctor and assignment. Copying or moving a running timer no longer blindly copies `_timer` pointer, allowing to disarm the original one. Since we are a simple wrapper around `os_timer_t`, just do the simpler thing (and not re-schedule the callback, try to store original times, etc. polledTimeout already does it and is copyable)
1 parent 43118f1 commit 20ca226

File tree

4 files changed

+226
-81
lines changed

4 files changed

+226
-81
lines changed

libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino

+3-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ void updateSensor(sensorType &sensor) {
146146
SSEBroadcastState(sensor.name, sensor.value, newVal); // only broadcast if state is different
147147
}
148148
sensor.value = newVal;
149-
sensor.update.once(rand() % 20 + 10, std::bind(updateSensor, sensor)); // randomly update sensor
149+
sensor.update.once(rand() % 20 + 10, [&]() {
150+
updateSensor(sensor);
151+
}); // randomly update sensor
150152
}
151153

152154
void handleSubscribe() {

libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ public:
1313
ExampleClass(int pin, int duration)
1414
: _pin(pin), _duration(duration) {
1515
pinMode(_pin, OUTPUT);
16-
_myTicker.attach_ms(_duration, std::bind(&ExampleClass::classBlink, this));
16+
_myTicker.attach_ms(_duration,
17+
[this]() {
18+
classBlink();
19+
});
1720
}
18-
~ExampleClass(){};
1921

2022
int _pin, _duration;
2123
Ticker _myTicker;
@@ -53,7 +55,7 @@ void setup() {
5355
scheduledTicker.attach_ms_scheduled(100, scheduledBlink);
5456

5557
pinMode(LED4, OUTPUT);
56-
parameterTicker.attach_ms(100, std::bind(parameterBlink, LED4));
58+
parameterTicker.attach_ms(100, parameterBlink, LED4);
5759

5860
pinMode(LED5, OUTPUT);
5961
lambdaTicker.attach_ms(100, []() {

libraries/Ticker/src/Ticker.cpp

+85-21
Original file line numberDiff line numberDiff line change
@@ -23,49 +23,113 @@
2323
#include "eagle_soc.h"
2424
#include "osapi.h"
2525

26+
#include <Arduino.h>
2627
#include "Ticker.h"
2728

28-
Ticker::Ticker()
29-
: _timer(nullptr) {}
29+
// ETSTimer is part of the instance, and we don't have any state besides
30+
// the things required for the callback. Allow copies and moves, but
31+
// disable any member copies and default-init + detach() instead.
3032

3133
Ticker::~Ticker()
3234
{
3335
detach();
3436
}
3537

36-
void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg)
38+
Ticker::Ticker(const Ticker&)
3739
{
38-
if (_timer)
39-
{
40+
}
41+
42+
Ticker& Ticker::operator=(const Ticker&)
43+
{
44+
detach();
45+
return *this;
46+
}
47+
48+
Ticker::Ticker(Ticker&& other) noexcept
49+
{
50+
other.detach();
51+
}
52+
53+
Ticker& Ticker::operator=(Ticker&& other) noexcept
54+
{
55+
other.detach();
56+
detach();
57+
return *this;
58+
}
59+
60+
void Ticker::_attach(Ticker::Milliseconds milliseconds, bool repeat)
61+
{
62+
if (_timer) {
4063
os_timer_disarm(_timer);
64+
} else {
65+
_timer = &_timer_internal;
4166
}
42-
else
43-
{
44-
_timer = &_etsTimer;
67+
68+
os_timer_setfn(_timer,
69+
[](void* ptr) {
70+
reinterpret_cast<Ticker*>(ptr)->_static_callback();
71+
}, this);
72+
73+
_repeat = repeat;
74+
75+
// whenever duration excedes this limit, make timer repeatable N times
76+
// in case it is really repeatable, it will reset itself and continue as usual
77+
size_t total = 0;
78+
if (milliseconds > DurationMax) {
79+
total = 1;
80+
while (milliseconds > DurationMax) {
81+
total *= 2;
82+
milliseconds /= 2;
83+
}
84+
_tick.reset(new callback_tick_t{
85+
.total = total,
86+
.count = 0,
87+
});
88+
repeat = true;
4589
}
4690

47-
os_timer_setfn(_timer, callback, arg);
48-
os_timer_arm(_timer, milliseconds, repeat);
91+
os_timer_arm(_timer, milliseconds.count(), repeat);
4992
}
5093

5194
void Ticker::detach()
5295
{
53-
if (!_timer)
54-
return;
55-
56-
os_timer_disarm(_timer);
57-
_timer = nullptr;
58-
_callback_function = nullptr;
96+
if (_timer) {
97+
os_timer_disarm(_timer);
98+
_timer = nullptr;
99+
_tick.reset(nullptr);
100+
_callback = std::monostate{};
101+
}
59102
}
60103

61104
bool Ticker::active() const
62105
{
63-
return _timer;
106+
return _timer != nullptr;
64107
}
65108

66-
void Ticker::_static_callback(void* arg)
109+
void Ticker::_static_callback()
67110
{
68-
Ticker* _this = reinterpret_cast<Ticker*>(arg);
69-
if (_this && _this->_callback_function)
70-
_this->_callback_function();
111+
if (_tick) {
112+
++_tick->count;
113+
if (_tick->count < _tick->total) {
114+
return;
115+
}
116+
}
117+
118+
std::visit([](auto&& callback) {
119+
using T = std::decay_t<decltype(callback)>;
120+
if constexpr (std::is_same_v<T, callback_ptr_t>) {
121+
callback.func(callback.arg);
122+
} else if constexpr (std::is_same_v<T, callback_function_t>) {
123+
callback();
124+
}
125+
}, _callback);
126+
127+
if (_repeat) {
128+
if (_tick) {
129+
_tick->count = 0;
130+
}
131+
return;
132+
}
133+
134+
detach();
71135
}

0 commit comments

Comments
 (0)