Commit Graph
23613 Commits
Author SHA1 Message Date
J. Nick Koston 28cec19380 Merge branch 'cache-lwip-sock-pointers' into integration 2026-03-01 14:32:36 -10:00
J. Nick Koston 710458161a [core] Cache lwip_sock pointers to simplify fast select hot path
Cache struct lwip_sock* pointers at socket registration time instead of
looking them up by fd on every iteration. The previous code stored file
descriptors and called lwip_socket_dbg_get_socket(fd) on every loop
iteration to convert each fd back to a pointer — only to read a single
field from it.

- register_socket(lwip_sock*) returns bool and hooks the callback
  internally — replaces the separate register_socket_fd + hook dance
- Hot loop iterates struct lwip_sock* directly instead of converting
  fds each iteration
- Socket::ready() uses the cached pointer instead of fd lookup
- esphome_lwip_hook_socket() takes struct lwip_sock* directly
- esphome_lwip_socket_has_data() moved to header as static inline
  for inlining — uses offset-based access verified by _Static_assert
- Null cached_sock_ on close to prevent post-close dereference
- socket_ready() tolerant of null pointer for safety
2026-03-01 14:32:28 -10:00
J. Nick Koston 9abb52f485 move socket_ready to header for inlining 2026-03-01 14:31:44 -10:00
J. Nick Koston 10ec8afbb2 Merge branch 'cache-lwip-sock-pointers' into integration
# Conflicts:
#	esphome/core/lwip_fast_select.h
2026-03-01 14:25:00 -10:00
J. Nick Koston 055ed0e751 [core] Cache lwip_sock pointers to simplify fast select hot path
Cache struct lwip_sock* pointers at socket registration time instead of
looking them up by fd on every iteration. The previous code stored file
descriptors and called lwip_socket_dbg_get_socket(fd) on every loop
iteration to convert each fd back to a pointer — only to read a single
field from it.

- register_socket(lwip_sock*) returns bool and hooks the callback
  internally — replaces the separate register_socket_fd + hook dance
- Hot loop iterates struct lwip_sock* directly instead of converting
  fds each iteration
- Socket::ready() uses the cached pointer instead of fd lookup
- esphome_lwip_hook_socket() takes struct lwip_sock* directly
- esphome_lwip_socket_has_data() moved to header as static inline
  for inlining — uses offset-based access verified by _Static_assert
- Null cached_sock_ on close to prevent post-close dereference
- socket_ready() tolerant of null pointer for safety
2026-03-01 14:24:30 -10:00
J. Nick Koston 760f6bc37d Merge branch 'cache-lwip-sock-pointers' into integration
# Conflicts:
#	esphome/core/lwip_fast_select.c
#	esphome/core/lwip_fast_select.h
2026-03-01 14:23:26 -10:00
J. Nick Koston b3b3f18b3b [core] Cache lwip_sock pointers to simplify fast select hot path
Cache struct lwip_sock* pointers at socket registration time instead of
looking them up by fd on every iteration. The previous code stored file
descriptors and called lwip_socket_dbg_get_socket(fd) on every loop
iteration to convert each fd back to a pointer — only to read a single
field from it.

- register_socket(lwip_sock*) returns bool and hooks the callback
  internally — replaces the separate register_socket_fd + hook dance
- Hot loop iterates struct lwip_sock* directly instead of converting
  fds each iteration
- Socket::ready() uses the cached pointer instead of fd lookup
- esphome_lwip_hook_socket() takes struct lwip_sock* directly
- esphome_lwip_socket_has_data() moved to header as static inline
  for inlining — uses offset-based access verified by _Static_assert
- Null cached_sock_ on close to prevent post-close dereference
- socket_ready() tolerant of null pointer for safety
2026-03-01 14:21:40 -10:00
J. Nick Koston 248061ab1f fix unterminated #ifdef USE_LWIP_FAST_SELECT in merge 2026-03-01 14:15:12 -10:00
J. Nick Koston fac328f2e0 Merge remote-tracking branch 'upstream/cache-lwip-sock-pointers' into integration 2026-03-01 14:13:21 -10:00
J. Nick Koston 9da23db21c [core] Cache lwip_sock pointers to simplify fast select hot path
Cache struct lwip_sock* pointers at socket registration time instead of
looking them up by fd on every iteration. The previous code stored file
descriptors and called lwip_socket_dbg_get_socket(fd) on every loop
iteration to convert each fd back to a pointer — only to read a single
field from it.

- register_socket(lwip_sock*) returns bool and hooks the callback
  internally — replaces the separate register_socket_fd + hook dance
- Hot loop iterates struct lwip_sock* directly instead of converting
  fds each iteration
- Socket::ready() uses the cached pointer instead of fd lookup
- esphome_lwip_hook_socket() takes struct lwip_sock* directly
- Null cached_sock_ on close to prevent post-close dereference
- socket_ready() tolerant of null pointer for safety
2026-03-01 14:12:41 -10:00
J. Nick Koston b987bcbec8 Merge branch 'cache-lwip-sock-pointers' into integration 2026-03-01 14:11:00 -10:00
J. Nick KostonandClaude Opus 4.6 f160a17617 restore removed comments in register/unregister_socket_fd
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:10:52 -10:00
J. Nick Koston 2aa750b5fc Merge branch 'cache-lwip-sock-pointers' into integration 2026-03-01 14:06:50 -10:00
J. Nick Koston 52cc24880c guards 2026-03-01 14:06:39 -10:00
J. Nick KostonandClaude Opus 4.6 bca53e35e1 fix merge conflict: remove stray #endif
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:04:08 -10:00
J. Nick Koston 0d54dc94ba Merge branch 'cache-lwip-sock-pointers' into integration
# Conflicts:
#	esphome/core/application.h
2026-03-01 13:59:55 -10:00
J. Nick Koston 9733c28c40 [core] Cache lwip_sock pointers to eliminate per-socket fd lookup in fast select hot path
In yield_with_select_(), the fast select path iterated all monitored
sockets and called esphome_lwip_socket_has_data(fd) for each one.
That C function called lwip_socket_dbg_get_socket(fd) — a cross-
compilation-unit call that can't be inlined because sockets[] is
static in lwip's sockets.c. The actual rcvevent check is ~4
instructions, but the lookup adds entry/retw window overhead on
Xtensa (~8-12 cycles per socket per loop iteration).

Now lwip_sock* pointers are cached at socket registration time and
stored directly in monitored_sockets_. The hot path iterates pointers
and reads rcvevent with no fd lookup. esphome_lwip_hook_socket() also
takes lwip_sock* directly, avoiding a redundant get_sock() call.

API split for fast select vs fallback select:
- register_socket(lwip_sock*) / unregister_socket(lwip_sock*)
- register_socket_fd(int fd) / unregister_socket_fd(int fd)

Chained on top of socket-devirtualize (#14398).
2026-03-01 13:38:25 -10:00
J. Nick Koston bb7d811865 Merge branch 'compile-time-has-overridden-loop' into integration 2026-03-01 12:12:07 -10:00
J. Nick Koston 836f403d53 reduce generated code 2026-03-01 12:11:44 -10:00
J. Nick Koston c876b95af5 Merge branch 'compile-time-has-overridden-loop' into integration
# Conflicts:
#	esphome/core/application.cpp
2026-03-01 12:08:14 -10:00
J. Nick Koston 78257db36a fix tests 2026-03-01 11:57:58 -10:00
J. Nick Koston 5f606a4154 [core] Pre-compute looping component count at compile time via codegen
Emit a static constexpr in main.cpp that uses std::is_same_v to count
components with overridden loop() at C++ compile time. Pre-init the
FixedVector with the exact capacity so calculate_looping_components_()
can skip its counting pass entirely.
2026-03-01 11:54:47 -10:00
J. Nick Koston 9621c5a3d3 [core] Compile-time detection of loop() overrides
Replace the runtime GCC-specific pointer-to-member-function comparison
in has_overridden_loop() with a compile-time std::is_same_v check in a
templated register_component_().

The template resolves &T::loop vs &Component::loop at compile time and
passes the result to register_component_impl_(), which stores it as
bit 5 in the existing component_state_ byte (zero additional RAM).

This eliminates:
- The non-standard -Wpmf-conversions GCC extension
- The USE_HOST/CLANG_TIDY special case that incorrectly returned true
  for all components (putting every component in the loop list)
- Runtime vtable probing during calculate_looping_components_()
2026-03-01 11:08:23 -10:00
J. Nick Koston a1760a1980 [improv_serial] Add missing USE_IMPROV_SERIAL define to fix WiFi scan filtering (#14359) 2026-03-02 09:23:10 +13:00
J. Nick Koston 3c599300c2 Merge branch 'socket-lwip-bugfix' into integration 2026-03-01 08:45:17 -10:00
J. Nick Koston 8c96ff8b61 [socket] Use tcp_close for listen PCB cleanup in destructor
tcp_abort()/tcp_abandon() asserts pcb->state != LISTEN and accesses
fields beyond tcp_pcb_listen. Use tcp_close() instead and null pcb_
so the base destructor skips tcp_abort.
2026-03-01 08:43:27 -10:00
J. Nick Koston 9151383f7d Merge remote-tracking branch 'upstream/socket-lwip-bugfix' into integration 2026-03-01 08:17:27 -10:00
J. Nick Koston 3246662a09 [socket] Remove invalid tcp_err on listen PCB
tcp_listen_with_backlog() converts the full tcp_pcb to a smaller
tcp_pcb_listen struct that lacks the errf field. Calling tcp_err()
on a listen PCB writes past the struct boundary, causing memory
corruption and crashes (confirmed on RP2040).

LWIP itself asserts pcb->state != LISTEN in tcp_err().
2026-03-01 08:16:23 -10:00
J. Nick Koston 6564c46c79 Merge branch 'socket-lwip-bugfix' into integration 2026-03-01 07:51:40 -10:00
J. Nick Koston d1b1090f93 [socket] Fix send() const-correctness to match POSIX
send() takes void* but should be const void* to match POSIX and
the class's own write()/sendto() signatures.
2026-03-01 07:46:32 -10:00
J. Nick Koston fe89d7c701 [socket] Free rx_buf_ in LWIPRawImpl destructor
The destructor relies solely on tcp_abort() for cleanup, but LWIP
considers ownership of pbufs transferred once the recv callback
accepts them. If rx_buf_ is non-null when the socket is destroyed,
those pbufs are leaked. Free them explicitly before the base class
destructor calls tcp_abort().
2026-03-01 07:41:38 -10:00
J. Nick Koston cce95ff58c [socket] Fix pre-existing bugs in LWIP raw TCP socket
Fix two pre-existing bugs found during review of #14398:

1. bind() returns 0 on nullptr name instead of -1, inconsistent with
   POSIX bind() semantics and can mask invalid calls.

2. listen() doesn't re-register tcp_err after tcp_listen_with_backlog
   reallocates the PCB. The error callback was set during init() on
   the original PCB which is now freed, leaving the new listen PCB
   without an error handler.
2026-03-01 07:22:27 -10:00
J. Nick Koston 5d9c3a0e71 Fix clang-tidy: use make_unique and fix include order
- Replace std::unique_ptr{new ...} with make_unique
- Fix include order to satisfy clang-format
2026-03-01 00:23:05 -10:00
J. Nick Koston 529c79a8a9 Add NOLINT for redundant socket_ready_fd forward declaration
clang-tidy flags the forward declaration in application.h as redundant
since socket.h (included earlier via esphome.h) already declares it.
The forward declaration is needed here for the friend declaration.
2026-02-28 23:30:35 -10:00
J. Nick Koston 8cedf8f882 Restore comments from original code to minimize diff 2026-02-28 23:03:53 -10:00
J. Nick Koston 164001b50c Revert unnecessary socket_ip_loop_monitored rename
Keep the original function name since all callers already use it for
listening sockets. Only the return type changes to ListenSocket.
2026-02-28 23:00:32 -10:00
J. Nick Koston 7e119b1cbd Fix clang-tidy naming violations in lwip_raw_tcp_impl
- Rename internal_write/internal_output to internal_write_/internal_output_
  (protected methods require trailing underscore)
- Fix getpeername/getsockname parameter name mismatch between .h and .cpp
2026-02-28 22:56:48 -10:00
J. Nick Koston 39bbdbbfce Revert unrelated IRAM_ATTR change that leaked in from stash 2026-02-28 22:47:32 -10:00
J. Nick KostonandJ. Nick Koston 7a2452b3a2 Move LWIP_LOG and inline methods from header to cpp
The CI lint rule disallows ESP_LOG* references in header files.
Move LWIP_LOG macro definition and all methods that use it
(destructors, init(), static callbacks) to the .cpp file.

Co-Authored-By: J. Nick Koston <nick@koston.org>
2026-02-28 22:43:24 -10:00
J. Nick KostonandJ. Nick Koston 807e786fac [socket] Devirtualize socket abstraction layer
Replace the virtual Socket base class with compile-time type aliases.
Since only one socket implementation is active per build (selected via
#ifdef), virtual dispatch was unnecessary overhead.

Each implementation (BSDSocketImpl, LwIPSocketImpl, LWIPRawImpl) is now
a concrete class in its own header with no virtual methods. socket::Socket
and socket::ListenSocket are type aliases to the active implementation.

For LWIP_TCP (ESP8266/RP2040), the listen socket is a separate type
(LWIPRawListenImpl) sharing a non-virtual LWIPRawCommon base with the
connected socket type (LWIPRawImpl). On BSD/LWIP_SOCKETS, both aliases
resolve to the same type.

This eliminates all vtable overhead, virtual destructors, and indirect
call overhead across all platforms.

Co-Authored-By: J. Nick Koston <nick@koston.org>
2026-02-28 22:39:25 -10:00
J. Nick Koston 7b645ef736 Merge branch 'controller-registry-shared-notify' into integration 2026-02-28 21:42:30 -10:00
J. Nick Koston 8e4c54cc38 lint 2026-02-28 21:42:18 -10:00
J. Nick Koston 4799b217a5 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-28 17:31:51 -10:00
J. Nick Koston 68818a535c Merge branch 'controller-registry-shared-notify' into integration 2026-02-28 17:10:46 -10:00
J. Nick Koston b4e0c917eb bot comments 2026-02-28 17:10:36 -10:00
J. Nick Koston 8c45791eeb Merge branch 'controller-registry-shared-notify' into integration 2026-02-28 17:08:39 -10:00
J. Nick Koston 19bbd39e33 [uart] Enable wake-on-RX by default on ESP32 (#14391) 2026-02-28 21:06:46 -06:00
J. Nick Koston 8f24e12bf9 fix merge 2026-02-28 16:50:50 -10:00
J. Nick Koston f3377e22b8 fix mege 2026-02-28 16:47:54 -10:00
J. Nick Koston c2a6295b37 Merge remote-tracking branch 'upstream/remove-virtual-destructor-proto-message' into integration 2026-02-28 16:40:48 -10:00