@@ -150,35 +150,53 @@ namespace ipsockets {
150150 socklen_t addr_len = sizeof (sockaddr_in_t );
151151 sockaddr_in_t addr_from = {};
152152
153- #ifdef _WIN32 // WINDOWS OS
154- // for windows ::accept will never be timeout and we should close sock connection to interrupt it or we should using poll()
155- DWORD tv_ms = 1000 ; // get SO_RCVTIMEO if set (DWORD on Windows), fallback to 1000 ms
156- int optlen = sizeof (tv_ms);
157- if (getsockopt (this ->sock , SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_ms, &optlen) == SOCKET_ERROR)
158- tv_ms = 1000 ;
159-
160- WSAPOLLFD pfd;
161- pfd.fd = this ->sock ;
162- pfd.events = POLLRDNORM; // wait incoming data
163-
164- int rv = WSAPoll (&pfd, 1 , (int )tv_ms);
165- if (rv == 0 ) { // timeout
153+ // Use poll() to wait for incoming connections with a timeout.
154+ // On Windows ::accept() does not respect SO_RCVTIMEO, and on some
155+ // Linux versions SO_RCVTIMEO may not interrupt accept(). poll() solves
156+ // both issues and allows graceful interruption via close() from another
157+ // thread (poll returns POLLNVAL or an error on a closed fd).
158+ {
159+ int tv_ms = 1000 ;
160+ #ifdef _WIN32 // WINDOWS OS
161+ DWORD tv_dword = 1000 ;
162+ int optlen = sizeof (tv_dword);
163+ if (getsockopt (this ->sock , SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_dword, &optlen) != SOCKET_ERROR)
164+ tv_ms = (int )tv_dword;
165+
166+ WSAPOLLFD pfd;
167+ pfd.fd = this ->sock ;
168+ pfd.events = POLLRDNORM;
169+ int rv = WSAPoll (&pfd, 1 , tv_ms);
170+ #else // LINUX OS
171+ timeval tv_val = {};
172+ socklen_t tv_len = sizeof (tv_val);
173+ if (getsockopt (this ->sock , SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_val, &tv_len) != SOCKET_ERROR)
174+ tv_ms = (int )(tv_val.tv_sec * 1000 + tv_val.tv_usec / 1000 );
175+ if (tv_ms <= 0 ) tv_ms = 1000 ;
176+
177+ pollfd pfd;
178+ pfd.fd = this ->sock ;
179+ pfd.events = POLLIN;
180+ int rv = poll (&pfd, 1 , tv_ms);
181+ #endif
182+
183+ if (rv == 0 ) {
166184 result.sock = INVALID_SOCKET;
167185 cerr = error_timeout;
168186 }
169- else if (pfd.revents & POLLRDNORM) { // data ready
187+ else if (rv > 0 && (pfd.revents & (POLLIN
188+ #ifdef _WIN32
189+ | POLLRDNORM
190+ #endif
191+ ))) {
170192 result.sock = ::accept (this ->sock , (sockaddr*)&addr_from, &addr_len);
171193 cerr = this ->_get_err ();
172194 }
173- else { // error
195+ else {
174196 result.sock = INVALID_SOCKET;
175197 cerr = this ->_get_err ();
176198 }
177- #else // LINUX OS
178- // for linux ::accept will be timeout by value SO_RCVTIMEO wich we set by setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof (tv));
179- result.sock = ::accept (this ->sock , (sockaddr*)&addr_from, &addr_len);
180- cerr = this ->_get_err ();
181- #endif
199+ }
182200
183201 if (result.sock == INVALID_SOCKET) {
184202 this ->log_and_return (' -' , " accept" , cerr);
0 commit comments