mirror of
https://github.com/esphome/esphome.git
synced 2026-03-23 22:37:31 +08:00
[socket] Fast path for TCP_NODELAY bypasses lwip_setsockopt overhead (#14693)
This commit is contained in:
committed by
Jesse Hills
parent
3a838d897f
commit
1d881ef6f4
@@ -14,7 +14,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
struct lwip_sock;
|
||||
#include "esphome/core/lwip_fast_select.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::socket {
|
||||
@@ -56,6 +56,15 @@ class BSDSocketImpl {
|
||||
return ::getsockopt(this->fd_, level, optname, optval, optlen);
|
||||
}
|
||||
int setsockopt(int level, int optname, const void *optval, socklen_t optlen) {
|
||||
#if defined(USE_LWIP_FAST_SELECT) && defined(CONFIG_LWIP_TCPIP_CORE_LOCKING)
|
||||
// Fast path for TCP_NODELAY: directly set the pcb flag under the TCPIP core lock,
|
||||
// bypassing lwip_setsockopt overhead (socket lookups, hook, switch cascade, refcounting).
|
||||
if (level == IPPROTO_TCP && optname == TCP_NODELAY && optlen == sizeof(int) && optval != nullptr) {
|
||||
LwIPLock lock;
|
||||
if (esphome_lwip_set_nodelay(this->cached_sock_, *reinterpret_cast<const int *>(optval) != 0))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return ::setsockopt(this->fd_, level, optname, optval, optlen);
|
||||
}
|
||||
int listen(int backlog) { return ::listen(this->fd_, backlog); }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "headers.h"
|
||||
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
struct lwip_sock;
|
||||
#include "esphome/core/lwip_fast_select.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::socket {
|
||||
@@ -52,6 +52,15 @@ class LwIPSocketImpl {
|
||||
return lwip_getsockopt(this->fd_, level, optname, optval, optlen);
|
||||
}
|
||||
int setsockopt(int level, int optname, const void *optval, socklen_t optlen) {
|
||||
#if defined(USE_LWIP_FAST_SELECT) && defined(CONFIG_LWIP_TCPIP_CORE_LOCKING)
|
||||
// Fast path for TCP_NODELAY: directly set the pcb flag under the TCPIP core lock,
|
||||
// bypassing lwip_setsockopt overhead (socket lookups, hook, switch cascade, refcounting).
|
||||
if (level == IPPROTO_TCP && optname == TCP_NODELAY && optlen == sizeof(int) && optval != nullptr) {
|
||||
LwIPLock lock;
|
||||
if (esphome_lwip_set_nodelay(this->cached_sock_, *reinterpret_cast<const int *>(optval) != 0))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return lwip_setsockopt(this->fd_, level, optname, optval, optlen);
|
||||
}
|
||||
int listen(int backlog) { return lwip_listen(this->fd_, backlog); }
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
// LwIP headers must come first — they define netconn_callback, struct lwip_sock, etc.
|
||||
#include <lwip/api.h>
|
||||
#include <lwip/priv/sockets_priv.h>
|
||||
#include <lwip/tcp.h>
|
||||
// FreeRTOS include paths differ: ESP-IDF uses freertos/ prefix, LibreTiny does not
|
||||
#ifdef USE_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
@@ -216,6 +217,21 @@ void esphome_lwip_hook_socket(struct lwip_sock *sock) {
|
||||
sock->conn->callback = esphome_socket_event_callback;
|
||||
}
|
||||
|
||||
bool esphome_lwip_set_nodelay(struct lwip_sock *sock, bool enable) {
|
||||
if (sock == NULL || sock->conn == NULL)
|
||||
return false;
|
||||
if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP)
|
||||
return false;
|
||||
if (sock->conn->pcb.tcp == NULL)
|
||||
return false;
|
||||
if (enable) {
|
||||
tcp_nagle_disable(sock->conn->pcb.tcp);
|
||||
} else {
|
||||
tcp_nagle_enable(sock->conn->pcb.tcp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wake the main loop from another FreeRTOS task. NOT ISR-safe.
|
||||
void esphome_lwip_wake_main_loop(void) {
|
||||
TaskHandle_t task = s_main_loop_task;
|
||||
|
||||
@@ -66,6 +66,13 @@ void esphome_lwip_wake_main_loop(void);
|
||||
/// @param px_higher_priority_task_woken Set to pdTRUE if a context switch is needed.
|
||||
void esphome_lwip_wake_main_loop_from_isr(int *px_higher_priority_task_woken);
|
||||
|
||||
/// Set or clear TCP_NODELAY on a socket's tcp_pcb directly.
|
||||
/// Must be called with the TCPIP core lock held (LwIPLock in C++).
|
||||
/// This bypasses lwip_setsockopt() overhead (socket lookups, switch cascade,
|
||||
/// hooks, refcounting) — just a direct pcb->flags bit set/clear.
|
||||
/// Returns true if successful, false if sock/conn/pcb is NULL or the socket is not TCP.
|
||||
bool esphome_lwip_set_nodelay(struct lwip_sock *sock, bool enable);
|
||||
|
||||
/// Wake the main loop task from any context (ISR, thread, or main loop).
|
||||
/// ESP32-only: uses xPortInIsrContext() to detect ISR context.
|
||||
/// LibreTiny lacks IRAM_ATTR support needed for ISR-safe paths.
|
||||
|
||||
Reference in New Issue
Block a user