Skip to content

Commit

Permalink
Longer delays for Ticker and some internal updates (#8625)
Browse files Browse the repository at this point in the history
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 #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)
  • Loading branch information
mcspr committed Nov 1, 2022
1 parent 04494f0 commit 27c0591
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ void updateSensor(sensorType &sensor) {
SSEBroadcastState(sensor.name, sensor.value, newVal); // only broadcast if state is different
}
sensor.value = newVal;
sensor.update.once(rand() % 20 + 10, std::bind(updateSensor, sensor)); // randomly update sensor
sensor.update.once(rand() % 20 + 10, [&]() {
updateSensor(sensor);
}); // randomly update sensor
}

void handleSubscribe() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ public:
ExampleClass(int pin, int duration)
: _pin(pin), _duration(duration) {
pinMode(_pin, OUTPUT);
_myTicker.attach_ms(_duration, std::bind(&ExampleClass::classBlink, this));
_myTicker.attach_ms(_duration,
[this]() {
classBlink();
});
}
~ExampleClass(){};

int _pin, _duration;
Ticker _myTicker;
Expand Down Expand Up @@ -53,7 +55,7 @@ void setup() {
scheduledTicker.attach_ms_scheduled(100, scheduledBlink);

pinMode(LED4, OUTPUT);
parameterTicker.attach_ms(100, std::bind(parameterBlink, LED4));
parameterTicker.attach_ms(100, parameterBlink, LED4);

pinMode(LED5, OUTPUT);
lambdaTicker.attach_ms(100, []() {
Expand Down
106 changes: 85 additions & 21 deletions libraries/Ticker/src/Ticker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,113 @@
#include "eagle_soc.h"
#include "osapi.h"

#include <Arduino.h>
#include "Ticker.h"

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

Ticker::~Ticker()
{
detach();
}

void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg)
Ticker::Ticker(const Ticker&)
{
if (_timer)
{
}

Ticker& Ticker::operator=(const Ticker&)
{
detach();
return *this;
}

Ticker::Ticker(Ticker&& other) noexcept
{
other.detach();
}

Ticker& Ticker::operator=(Ticker&& other) noexcept
{
other.detach();
detach();
return *this;
}

void Ticker::_attach(Ticker::Milliseconds milliseconds, bool repeat)
{
if (_timer) {
os_timer_disarm(_timer);
} else {
_timer = &_timer_internal;
}
else
{
_timer = &_etsTimer;

os_timer_setfn(_timer,
[](void* ptr) {
reinterpret_cast<Ticker*>(ptr)->_static_callback();
}, this);

_repeat = repeat;

// whenever duration excedes this limit, make timer repeatable N times
// in case it is really repeatable, it will reset itself and continue as usual
size_t total = 0;
if (milliseconds > DurationMax) {
total = 1;
while (milliseconds > DurationMax) {
total *= 2;
milliseconds /= 2;
}
_tick.reset(new callback_tick_t{
.total = total,
.count = 0,
});
repeat = true;
}

os_timer_setfn(_timer, callback, arg);
os_timer_arm(_timer, milliseconds, repeat);
os_timer_arm(_timer, milliseconds.count(), repeat);
}

void Ticker::detach()
{
if (!_timer)
return;

os_timer_disarm(_timer);
_timer = nullptr;
_callback_function = nullptr;
if (_timer) {
os_timer_disarm(_timer);
_timer = nullptr;
_tick.reset(nullptr);
_callback = std::monostate{};
}
}

bool Ticker::active() const
{
return _timer;
return _timer != nullptr;
}

void Ticker::_static_callback(void* arg)
void Ticker::_static_callback()
{
Ticker* _this = reinterpret_cast<Ticker*>(arg);
if (_this && _this->_callback_function)
_this->_callback_function();
if (_tick) {
++_tick->count;
if (_tick->count < _tick->total) {
return;
}
}

std::visit([](auto&& callback) {
using T = std::decay_t<decltype(callback)>;
if constexpr (std::is_same_v<T, callback_ptr_t>) {
callback.func(callback.arg);
} else if constexpr (std::is_same_v<T, callback_function_t>) {
callback();
}
}, _callback);

if (_repeat) {
if (_tick) {
_tick->count = 0;
}
return;
}

detach();
}
Loading

0 comments on commit 27c0591

Please sign in to comment.