document unlocked hint read rationale

This commit is contained in:
J. Nick Koston
2026-03-01 15:34:55 -10:00
parent b86807a8ea
commit 50fb013fc5
+9 -5
View File
@@ -29,17 +29,21 @@ void esphome_lwip_fast_select_init(void);
/// Use this at registration time to cache the pointer for esphome_lwip_socket_has_data().
struct lwip_sock *esphome_lwip_get_sock(int fd);
/// Check if a cached LwIP socket has data ready via direct rcvevent read.
/// Check if a cached LwIP socket has data ready via unlocked hint read of rcvevent.
/// This avoids lwIP core lock contention between the main loop (CPU0) and
/// streaming/networking work (CPU1). Correctness is preserved because sockets are
/// nonblocking and EWOULDBLOCK is handled as normal — a stale hint simply means
/// we retry on the next loop iteration.
///
/// The sock pointer must have been obtained from esphome_lwip_get_sock() and must
/// remain valid (caller owns socket lifetime — no concurrent close).
/// Hot path: inlined volatile 16-bit load — no function call overhead.
/// Uses offset-based access because lwip/priv/sockets_priv.h conflicts with C++.
/// The offset is verified at compile time in lwip_fast_select.c.
/// The offset and size are verified at compile time in lwip_fast_select.c.
static inline bool esphome_lwip_socket_has_data(struct lwip_sock *sock) {
// Unlocked hint read — no lwIP core lock needed.
// volatile prevents the compiler from caching/reordering this cross-thread read.
// The write side (TCP/IP thread) commits via SYS_ARCH_UNPROTECT which releases a
// FreeRTOS mutex (ESP32) or resumes the scheduler (LibreTiny), ensuring the value
// is visible. Aligned 16-bit reads are single-instruction loads (L16SI/LH/LDRH) on
// Aligned 16-bit reads are single-instruction loads (L16SI/LH/LDRH) on
// Xtensa/RISC-V/ARM and cannot produce torn values.
return *(volatile int16_t *) ((char *) sock + (int) ESPHOME_LWIP_SOCK_RCVEVENT_OFFSET) > 0;
}