8000 Safeguard for Ticker internal storage that may be changed during callback execution by mcspr · Pull Request #8820 · esp8266/Arduino · GitHub
[go: up one dir, main page]

Skip to content

Safeguard for Ticker internal storage that may be changed during callback execution #8820

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 26, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Ticker callback should be allowed to be destroyed
Mainly, we protect std::function and variant itself from undefined
behaviour when the underlying object is destroyed and then re-created
with something else inside of it.

For example, every captured object would be destroyed.
```cpp
Ticker ticker;
void oops(Job job) {
    ticker.once(1.0f,
        [job = std::move(job)]() {
            ticker.once(job.next(), do_something_else);
            job.work(); // ~Job() just happened!
        });
}
```

Another important case is repeating through once()
```cpp
Ticker ticker;
void repeat() {
    ticker.once(1.0f, repeat);
    puts("tick");
}

void setup() {
    repeat();
}
```

Temporarily moving callback object into the function and returning
earlier should solve both cases.
  • Loading branch information
mcspr committed Jan 20, 2023
commit 1623b6a71fb59d7fc09e87f35286d7ccfa9034e9
15 changes: 14 additions & 1 deletion libraries/Ticker/src/Ticker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,27 @@ void Ticker::_static_callback()
}
}

// it is technically allowed to call either schedule or detach
// *during* callback execution. allow both to happen
auto tmp = std::move(_callback);

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);
}, tmp);

// ...and move ourselves back only when object is in a valid state
// * ticker was not detached, zeroing timer pointer
// * nothing else replaced callback variant
if ((_timer == nullptr) || !std::holds_alternative<std::monostate>(_callback)) {
return;
}

_callback = std::move(tmp);

if (_repeat) {
if (_tick) {
Expand Down
0