Merge remote-tracking branch 'upstream/web_server_disable_loop' into integration

This commit is contained in:
J. Nick Koston
2026-04-03 14:33:32 -10:00
4 changed files with 22 additions and 5 deletions
+13 -2
View File
@@ -286,10 +286,11 @@ void DeferredUpdateEventSource::try_send_nodefer(const char *message, const char
this->send(message, event, id, reconnect);
}
void DeferredUpdateEventSourceList::loop() {
bool DeferredUpdateEventSourceList::loop() {
for (DeferredUpdateEventSource *dues : *this) {
dues->loop();
}
return !this->empty();
}
void DeferredUpdateEventSourceList::deferrable_send_state(void *source, const char *event_type,
@@ -318,6 +319,7 @@ void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServer
es->onDisconnect([this, es](AsyncEventSourceClient *client) { this->on_client_disconnect_(es); });
es->handleRequest(request);
ws->enable_loop_soon_any_context();
}
void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource *source) {
@@ -419,7 +421,16 @@ void WebServer::setup() {
this->events_.try_send_nodefer(buf, "ping", millis(), 30000);
});
}
void WebServer::loop() { this->events_.loop(); }
void WebServer::loop() {
// No SSE clients connected; stop looping until a new client connects via
// enable_loop_soon_any_context(). This is safe because:
// - set_interval/set_timeout/defer run via the Scheduler, independent of loop()
// - deferrable_send_state early-outs when no clients are connected
// - try_send_nodefer (log, ping) iterates sessions which are empty
// - REST API handlers use defer() which runs via the Scheduler
if (!this->events_.loop())
this->disable_loop();
}
#ifdef USE_LOGGER
void WebServer::on_log(uint8_t level, const char *tag, const char *message, size_t message_len) {
+2 -1
View File
@@ -169,7 +169,8 @@ class DeferredUpdateEventSourceList final : public std::list<DeferredUpdateEvent
void on_client_disconnect_(DeferredUpdateEventSource *source);
public:
void loop();
/// Returns true if there are still connected clients.
bool loop();
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
@@ -484,9 +484,12 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) {
this->on_connect_(rsp);
}
this->sessions_.push_back(rsp);
// Wake up WebServer::loop() to drain deferred event queues for this client.
// Safe from httpd task context via the pending_enable_loop_ flag.
this->web_server_->enable_loop_soon_any_context();
}
void AsyncEventSource::loop() {
bool AsyncEventSource::loop() {
// Clean up dead sessions safely
// This follows the ESP-IDF pattern where free_ctx marks resources as dead
// and the main loop handles the actual cleanup to avoid race conditions
@@ -504,6 +507,7 @@ void AsyncEventSource::loop() {
++i;
}
}
return !this->sessions_.empty();
}
void AsyncEventSource::try_send_nodefer(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
@@ -340,7 +340,8 @@ class AsyncEventSource : public AsyncWebHandler {
void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
void loop();
/// Returns true if there are still connected clients.
bool loop();
bool empty() { return this->count() == 0; }
size_t count() const { return this->sessions_.size(); }