Skip to content

Commit 12e643a

Browse files
committed
CDRIVER-2081 use select() on Windows
WSAPoll doesn't detect connection resets, and forces us to wait for a timeout on Windows even when the server actively refused the connection. Use select() instead: it detects resets, and on Windows doesn't have the maximum file descriptor limit it has on Unix. daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken msdn.microsoft.com/en-us/library/windows/desktop/ms740142.aspx
1 parent b48a0da commit 12e643a

File tree

2 files changed

+76
-30
lines changed

2 files changed

+76
-30
lines changed

src/mongoc/mongoc-socket.c

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ _mongoc_socket_wait (int sd, /* IN */
108108
int64_t expire_at) /* IN */
109109
{
110110
#ifdef _WIN32
111-
WSAPOLLFD pfd;
111+
fd_set read_fds;
112+
fd_set write_fds;
113+
fd_set error_fds;
114+
struct timeval timeout_tv;
112115
#else
113116
struct pollfd pfd;
114117
#endif
@@ -120,14 +123,25 @@ _mongoc_socket_wait (int sd, /* IN */
120123

121124
BSON_ASSERT (events);
122125

123-
pfd.fd = sd;
124126
#ifdef _WIN32
125-
pfd.events = events;
127+
FD_ZERO (&read_fds);
128+
FD_ZERO (&write_fds);
129+
FD_ZERO (&error_fds);
130+
131+
if (events & POLLIN) {
132+
FD_SET (sd, &read_fds);
133+
}
134+
135+
if (events & POLLOUT) {
136+
FD_SET (sd, &write_fds);
137+
}
138+
139+
FD_SET (sd, &error_fds);
126140
#else
141+
pfd.fd = sd;
127142
pfd.events = events | POLLERR | POLLHUP;
128-
#endif
129143
pfd.revents = 0;
130-
144+
#endif
131145
now = bson_get_monotonic_time ();
132146

133147
for (;;) {
@@ -143,10 +157,21 @@ _mongoc_socket_wait (int sd, /* IN */
143157
}
144158

145159
#ifdef _WIN32
146-
ret = WSAPoll (&pfd, 1, timeout);
160+
if (timeout == -1) {
161+
/* not WSAPoll: daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken */
162+
ret = select (0 /*unused*/, &read_fds, &write_fds, &error_fds, NULL);
163+
} else {
164+
timeout_tv.tv_sec = timeout / 1000;
165+
timeout_tv.tv_usec = (timeout % 1000) * 1000;
166+
ret = select (
167+
0 /*unused*/, &read_fds, &write_fds, &error_fds, &timeout_tv);
168+
}
147169
if (ret == SOCKET_ERROR) {
148170
errno = WSAGetLastError ();
149171
ret = -1;
172+
} else if (FD_ISSET (sd, &error_fds)) {
173+
errno = WSAECONNRESET;
174+
ret = -1;
150175
}
151176
#else
152177
ret = poll (&pfd, 1, timeout);
@@ -155,7 +180,7 @@ _mongoc_socket_wait (int sd, /* IN */
155180
if (ret > 0) {
156181
/* Something happened, so return that */
157182
#ifdef _WIN32
158-
RETURN (0 != (pfd.revents & (events | POLLHUP | POLLERR)));
183+
return (FD_ISSET (sd, &read_fds) || FD_ISSET (sd, &write_fds));
159184
#else
160185
RETURN (0 != (pfd.revents & events));
161186
#endif
@@ -176,7 +201,7 @@ _mongoc_socket_wait (int sd, /* IN */
176201
RETURN (false);
177202
}
178203
} else {
179-
/* poll timed out */
204+
/* ret == 0, poll timed out */
180205
RETURN (false);
181206
}
182207
}
@@ -210,7 +235,10 @@ mongoc_socket_poll (mongoc_socket_poll_t *sds, /* IN */
210235
int32_t timeout) /* IN */
211236
{
212237
#ifdef _WIN32
213-
WSAPOLLFD *pfds;
238+
fd_set read_fds;
239+
fd_set write_fds;
240+
fd_set error_fds;
241+
struct timeval timeout_tv;
214242
#else
215243
struct pollfd *pfds;
216244
#endif
@@ -222,36 +250,59 @@ mongoc_socket_poll (mongoc_socket_poll_t *sds, /* IN */
222250
BSON_ASSERT (sds);
223251

224252
#ifdef _WIN32
225-
pfds = (WSAPOLLFD *) bson_malloc (sizeof (*pfds) * nsds);
253+
FD_ZERO (&read_fds);
254+
FD_ZERO (&write_fds);
255+
FD_ZERO (&error_fds);
256+
257+
for (i = 0; i < nsds; i++) {
258+
if (sds[i].events & POLLIN) {
259+
FD_SET (sds[i].socket->sd, &read_fds);
260+
}
261+
262+
if (sds[i].events & POLLOUT) {
263+
FD_SET (sds[i].socket->sd, &write_fds);
264+
}
265+
266+
FD_SET (sds[i].socket->sd, &error_fds);
267+
}
268+
269+
timeout_tv.tv_sec = timeout / 1000;
270+
timeout_tv.tv_usec = (timeout % 1000) * 1000;
271+
272+
/* not WSAPoll: daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken */
273+
ret = select (0 /*unused*/, &read_fds, &write_fds, &error_fds, &timeout_tv);
274+
if (ret == SOCKET_ERROR) {
275+
errno = WSAGetLastError ();
276+
return -1;
277+
}
278+
279+
for (i = 0; i < nsds; i++) {
280+
if (FD_ISSET (sds[i].socket->sd, &read_fds)) {
281+
sds[i].revents = POLLIN;
282+
} else if (FD_ISSET (sds[i].socket->sd, &write_fds)) {
283+
sds[i].revents = POLLOUT;
284+
} else if (FD_ISSET (sds[i].socket->sd, &error_fds)) {
285+
sds[i].revents = POLLHUP;
286+
} else {
287+
sds[i].revents = 0;
288+
}
289+
}
226290
#else
227291
pfds = (struct pollfd *) bson_malloc (sizeof (*pfds) * nsds);
228-
#endif
229292

230293
for (i = 0; i < nsds; i++) {
231294
pfds[i].fd = sds[i].socket->sd;
232-
#ifdef _WIN32
233-
pfds[i].events = sds[i].events;
234-
#else
235295
pfds[i].events = sds[i].events | POLLERR | POLLHUP;
236-
#endif
237296
pfds[i].revents = 0;
238297
}
239298

240-
#ifdef _WIN32
241-
ret = WSAPoll (pfds, nsds, timeout);
242-
if (ret == SOCKET_ERROR) {
243-
MONGOC_WARNING ("WSAGetLastError(): %d", WSAGetLastError ());
244-
ret = -1;
245-
}
246-
#else
247299
ret = poll (pfds, nsds, timeout);
248-
#endif
249-
250300
for (i = 0; i < nsds; i++) {
251301
sds[i].revents = pfds[i].revents;
252302
}
253303

254304
bson_free (pfds);
305+
#endif
255306

256307
return ret;
257308
}

tests/test-mongoc-topology-scanner.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,16 +281,14 @@ test_topology_scanner_oscillate (void)
281281
}
282282

283283

284-
/* skip on Windows: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/ */
285-
#ifndef _WIN32
286284
void
287285
test_topology_scanner_connection_error (void)
288286
{
289287
mongoc_client_t *client;
290288
bson_error_t error;
291289

292290
/* assuming nothing is listening on this port */
293-
client = mongoc_client_new ("mongodb://localhost:9876/?connectTimeoutMS=10");
291+
client = mongoc_client_new ("mongodb://localhost:9876");
294292

295293
ASSERT (!mongoc_client_command_simple (
296294
client, "db", tmp_bson ("{'foo': 1}"), NULL, NULL, &error));
@@ -303,7 +301,6 @@ test_topology_scanner_connection_error (void)
303301

304302
mongoc_client_destroy (client);
305303
}
306-
#endif
307304

308305

309306
void
@@ -414,11 +411,9 @@ test_topology_scanner_install (TestSuite *suite)
414411
suite, "/TOPOLOGY/scanner_discovery", test_topology_scanner_discovery);
415412
TestSuite_AddMockServerTest (
416413
suite, "/TOPOLOGY/scanner_oscillate", test_topology_scanner_oscillate);
417-
#ifndef _WIN32
418414
TestSuite_Add (suite,
419415
"/TOPOLOGY/scanner_connection_error",
420416
test_topology_scanner_connection_error);
421-
#endif
422417
TestSuite_AddMockServerTest (suite,
423418
"/TOPOLOGY/scanner_socket_timeout",
424419
test_topology_scanner_socket_timeout);

0 commit comments

Comments
 (0)