mirror of
https://github.com/esphome/esphome.git
synced 2025-11-20 08:46:01 +00:00
actions: fix loop re-entry (#7972)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <forward_list>
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -264,10 +265,22 @@ template<class C, typename... Ts> class IsRunningCondition : public Condition<Ts
|
||||
C *parent_;
|
||||
};
|
||||
|
||||
/** Wait for a script to finish before continuing.
|
||||
*
|
||||
* Uses queue-based storage to safely handle concurrent executions.
|
||||
* While concurrent execution from the same trigger is uncommon, it's possible
|
||||
* (e.g., rapid button presses, high-frequency sensor updates), so we use
|
||||
* queue-based storage for correctness.
|
||||
*/
|
||||
template<class C, typename... Ts> class ScriptWaitAction : public Action<Ts...>, public Component {
|
||||
public:
|
||||
ScriptWaitAction(C *script) : script_(script) {}
|
||||
|
||||
void setup() override {
|
||||
// Start with loop disabled - only enable when there's work to do
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
void play_complex(Ts... x) override {
|
||||
this->num_running_++;
|
||||
// Check if we can continue immediately.
|
||||
@@ -275,7 +288,11 @@ template<class C, typename... Ts> class ScriptWaitAction : public Action<Ts...>,
|
||||
this->play_next_(x...);
|
||||
return;
|
||||
}
|
||||
this->var_ = std::make_tuple(x...);
|
||||
|
||||
// Store parameters for later execution
|
||||
this->param_queue_.emplace_front(x...);
|
||||
// Enable loop now that we have work to do
|
||||
this->enable_loop();
|
||||
this->loop();
|
||||
}
|
||||
|
||||
@@ -286,15 +303,30 @@ template<class C, typename... Ts> class ScriptWaitAction : public Action<Ts...>,
|
||||
if (this->script_->is_running())
|
||||
return;
|
||||
|
||||
this->play_next_tuple_(this->var_);
|
||||
while (!this->param_queue_.empty()) {
|
||||
auto ¶ms = this->param_queue_.front();
|
||||
this->play_next_tuple_(params, typename gens<sizeof...(Ts)>::type());
|
||||
this->param_queue_.pop_front();
|
||||
}
|
||||
// Queue is now empty - disable loop until next play_complex
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
void play(Ts... x) override { /* ignore - see play_complex */
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
this->param_queue_.clear();
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<int... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
|
||||
this->play_next_(std::get<S>(tuple)...);
|
||||
}
|
||||
|
||||
C *script_;
|
||||
std::tuple<Ts...> var_{};
|
||||
std::forward_list<std::tuple<Ts...>> param_queue_;
|
||||
};
|
||||
|
||||
} // namespace script
|
||||
|
||||
Reference in New Issue
Block a user