Co-authored-by: Jonathan Swoboda <swoboda1337@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
On ESP32 with CONFIG_LWIP_TCPIP_CORE_LOCKING, bypass lwip_setsockopt()
for TCP_NODELAY by directly modifying tcp_pcb->flags under the TCPIP
core lock. This eliminates ~1091 bytes of overhead per call (socket
lookups, hook, switch cascade, refcounting) for what is just a single
bit flip.
The API frame helper toggles Nagle's algorithm on every message send
via set_nodelay_for_message(), making this a hot path. The fast path
reduces set_nodelay_raw_ from calling the full lwip_setsockopt to just
acquiring the mutex, loading 3 pointers, and flipping the TF_NODELAY
bit.
Only enabled when both USE_LWIP_FAST_SELECT (cached lwip_sock pointer)
and CONFIG_LWIP_TCPIP_CORE_LOCKING (real mutex protection) are
available. Falls back to the standard setsockopt call otherwise.
Defensively free pb when err != ERR_OK but pb != nullptr in both
recv_fn and s_queued_recv_fn. In practice lwip never sends data
with an error code, but this prevents a leak if it ever does.
Also add comment noting tcp_recved is deferred to read().
Defensively free pb when err != ERR_OK but pb != nullptr in both
recv_fn and s_queued_recv_fn. In practice lwip never sends data
with an error code, but this prevents a leak if it ever does.
Also add comment noting tcp_recved is deferred to read().
Consolidate the dequeue + shift + tcp_arg update into a single while
loop that skips null entries (freed by lwip while queued) and returns
the first valid PCB. Eliminates the duplicated shift/update logic.
Consolidate the dequeue + shift + tcp_arg update into a single while
loop that skips null entries (freed by lwip while queued) and returns
the first valid PCB. Eliminates the duplicated shift/update logic.
When LWIPRawImpl creation was deferred to the main-loop accept(),
the queued PCB had no recv callback registered. lwip's default
tcp_recv_null handler ACKs incoming data but drops it silently.
On ESP8266, lwip processes TCP segments in batches — if the SYN
completion and first data packet arrive in the same batch, the
API handshake data is lost, causing SocketClosedAPIError (EOF).
Fix: register temporary recv/err callbacks on queued PCBs that
buffer any data received before accept() creates the LWIPRawImpl.
The buffered data is transferred to the new socket via init().
The LWIPRawImpl object is only 20 bytes — no reason to heap-allocate it
in the callback on ESP8266 either. Store raw tcp_pcb pointers on both
platforms, removing the #ifdef branches and simplifying the code.