Loading src/posix/socket.cpp +25 −0 Changes for src/posix/socket.cpp: 25 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <sys/socket.h> #include <poll.h> namespace netplus { Loading Loading @@ -69,6 +70,30 @@ void netplus::socket::close() { } while (rc < 0 && errno == EINTR); } bool netplus::socket::waitReadable(int timeoutMs) { if (_Socket < 0) return false; struct pollfd pfd; pfd.fd = _Socket; pfd.events = POLLIN; pfd.revents = 0; int r; do { r = ::poll(&pfd, 1, timeoutMs); } while (r < 0 && errno == EINTR); if (r <= 0) return false; /* timeout or poll() error */ if (pfd.revents & POLLIN) return true; /* real data waiting */ if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { /* Error-only wakeup (e.g. a latched ICMP error on an unconnected UDP * socket): drain it so poll() blocks again instead of spinning. */ int err = 0; socklen_t el = sizeof(err); ::getsockopt(_Socket, SOL_SOCKET, SO_ERROR, &err, &el); } return false; } void socket::setFlag(int flag, int value) { int sockopts; do { Loading src/socket.h +17 −0 Changes for src/socket.h: 17 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -261,6 +261,23 @@ namespace netplus { SOCKET fd(); // Block until this socket has data to read (POLLIN) or timeoutMs // elapses; timeoutMs < 0 blocks indefinitely. Returns true ONLY when // there is data to read. Non-blocking batch receivers (udp // recvBatchAddr, non-blocking accept) never wait themselves, so a // receive loop must gate on this or it spins a core at 100%. // // poll()/WSAPoll() report POLLERR/POLLHUP/POLLNVAL in revents even // though we only ask for POLLIN. A latched socket error — e.g. an // ICMP port-unreachable on an unconnected UDP socket after a sendto — // makes poll() return immediately with POLLERR forever while a // non-blocking recv keeps yielding EAGAIN without clearing it; a // naive "poll() > 0" readiness test then busy-loops. So this gates on // POLLIN and, on an error-only wakeup, drains the pending error via // SO_ERROR (which resets sk_err) so the next call blocks again. // Platform-neutral: poll() on POSIX, WSAPoll() on Windows. bool waitReadable(int timeoutMs); virtual socket& operator=(SOCKET sock) = 0; virtual void close(); virtual void flush_out() = 0; Loading src/windows/socket.cpp +26 −0 Changes for src/windows/socket.cpp: 26 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -121,6 +121,32 @@ namespace netplus { return _Socket; } // ------------------------------------------------------------------------- // socket::waitReadable (see socket.h for the full contract / rationale) // Platform-neutral readiness gate; Windows uses WSAPoll(). // ------------------------------------------------------------------------- bool socket::waitReadable(int timeoutMs) { if (_Socket == INVALID_SOCKET) return false; WSAPOLLFD pfd; pfd.fd = _Socket; pfd.events = POLLRDNORM; pfd.revents = 0; int r = ::WSAPoll(&pfd, 1, timeoutMs); if (r <= 0) return false; // timeout or WSAPoll() error if (pfd.revents & POLLRDNORM) return true; // real data waiting if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { // Error-only wakeup: drain the latched error so the next call // blocks again instead of spinning. int err = 0; int el = sizeof(err); ::getsockopt(_Socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &el); } return false; } // ------------------------------------------------------------------------- // socket::setFlag // Windows: only useful flag is nonblocking (FIONBIO) Loading Loading
src/posix/socket.cpp +25 −0 Changes for src/posix/socket.cpp: 25 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <sys/socket.h> #include <poll.h> namespace netplus { Loading Loading @@ -69,6 +70,30 @@ void netplus::socket::close() { } while (rc < 0 && errno == EINTR); } bool netplus::socket::waitReadable(int timeoutMs) { if (_Socket < 0) return false; struct pollfd pfd; pfd.fd = _Socket; pfd.events = POLLIN; pfd.revents = 0; int r; do { r = ::poll(&pfd, 1, timeoutMs); } while (r < 0 && errno == EINTR); if (r <= 0) return false; /* timeout or poll() error */ if (pfd.revents & POLLIN) return true; /* real data waiting */ if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { /* Error-only wakeup (e.g. a latched ICMP error on an unconnected UDP * socket): drain it so poll() blocks again instead of spinning. */ int err = 0; socklen_t el = sizeof(err); ::getsockopt(_Socket, SOL_SOCKET, SO_ERROR, &err, &el); } return false; } void socket::setFlag(int flag, int value) { int sockopts; do { Loading
src/socket.h +17 −0 Changes for src/socket.h: 17 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -261,6 +261,23 @@ namespace netplus { SOCKET fd(); // Block until this socket has data to read (POLLIN) or timeoutMs // elapses; timeoutMs < 0 blocks indefinitely. Returns true ONLY when // there is data to read. Non-blocking batch receivers (udp // recvBatchAddr, non-blocking accept) never wait themselves, so a // receive loop must gate on this or it spins a core at 100%. // // poll()/WSAPoll() report POLLERR/POLLHUP/POLLNVAL in revents even // though we only ask for POLLIN. A latched socket error — e.g. an // ICMP port-unreachable on an unconnected UDP socket after a sendto — // makes poll() return immediately with POLLERR forever while a // non-blocking recv keeps yielding EAGAIN without clearing it; a // naive "poll() > 0" readiness test then busy-loops. So this gates on // POLLIN and, on an error-only wakeup, drains the pending error via // SO_ERROR (which resets sk_err) so the next call blocks again. // Platform-neutral: poll() on POSIX, WSAPoll() on Windows. bool waitReadable(int timeoutMs); virtual socket& operator=(SOCKET sock) = 0; virtual void close(); virtual void flush_out() = 0; Loading
src/windows/socket.cpp +26 −0 Changes for src/windows/socket.cpp: 26 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -121,6 +121,32 @@ namespace netplus { return _Socket; } // ------------------------------------------------------------------------- // socket::waitReadable (see socket.h for the full contract / rationale) // Platform-neutral readiness gate; Windows uses WSAPoll(). // ------------------------------------------------------------------------- bool socket::waitReadable(int timeoutMs) { if (_Socket == INVALID_SOCKET) return false; WSAPOLLFD pfd; pfd.fd = _Socket; pfd.events = POLLRDNORM; pfd.revents = 0; int r = ::WSAPoll(&pfd, 1, timeoutMs); if (r <= 0) return false; // timeout or WSAPoll() error if (pfd.revents & POLLRDNORM) return true; // real data waiting if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { // Error-only wakeup: drain the latched error so the next call // blocks again instead of spinning. int err = 0; int el = sizeof(err); ::getsockopt(_Socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &el); } return false; } // ------------------------------------------------------------------------- // socket::setFlag // Windows: only useful flag is nonblocking (FIONBIO) Loading