Cache errno into local variable to eliminate duplicate __errno() calls

On embedded targets (ESP32, RP2040, ESP8266), errno expands to
(*__errno()) - a function call returning a pointer to thread-local
storage. The compiler cannot optimize away repeated accesses since
errno is treated as volatile. Cache it once into a const int local
to avoid redundant calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
J. Nick Koston
2026-03-12 15:57:16 -10:00
parent 920af91db6
commit 4e5d1cae8d
6 changed files with 43 additions and 29 deletions
+6 -4
View File
@@ -113,10 +113,11 @@ APIError APIFrameHelper::loop() {
// Common socket write error handling
APIError APIFrameHelper::handle_socket_write_error_() {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
const int err = errno;
if (err == EWOULDBLOCK || err == EAGAIN) {
return APIError::WOULD_BLOCK;
}
HELPER_LOG("Socket write failed with errno %d", errno);
HELPER_LOG("Socket write failed with errno %d", err);
this->state_ = State::FAILED;
return APIError::SOCKET_WRITE_FAILED;
}
@@ -278,11 +279,12 @@ APIError APIFrameHelper::init_common_() {
APIError APIFrameHelper::handle_socket_read_result_(ssize_t received) {
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
const int err = errno;
if (err == EWOULDBLOCK || err == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
HELPER_LOG("Socket read failed with errno %d", err);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
@@ -52,11 +52,12 @@ bool AsyncClient::connect(const char *host, uint16_t port) {
connect_cb_(connect_arg_, this);
return true;
}
if (errno != EINPROGRESS) {
ESP_LOGE(TAG, "Connect failed: %d", errno);
const int saved_errno = errno;
if (saved_errno != EINPROGRESS) {
ESP_LOGE(TAG, "Connect failed: %d", saved_errno);
close();
if (error_cb_)
error_cb_(error_arg_, this, errno);
error_cb_(error_arg_, this, saved_errno);
return false;
}
@@ -79,11 +80,12 @@ size_t AsyncClient::write(const char *data, size_t len) {
ssize_t sent = socket_->write(data, len);
if (sent < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
ESP_LOGE(TAG, "Write error: %d", errno);
const int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK) {
ESP_LOGE(TAG, "Write error: %d", err);
close();
if (error_cb_)
error_cb_(error_arg_, this, errno);
error_cb_(error_arg_, this, err);
}
return 0;
}
@@ -129,10 +131,11 @@ void AsyncClient::loop() {
error_cb_(error_arg_, this, error);
}
} else if (ret < 0) {
ESP_LOGE(TAG, "Select error: %d", errno);
const int err = errno;
ESP_LOGE(TAG, "Select error: %d", err);
close();
if (error_cb_)
error_cb_(error_arg_, this, errno);
error_cb_(error_arg_, this, err);
}
} else if (connected_) {
// For connected sockets, use the Application's select() results
@@ -148,11 +151,14 @@ void AsyncClient::loop() {
} else if (len > 0) {
if (data_cb_)
data_cb_(data_arg_, this, buf, len);
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
ESP_LOGW(TAG, "Read error: %d", errno);
close();
if (error_cb_)
error_cb_(error_arg_, this, errno);
} else {
const int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK) {
ESP_LOGW(TAG, "Read error: %d", err);
close();
if (error_cb_)
error_cb_(error_arg_, this, err);
}
}
}
}
@@ -100,8 +100,9 @@ void DNSServer::process_next_request() {
&client_addr_len);
if (len < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
ESP_LOGE(TAG, "recvfrom failed: %d", errno);
const int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
ESP_LOGE(TAG, "recvfrom failed: %d", err);
}
return;
}
@@ -332,12 +332,13 @@ void ESPHomeOTAComponent::handle_data_() {
size_t requested = remaining < OTA_BUFFER_SIZE ? remaining : OTA_BUFFER_SIZE;
ssize_t read = this->client_->read(buf, requested);
if (read == -1) {
if (this->would_block_(errno)) {
const int err = errno;
if (this->would_block_(err)) {
// read() already waited up to SO_RCVTIMEO for data, just feed WDT
App.feed_wdt();
continue;
}
ESP_LOGW(TAG, "Read err %d", errno);
ESP_LOGW(TAG, "Read err %d", err);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
} else if (read == 0) {
ESP_LOGW(TAG, "Remote closed");
@@ -426,8 +427,9 @@ bool ESPHomeOTAComponent::readall_(uint8_t *buf, size_t len) {
ssize_t read = this->client_->read(buf + at, len - at);
if (read == -1) {
if (!this->would_block_(errno)) {
ESP_LOGW(TAG, "Read err %zu bytes, errno %d", len, errno);
const int err = errno;
if (!this->would_block_(err)) {
ESP_LOGW(TAG, "Read err %zu bytes, errno %d", len, err);
return false;
}
} else if (read == 0) {
@@ -455,8 +457,9 @@ bool ESPHomeOTAComponent::writeall_(const uint8_t *buf, size_t len) {
ssize_t written = this->client_->write(buf + at, len - at);
if (written == -1) {
if (!this->would_block_(errno)) {
ESP_LOGW(TAG, "Write err %zu bytes, errno %d", len, errno);
const int err = errno;
if (!this->would_block_(err)) {
ESP_LOGW(TAG, "Write err %zu bytes, errno %d", len, err);
return false;
}
// EWOULDBLOCK: on raw TCP writes never block, delay(1) prevents spinning
@@ -74,12 +74,13 @@ int nonblocking_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_
// Use MSG_DONTWAIT to prevent blocking when TCP send buffer is full
int ret = send(sockfd, buf, buf_len, flags | MSG_DONTWAIT);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
const int err = errno;
if (err == EAGAIN || err == EWOULDBLOCK) {
// Buffer full - retry later
return HTTPD_SOCK_ERR_TIMEOUT;
}
// Real error
ESP_LOGD(TAG, "send error: errno %d", errno);
ESP_LOGD(TAG, "send error: errno %d", err);
return HTTPD_SOCK_ERR_FAIL;
}
return ret;
+3 -2
View File
@@ -701,7 +701,8 @@ void Application::yield_with_select_(uint32_t delay_ms) {
// ret < 0: error (except EINTR which is normal)
// ret > 0: socket(s) have data ready - normal and expected
// ret == 0: timeout occurred - normal and expected
if (ret >= 0 || errno == EINTR) [[likely]] {
const int err = errno;
if (ret >= 0 || err == EINTR) [[likely]] {
// Yield if zero timeout since select(0) only polls without yielding
if (delay_ms == 0) [[unlikely]] {
yield();
@@ -709,7 +710,7 @@ void Application::yield_with_select_(uint32_t delay_ms) {
return;
}
// select() error - log and fall through to delay()
ESP_LOGW(TAG, "select() failed with errno %d", errno);
ESP_LOGW(TAG, "select() failed with errno %d", err);
}
// No sockets registered or select() failed - use regular delay
delay(delay_ms);