[socket] Eliminate closed_ and loop_monitored_ redundancy

Replace the separate closed_ bool with fd_ < 0 as the 'not open'
sentinel. close() now sets fd_ = -1 after the underlying close call,
so the destructor and double-close paths just check fd_ < 0. As a
side benefit, get_fd() on a closed socket now returns -1, making
use-after-close visible to callers instead of returning a stale
descriptor.

Drop loop_monitored_ on the USE_LWIP_FAST_SELECT path — the pointer
cached_sock_ already encodes monitoring state (non-null iff
monitored). On USE_HOST the bool is still needed because there is no
cached pointer to derive from.

Combined effect on the fast-select path:

  Before:  fd_(4) + cached_sock_(4) + closed_(1) + loop_monitored_(1)
           + pad(2) = 12 bytes per socket
  After:   fd_(4) + cached_sock_(4)
           = 8 bytes per socket (aligned, no tail padding)

Saves 4 bytes per Socket instance on ESP32/LibreTiny. With typical
workloads running 5-10 sockets (API listen + clients + mDNS) that's
20-40 bytes of RAM.
This commit is contained in:
J. Nick Koston
2026-04-10 21:05:21 -10:00
parent 107915fe36
commit b9541cd5db
5 changed files with 48 additions and 45 deletions
+14 -19
View File
@@ -15,33 +15,28 @@ BSDSocketImpl::BSDSocketImpl(int fd, bool monitor_loop) {
return;
#ifdef USE_LWIP_FAST_SELECT
this->cached_sock_ = fast_select_hook_fd(this->fd_);
this->loop_monitored_ = this->cached_sock_ != nullptr;
#else
this->loop_monitored_ = App.register_socket_fd(this->fd_);
#endif
}
BSDSocketImpl::~BSDSocketImpl() {
if (!this->closed_) {
this->close();
}
}
BSDSocketImpl::~BSDSocketImpl() { this->close(); }
int BSDSocketImpl::close() {
if (!this->closed_) {
#ifndef USE_LWIP_FAST_SELECT
// All LwIP sockets share the same static event_callback, so on the fast-select path
// there is no per-socket unhook needed. cached_sock_ is not cleared because closed_
// makes the socket a corpse — no ready() or other member access is valid afterwards.
if (this->loop_monitored_) {
App.unregister_socket_fd(this->fd_);
}
#endif
int ret = ::close(this->fd_);
this->closed_ = true;
return ret;
if (this->fd_ < 0) {
// Already closed, or never opened.
return 0;
}
return 0;
#ifndef USE_LWIP_FAST_SELECT
// On the fast-select path there is no per-socket unhook needed — all LwIP sockets
// share the same static event_callback.
if (this->loop_monitored_) {
App.unregister_socket_fd(this->fd_);
}
#endif
int ret = ::close(this->fd_);
this->fd_ = -1; // Sentinel for "closed" — prevents double-close and makes use-after-close visible.
return ret;
}
int BSDSocketImpl::setblocking(bool blocking) {