[web_server_idf] Move session adoption to out-of-line cold path

Keep the hot path in AsyncEventSource::loop() to the atomic load,
branch, and dead-session cleanup. On a real connect, call a
noinline+cold helper (adopt_pending_sessions_main_loop_) to swap the
pending list out, drop any already-disconnected sessions, and invoke
on_connect_ / start_session_main_loop_ on the rest.
This commit is contained in:
J. Nick Koston
2026-04-24 03:08:01 -05:00
parent c7a67ac41d
commit 121b4d6916
2 changed files with 26 additions and 19 deletions
@@ -493,26 +493,9 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) {
}
bool AsyncEventSource::loop() {
// Fast path: one atomic load per tick. Lock only on a real connect.
// Fast path: one atomic load per tick. Slow path is out-of-line on connect.
if (this->has_pending_sessions_.load(std::memory_order_acquire)) {
std::vector<AsyncEventSourceResponse *> incoming;
{
LockGuard guard{this->pending_mutex_};
incoming.swap(this->pending_sessions_);
this->has_pending_sessions_.store(false, std::memory_order_relaxed);
}
for (auto *rsp : incoming) {
// Already disconnected? Drop it; skip on_connect_/prime on a dead session.
if (rsp->fd_.load() == 0) {
delete rsp; // NOLINT(cppcoreguidelines-owning-memory)
continue;
}
this->sessions_.push_back(rsp);
if (this->on_connect_) {
this->on_connect_(rsp);
}
rsp->start_session_main_loop_();
}
this->adopt_pending_sessions_main_loop_();
}
// Clean up dead sessions safely
@@ -535,6 +518,27 @@ bool AsyncEventSource::loop() {
return !this->sessions_.empty();
}
void AsyncEventSource::adopt_pending_sessions_main_loop_() {
std::vector<AsyncEventSourceResponse *> incoming;
{
LockGuard guard{this->pending_mutex_};
incoming.swap(this->pending_sessions_);
this->has_pending_sessions_.store(false, std::memory_order_relaxed);
}
for (auto *rsp : incoming) {
// Already disconnected? Drop it; skip on_connect_/session start on a dead session.
if (rsp->fd_.load() == 0) {
delete rsp; // NOLINT(cppcoreguidelines-owning-memory)
continue;
}
this->sessions_.push_back(rsp);
if (this->on_connect_) {
this->on_connect_(rsp);
}
rsp->start_session_main_loop_();
}
}
void AsyncEventSource::try_send_nodefer(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
for (auto *ses : this->sessions_) {
if (ses->fd_.load() != 0) { // Skip dead sessions
@@ -350,6 +350,9 @@ class AsyncEventSource : public AsyncWebHandler {
size_t count() const { return this->sessions_.size(); }
protected:
// Cold path: move sessions from pending_sessions_ into sessions_ and greet each one.
void __attribute__((noinline, cold)) adopt_pending_sessions_main_loop_();
std::string url_;
// Main-loop only. Vector: SSE sessions are 1-5 connections, linear search beats set.
std::vector<AsyncEventSourceResponse *> sessions_;