blob: 0033f01f987135e622a6d335c128dd97d400d6cf [file] [log] [blame]
Ben Noordhuis039fac62012-05-17 05:13:291/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22#include <errno.h>
23
24#ifndef _WIN32
25# include <fcntl.h>
26# include <sys/socket.h>
27# include <unistd.h>
28#endif
29
30#include "uv.h"
31#include "task.h"
32
33
34#define NUM_CLIENTS 5
35#define TRANSFER_BYTES (1 << 16)
36
37#undef MIN
38#define MIN(a, b) (((a) < (b)) ? (a) : (b));
39
40
41typedef enum {
42 UNIDIRECTIONAL,
43 DUPLEX
44} test_mode_t;
45
46typedef struct connection_context_s {
47 uv_poll_t poll_handle;
48 uv_timer_t timer_handle;
49 uv_os_sock_t sock;
50 size_t read, sent;
51 int is_server_connection;
52 int open_handles;
53 int got_fin, sent_fin;
54 unsigned int events, delayed_events;
55} connection_context_t;
56
57typedef struct server_context_s {
58 uv_poll_t poll_handle;
59 uv_os_sock_t sock;
60 int connections;
61} server_context_t;
62
63
64static void delay_timer_cb(uv_timer_t* timer, int status);
65
66
67static test_mode_t test_mode = DUPLEX;
68
69static int closed_connections = 0;
70
71static int valid_writable_wakeups = 0;
72static int spurious_writable_wakeups = 0;
73
74
75static int got_eagain() {
76#ifdef _WIN32
77 return WSAGetLastError() == WSAEWOULDBLOCK;
78#else
79 return errno == EAGAIN
80 || errno == EINPROGRESS
81#ifdef EWOULDBLOCK
82 || errno == EWOULDBLOCK;
83#endif
84 ;
85#endif
86}
87
88
89static void set_nonblocking(uv_os_sock_t sock) {
90 int r;
91#ifdef _WIN32
92 unsigned long on = 1;
93 r = ioctlsocket(sock, FIONBIO, &on);
94 ASSERT(r == 0);
95#else
96 int flags = fcntl(sock, F_GETFL, 0);
97 ASSERT(flags >= 0);
98 r = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
99 ASSERT(r >= 0);
100#endif
101}
102
103
104static uv_os_sock_t create_nonblocking_bound_socket(
105 struct sockaddr_in bind_addr) {
106 uv_os_sock_t sock;
107 int r;
108
109 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
110#ifdef _WIN32
111 ASSERT(sock != INVALID_SOCKET);
112#else
113 ASSERT(sock >= 0);
114#endif
115
116 set_nonblocking(sock);
117
118#ifndef _WIN32
119 {
120 /* Allow reuse of the port. */
121 int yes = 1;
122 r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
123 ASSERT(r == 0);
124 }
125#endif
126
127 r = bind(sock, (const struct sockaddr*) &bind_addr, sizeof bind_addr);
128 ASSERT(r == 0);
129
130 return sock;
131}
132
133
134static void close_socket(uv_os_sock_t sock) {
135 int r;
136#ifdef _WIN32
137 r = closesocket(sock);
138#else
139 r = close(sock);
140#endif
141 ASSERT(r == 0);
142}
143
144
145static connection_context_t* create_connection_context(
146 uv_os_sock_t sock, int is_server_connection) {
147 int r;
148 connection_context_t* context;
149
150 context = (connection_context_t*) malloc(sizeof *context);
151 ASSERT(context != NULL);
152
153 context->sock = sock;
154 context->is_server_connection = is_server_connection;
155 context->read = 0;
156 context->sent = 0;
157 context->open_handles = 0;
158 context->events = 0;
159 context->delayed_events = 0;
160 context->got_fin = 0;
161 context->sent_fin = 0;
162
163 r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock);
164 context->open_handles++;
165 context->poll_handle.data = context;
166 ASSERT(r == 0);
167
168 r = uv_timer_init(uv_default_loop(), &context->timer_handle);
169 context->open_handles++;
170 context->timer_handle.data = context;
171 ASSERT(r == 0);
172
173 return context;
174}
175
176
177static void connection_close_cb(uv_handle_t* handle) {
178 connection_context_t* context = (connection_context_t*) handle->data;
179
180 if (--context->open_handles == 0) {
181 if (test_mode == DUPLEX || context->is_server_connection) {
182 ASSERT(context->read == TRANSFER_BYTES);
183 } else {
184 ASSERT(context->read == 0);
185 }
186
187 if (test_mode == DUPLEX || !context->is_server_connection) {
188 ASSERT(context->sent == TRANSFER_BYTES);
189 } else {
190 ASSERT(context->sent == 0);
191 }
192
193 closed_connections++;
194
195 free(context);
196 }
197}
198
199
200static void destroy_connection_context(connection_context_t* context) {
201 uv_close((uv_handle_t*) &context->poll_handle, connection_close_cb);
202 uv_close((uv_handle_t*) &context->timer_handle, connection_close_cb);
203}
204
205
206static void connection_poll_cb(uv_poll_t* handle, int status, int events) {
207 connection_context_t* context = (connection_context_t*) handle->data;
208 int new_events;
209 int r;
210
211 ASSERT(status == 0);
212 ASSERT(events & context->events);
213 ASSERT(!(events & ~context->events));
214
215 new_events = context->events;
216
217 if (events & UV_READABLE) {
218 int action = rand() % 7;
219
220 switch (action) {
221 case 0:
222 case 1: {
223 /* Read a couple of bytes. */
224 static char buffer[74];
225 r = recv(context->sock, buffer, sizeof buffer, 0);
226 ASSERT(r >= 0);
227
228 if (r > 0) {
229 context->read += r;
230 } else {
231 /* Got FIN. */
232 context->got_fin = 1;
233 new_events &= ~UV_READABLE;
234 }
235
236 break;
237 }
238
239 case 2:
240 case 3: {
241 /* Read until EAGAIN. */
242 static char buffer[931];
243 r = recv(context->sock, buffer, sizeof buffer, 0);
244 ASSERT(r >= 0);
245
246 while (r > 0) {
247 context->read += r;
248 r = recv(context->sock, buffer, sizeof buffer, 0);
249 }
250
251 if (r == 0) {
252 /* Got FIN. */
253 context->got_fin = 1;
254 new_events &= ~UV_READABLE;
255 } else {
256 ASSERT(got_eagain());
257 }
258
259 break;
260 }
261
262 case 4:
263 /* Ignore. */
264 break;
265
266 case 5:
267 /* Stop reading for a while. Restart in timer callback. */
268 new_events &= ~UV_READABLE;
269 if (!uv_is_active((uv_handle_t*) &context->timer_handle)) {
270 context->delayed_events = UV_READABLE;
271 uv_timer_start(&context->timer_handle, delay_timer_cb, 10, 0);
272 } else {
273 context->delayed_events |= UV_READABLE;
274 }
275 break;
276
277 case 6:
278 /* Fudge with the event mask. */
279 uv_poll_start(&context->poll_handle, UV_WRITABLE, connection_poll_cb);
280 uv_poll_start(&context->poll_handle, UV_READABLE, connection_poll_cb);
281 context->events = UV_READABLE;
282 break;
283
284 default:
285 ASSERT(0);
286 }
287 }
288
289 if (events & UV_WRITABLE) {
290 if (context->sent < TRANSFER_BYTES &&
291 !(test_mode == UNIDIRECTIONAL && context->is_server_connection)) {
292 /* We have to send more bytes. */
293 int action = rand() % 7;
294
295 switch (action) {
296 case 0:
297 case 1: {
298 /* Send a couple of bytes. */
299 static char buffer[103];
300
301 int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer);
302 ASSERT(send_bytes > 0);
303
304 r = send(context->sock, buffer, send_bytes, 0);
305
306 if (r < 0) {
307 ASSERT(got_eagain());
308 spurious_writable_wakeups++;
309 break;
310 }
311
312 ASSERT(r > 0);
313 context->sent += r;
314 valid_writable_wakeups++;
315 break;
316 }
317
318 case 2:
319 case 3: {
320 /* Send until EAGAIN. */
321 static char buffer[1234];
322
323 int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer);
324 ASSERT(send_bytes > 0);
325
326 r = send(context->sock, buffer, send_bytes, 0);
327
328 if (r < 0) {
329 ASSERT(got_eagain());
330 spurious_writable_wakeups++;
331 break;
332 }
333
334 ASSERT(r > 0);
335 valid_writable_wakeups++;
336 context->sent += r;
337
338 while (context->sent < TRANSFER_BYTES) {
339 send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer);
340 ASSERT(send_bytes > 0);
341
342 r = send(context->sock, buffer, send_bytes, 0);
343
344 if (r <= 0) break;
345 context->sent += r;
346 }
347 ASSERT(r > 0 || got_eagain());
348 break;
349 }
350
351 case 4:
352 /* Ignore. */
353 break;
354
355 case 5:
356 /* Stop sending for a while. Restart in timer callback. */
357 new_events &= ~UV_WRITABLE;
358 if (!uv_is_active((uv_handle_t*) &context->timer_handle)) {
359 context->delayed_events = UV_WRITABLE;
360 uv_timer_start(&context->timer_handle, delay_timer_cb, 100, 0);
361 } else {
362 context->delayed_events |= UV_WRITABLE;
363 }
364 break;
365
366 case 6:
367 /* Fudge with the event mask. */
368 uv_poll_start(&context->poll_handle,
369 UV_READABLE,
370 connection_poll_cb);
371 uv_poll_start(&context->poll_handle,
372 UV_WRITABLE,
373 connection_poll_cb);
374 context->events = UV_WRITABLE;
375 break;
376
377 default:
378 ASSERT(0);
379 }
380
381 } else {
382 /* Nothing more to write. Send FIN. */
383 int r;
384#ifdef _WIN32
385 r = shutdown(context->sock, SD_SEND);
386#else
387 r = shutdown(context->sock, SHUT_WR);
388#endif
389 ASSERT(r == 0);
390 context->sent_fin = 1;
391 new_events &= ~UV_WRITABLE;
392 }
393 }
394
395 if (context->got_fin && context->sent_fin) {
396 /* Sent and received FIN. Close and destroy context. */
397 close_socket(context->sock);
398 destroy_connection_context(context);
399 context->events = 0;
400
401 } else if (new_events != context->events) {
402 /* Poll mask changed. Call uv_poll_start again. */
403 context->events = new_events;
404 uv_poll_start(handle, new_events, connection_poll_cb);
405 }
406
407 /* Assert that uv_is_active works correctly for poll handles. */
408 if (context->events != 0) {
409 ASSERT(uv_is_active((uv_handle_t*) handle));
410 } else {
411 ASSERT(!uv_is_active((uv_handle_t*) handle));
412 }
413}
414
415
416static void delay_timer_cb(uv_timer_t* timer, int status) {
417 connection_context_t* context = (connection_context_t*) timer->data;
418 int r;
419
420 /* Timer should auto stop. */
421 ASSERT(!uv_is_active((uv_handle_t*) timer));
422
423 /* Add the requested events to the poll mask. */
424 ASSERT(context->delayed_events != 0);
425 context->events |= context->delayed_events;
426 context->delayed_events = 0;
427
428 r = uv_poll_start(&context->poll_handle,
429 context->events,
430 connection_poll_cb);
431 ASSERT(r == 0);
432}
433
434
435static server_context_t* create_server_context(
436 uv_os_sock_t sock) {
437 int r;
438 server_context_t* context;
439
440 context = (server_context_t*) malloc(sizeof *context);
441 ASSERT(context != NULL);
442
443 context->sock = sock;
444 context->connections = 0;
445
446 r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock);
447 context->poll_handle.data = context;
448 ASSERT(r == 0);
449
450 return context;
451}
452
453
454static void server_close_cb(uv_handle_t* handle) {
455 server_context_t* context = (server_context_t*) handle->data;
456 free(context);
457}
458
459
460static void destroy_server_context(server_context_t* context) {
461 uv_close((uv_handle_t*) &context->poll_handle, server_close_cb);
462}
463
464
465static void server_poll_cb(uv_poll_t* handle, int status, int events) {
466 server_context_t* server_context = (server_context_t*)
467 handle->data;
468 connection_context_t* connection_context;
469 struct sockaddr_in addr;
470 socklen_t addr_len;
471 uv_os_sock_t sock;
472 int r;
473
474 addr_len = sizeof addr;
475 sock = accept(server_context->sock, (struct sockaddr*) &addr, &addr_len);
476#ifdef _WIN32
477 ASSERT(sock != INVALID_SOCKET);
478#else
479 ASSERT(sock >= 0);
480#endif
481
482 set_nonblocking(sock);
483
484 connection_context = create_connection_context(sock, 1);
485 connection_context->events = UV_READABLE | UV_WRITABLE;
486 r = uv_poll_start(&connection_context->poll_handle,
487 UV_READABLE | UV_WRITABLE,
488 connection_poll_cb);
489 ASSERT(r == 0);
490
491 if (++server_context->connections == NUM_CLIENTS) {
492 close_socket(server_context->sock);
493 destroy_server_context(server_context);
494 }
495}
496
497
498static void start_server() {
499 uv_os_sock_t sock;
500 server_context_t* context;
501 int r;
502
503 sock = create_nonblocking_bound_socket(uv_ip4_addr("127.0.0.1", TEST_PORT));
504 context = create_server_context(sock);
505
506 r = listen(sock, 100);
507 ASSERT(r == 0);
508
509 r = uv_poll_start(&context->poll_handle, UV_READABLE, server_poll_cb);
510 ASSERT(r == 0);
511}
512
513
514static void start_client() {
515 uv_os_sock_t sock;
516 connection_context_t* context;
517 struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
518 int r;
519
520 sock = create_nonblocking_bound_socket(uv_ip4_addr("0.0.0.0", 0));
521 context = create_connection_context(sock, 0);
522
523 context->events = UV_READABLE | UV_WRITABLE;
524 r = uv_poll_start(&context->poll_handle,
525 UV_READABLE | UV_WRITABLE,
526 connection_poll_cb);
527 ASSERT(r == 0);
528
529 r = connect(sock, (struct sockaddr*) &server_addr, sizeof server_addr);
530 ASSERT(r == 0 || got_eagain());
531}
532
533
534static void start_poll_test() {
535 int i, r;
536
537#ifdef _WIN32
538 {
539 struct WSAData wsa_data;
540 r = WSAStartup(MAKEWORD(2, 2), &wsa_data);
541 ASSERT(r == 0);
542 }
543#endif
544
545 start_server();
546
547 for (i = 0; i < NUM_CLIENTS; i++)
548 start_client();
549
550 r = uv_run(uv_default_loop());
551 ASSERT(r == 0);
552
553 /* Assert that at most one percent of the writable wakeups was spurious. */
554 ASSERT(spurious_writable_wakeups == 0 ||
555 (valid_writable_wakeups + spurious_writable_wakeups) /
556 spurious_writable_wakeups > 100);
557
558 ASSERT(closed_connections == NUM_CLIENTS * 2);
559}
560
561
562TEST_IMPL(poll_duplex) {
563 test_mode = DUPLEX;
564 start_poll_test();
565 return 0;
566}
567
568
569TEST_IMPL(poll_unidirectional) {
570 test_mode = UNIDIRECTIONAL;
571 start_poll_test();
572 return 0;
573}