[socket] Refactor accept() to skip null entries without duplicate code

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.
This commit is contained in:
J. Nick Koston
2026-03-10 14:51:09 -10:00
parent acbfe9838d
commit fab32d9e7b
+37 -39
View File
@@ -745,48 +745,46 @@ std::unique_ptr<LWIPRawImpl> LWIPRawListenImpl::accept(struct sockaddr *addr, so
errno = EBADF;
return nullptr;
}
if (this->accepted_socket_count_ == 0) {
errno = EWOULDBLOCK;
return nullptr;
}
// Take entry from front of queue
QueuedPcb entry = this->accepted_pcbs_[0];
// Shift remaining entries forward
for (uint8_t i = 1; i < this->accepted_socket_count_; i++) {
this->accepted_pcbs_[i - 1] = this->accepted_pcbs_[i];
}
this->accepted_pcbs_[this->accepted_socket_count_ - 1] = {};
this->accepted_socket_count_--;
// Update tcp_arg for remaining queued PCBs — their array slots shifted by one.
// Safe because we hold LWIP_LOCK, so err/recv callbacks can't fire during the update.
for (uint8_t i = 0; i < this->accepted_socket_count_; i++) {
if (this->accepted_pcbs_[i].pcb != nullptr) {
tcp_arg(this->accepted_pcbs_[i].pcb, &this->accepted_pcbs_[i]);
// Dequeue front entry, then skip any null entries (PCBs freed by lwip while queued).
// The error callback nulled their pcb pointers; clean up buffered data and discard.
while (this->accepted_socket_count_ > 0) {
QueuedPcb entry = this->accepted_pcbs_[0];
// Shift remaining entries forward and update tcp_arg pointers (slots shifted by one).
// Safe because we hold LWIP_LOCK, so err/recv callbacks can't fire during the update.
for (uint8_t i = 1; i < this->accepted_socket_count_; i++) {
this->accepted_pcbs_[i - 1] = this->accepted_pcbs_[i];
}
}
LWIP_LOG("Connection accepted by application, queue size: %d", this->accepted_socket_count_);
if (entry.pcb == nullptr) {
// PCB was freed by lwip (RST/timeout) while queued — the temporary error callback
// nulled our pointer. Free any buffered data and return EWOULDBLOCK.
if (entry.rx_buf != nullptr) {
pbuf_free(entry.rx_buf);
this->accepted_pcbs_[this->accepted_socket_count_ - 1] = {};
this->accepted_socket_count_--;
for (uint8_t i = 0; i < this->accepted_socket_count_; i++) {
if (this->accepted_pcbs_[i].pcb != nullptr) {
tcp_arg(this->accepted_pcbs_[i].pcb, &this->accepted_pcbs_[i]);
}
}
errno = EWOULDBLOCK;
return nullptr;
if (entry.pcb == nullptr) {
// PCB was freed by lwip (RST/timeout) while queued — discard and try next
if (entry.rx_buf != nullptr) {
pbuf_free(entry.rx_buf);
}
continue;
}
LWIP_LOG("Connection accepted by application, queue size: %d", this->accepted_socket_count_);
// Create socket wrapper on the main loop (not in accept callback) to avoid
// heap allocation in IRQ context on RP2040. Transfer any data received while queued.
auto sock = make_unique<LWIPRawImpl>(this->family_, entry.pcb);
sock->init(entry.rx_buf);
if (entry.rx_closed) {
// Remote closed while queued — mark so read() returns EOF after buffered data
sock->rx_closed_ = true;
}
if (addr != nullptr) {
sock->getpeername(addr, addrlen);
}
LWIP_LOG("accept(%p)", sock.get());
return sock;
}
// Create socket wrapper on the main loop (not in accept callback) to avoid
// heap allocation in IRQ context on RP2040. Transfer any data received while queued.
auto sock = make_unique<LWIPRawImpl>(this->family_, entry.pcb);
sock->init(entry.rx_buf);
if (entry.rx_closed) {
// Remote closed while queued — mark so read() returns EOF after buffered data
sock->rx_closed_ = true;
}
if (addr != nullptr) {
sock->getpeername(addr, addrlen);
}
LWIP_LOG("accept(%p)", sock.get());
return sock;
errno = EWOULDBLOCK;
return nullptr;
}
int LWIPRawListenImpl::listen(int backlog) {