1 /* Unit test for W32 version of g_poll()
2 *
3 * Copyright © 2017 Руслан Ижбулатов <lrn1986@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <glib.h>
22 #include <winsock2.h>
23
24 #define NUM_POLLEES 999
25 #define NUM_POLLFDS 1000
26
27 #define ASYNC_CONNECT_OK(r) (r == 0 || (r < 0 && GetLastError () == WSAEWOULDBLOCK))
28
29 #define REPEAT 1
30
31 static void
32 init_networking (void)
33 {
34 WSADATA wsadata;
35
36 if (WSAStartup (MAKEWORD (2, 0), &wsadata) != 0)
37 g_error ("Windows Sockets could not be initialized");
38 }
39
40 static void
41 prepare_fds (SOCKET sockets[],
42 GPollFD fds[],
43 int num_pollees)
44 {
45 gint i;
46
47 for (i = 0; i < num_pollees; i++)
48 {
49 fds[i].fd = (gintptr) WSACreateEvent ();
50 g_assert (WSAEventSelect (sockets[i], (HANDLE) fds[i].fd, FD_READ | FD_CLOSE) == 0);
51 }
52 }
53
54 static void
55 reset_fds (GPollFD fds[],
56 int num_pollees)
57 {
58 gint i;
59
60 for (i = 0; i < num_pollees; i++)
61 {
62 WSAResetEvent ((HANDLE) fds[i].fd);
63 fds[i].events = G_IO_IN | G_IO_OUT | G_IO_ERR;
64 fds[i].revents = 0;
65 }
66 }
67
68 static void
69 reset_fds_msg (GPollFD fds[],
70 int num_pollfds)
71 {
72 fds[num_pollfds - 1].fd = G_WIN32_MSG_HANDLE;
73 fds[num_pollfds - 1].events = G_IO_IN;
74 fds[num_pollfds - 1].revents = 0;
75 }
76
77 static void
78 check_fds (SOCKET sockets[],
79 GPollFD fds[],
80 int num_pollees)
81 {
82 gint i;
83
84 for (i = 0; i < num_pollees; i++)
85 {
86 if (fds[i].revents != 0)
87 {
88 WSANETWORKEVENTS events;
89 g_assert (WSAEnumNetworkEvents (sockets[i], 0, &events) == 0);
90
91 fds[i].revents = 0;
92 if (events.lNetworkEvents & (FD_READ | FD_ACCEPT))
93 fds[i].revents |= G_IO_IN;
94
95 if (events.lNetworkEvents & FD_WRITE)
96 fds[i].revents |= G_IO_OUT;
97 else
98 {
99 /* We have called WSAEnumNetworkEvents() above but it didn't
100 * set FD_WRITE.
101 */
102 if (events.lNetworkEvents & FD_CONNECT)
103 {
104 if (events.iErrorCode[FD_CONNECT_BIT] == 0)
105 fds[i].revents |= G_IO_OUT;
106 else
107 fds[i].revents |= (G_IO_HUP | G_IO_ERR);
108 }
109 if (fds[i].revents == 0 && (events.lNetworkEvents & (FD_CLOSE)))
110 fds[i].revents |= G_IO_HUP;
111 }
112 }
113 }
114 }
115
116 static void
117 prepare_sockets (SOCKET sockets[],
118 SOCKET opp_sockets[],
119 GPollFD fds[],
120 int num_pollees)
121 {
122 gint i;
123 SOCKET server;
124 struct sockaddr_in sa;
125 unsigned long ul = 1;
126 int sa_size;
127 int r;
128
129 server = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
130 g_assert (server != INVALID_SOCKET);
131
132 memset(&sa, 0, sizeof sa);
133
134 sa.sin_family = AF_INET;
135 sa.sin_port = 0;
136 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
137 sa_size = sizeof (sa);
138
139 g_assert (bind (server, (const struct sockaddr *) &sa, sa_size) == 0);
140 g_assert (getsockname (server, (struct sockaddr *) &sa, &sa_size) == 0);
141 g_assert (listen (server, 1) == 0);
142
143 for (i = 0; i < num_pollees; i++)
144 {
145 opp_sockets[i] = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
146 g_assert (opp_sockets[i] != INVALID_SOCKET);
147 g_assert (ioctlsocket (opp_sockets[i], FIONBIO, &ul) == 0);
148
149 r = connect (opp_sockets[i], (const struct sockaddr *) &sa, sizeof (sa));
150 g_assert (ASYNC_CONNECT_OK (r));
151
152 sockets[i] = accept (server, NULL, NULL);
153 g_assert (sockets[i] != INVALID_SOCKET);
154 g_assert (ioctlsocket (sockets[i], FIONBIO, &ul) == 0);
155 }
156
157 closesocket (server);
158 }
159
160 static void
161 cleanup_sockets (SOCKET sockets[],
162 SOCKET opp_sockets[],
163 int num_pollees)
164 {
165 gint i;
166
167 for (i = 0; i < num_pollees; i++)
168 {
169 closesocket (sockets[i]);
170 closesocket (opp_sockets[i]);
171 }
172 }
173
174 static void
175 bucketize (gint64 val,
176 gint buckets[],
177 gint64 bucket_limits[],
178 gint count)
179 {
180 gint i;
181
182 if (val > bucket_limits[count - 1])
183 {
184 buckets[count - 1] += 1;
185 return;
186 }
187
188 for (i = count - 1; i > 0; i--)
189 if (val < bucket_limits[i] && val >= bucket_limits[i - 1])
190 {
191 buckets[i] += 1;
192 return;
193 }
194
195 buckets[0] += 1;
196 }
197
198 static void
199 print_buckets (gint buckets[],
200 gint64 bucket_limits[],
201 gint count)
202 {
203 gint i;
204
205 for (i = 0; i < count; i++)
206 if (i < count - 1)
207 g_printerr ("%-4lld-%4lld|", i == 0 ? 0 : bucket_limits[i - 1], bucket_limits[i] - 1);
208 else
209 g_printerr (" >= %-4lld|", bucket_limits[i - 1]);
210
211 g_printerr ("\n");
212
213 for (i = 0; i < count; i++)
214 {
215 gint len;
216 gint padding;
217 gint j;
218 if (buckets[i] < 10)
219 len = 1;
220 else if (buckets[i] < 100)
221 len = 2;
222 else if (buckets[i] < 1000)
223 len = 3;
224 else
225 len = 4;
226 padding = 9 - len;
227 for (j = 0; j < padding / 2; j++)
228 g_printerr (" ");
229 if (buckets[i] != 0)
230 g_printerr ("%*d", len, buckets[i]);
231 else
232 g_printerr (" ");
233 for (j = padding / 2; j < padding; j++)
234 g_printerr (" ");
235 g_printerr (" ");
236 }
237
238 g_printerr ("\n\n");
239 }
240
241 static void
242 test_gpoll (void)
243 {
244 SOCKET sockets[NUM_POLLEES];
245 GPollFD fds[NUM_POLLFDS];
246 SOCKET opp_sockets[NUM_POLLEES];
247 gint i;
248 gint activatable;
249 gint64 times[REPEAT][2];
250 #define BUCKET_COUNT 25
251 gint64 bucket_limits[BUCKET_COUNT] = {3, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 100, 120, 150, 180, 220, 280, 350, 450, 600, 800, 1000};
252 gint buckets[BUCKET_COUNT];
253 gint64 times_avg = 0, times_min = G_MAXINT64, times_max = 0;
254
255 prepare_sockets (sockets, opp_sockets, fds, NUM_POLLEES);
256 prepare_fds (sockets, fds, NUM_POLLEES);
257
258 times_avg = 0;
259 times_min = G_MAXINT64;
260 times_max = 0;
261 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
262
263 for (i = 0; i < REPEAT; i++)
264 {
265 gint r;
266 gint64 diff;
267
268 reset_fds (fds, NUM_POLLEES);
269 reset_fds_msg (fds, NUM_POLLFDS);
270 times[i][0] = g_get_monotonic_time ();
271 r = g_poll (fds, NUM_POLLFDS, 0);
272 times[i][1] = g_get_monotonic_time ();
273 g_assert (r == 0);
274 diff = times[i][1] - times[i][0];
275 if (times_min > diff)
276 times_min = diff;
277 if (times_max < diff)
278 times_max = diff;
279 times_avg += diff;
280 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
281 }
282
283 times_avg /= NUM_POLLEES;
284 g_printerr ("\nempty poll time:\n%4lldns - %4lldns, average %4lldns\n", times_min, times_max, times_avg);
285 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
286
287 times_avg = 0;
288 times_min = G_MAXINT64;
289 times_max = 0;
290 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
291
292 activatable = 0;
293
294 for (i = 0; i < REPEAT; i++)
295 {
296 gint r, s, v, t;
297 gint64 diff;
298 MSG msg;
299 gboolean found_app;
300
301 reset_fds (fds, NUM_POLLEES);
302 reset_fds_msg (fds, NUM_POLLFDS);
303 s = send (opp_sockets[activatable], (const char *) &t, 1, 0);
304 g_assert (PostMessage (NULL, WM_APP, 1, 2));
305 /* This is to ensure that all sockets catch up, otherwise some might not poll active */
306 g_usleep (G_USEC_PER_SEC / 1000);
307
308 times[i][0] = g_get_monotonic_time ();
309 r = g_poll (fds, NUM_POLLFDS, 1000);
310 times[i][1] = g_get_monotonic_time ();
311
312 check_fds (sockets, fds, NUM_POLLEES);
313 v = recv (sockets[activatable], (char *) &t, 1, 0);
314 found_app = FALSE;
315 while (!found_app && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
316 if (msg.message == WM_APP && msg.wParam == 1 && msg.lParam == 2)
317 found_app = TRUE;
318 g_assert (s == 1);
319 g_assert (r == 2);
320 g_assert (v == 1);
321 g_assert (found_app);
322
323 reset_fds (fds, NUM_POLLEES);
324 reset_fds_msg (fds, NUM_POLLFDS);
325 r = g_poll (fds, NUM_POLLFDS, 0);
326 check_fds (sockets, fds, NUM_POLLEES);
327 g_assert (r == 0);
328 diff = times[i][1] - times[i][0];
329 if (times_min > diff)
330 times_min = diff;
331 if (times_max < diff)
332 times_max = diff;
333 times_avg += diff;
334 activatable = (activatable + 1) % NUM_POLLEES;
335 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
336 }
337
338 times_avg /= NUM_POLLEES;
339 g_printerr ("1-socket + msg poll time:\n%4lldns - %4lldns, average %4lldns\n", times_min, times_max, times_avg);
340 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
341
342 times_avg = 0;
343 times_min = G_MAXINT64;
344 times_max = 0;
345 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
346
347 activatable = 0;
348
349 for (i = 0; i < REPEAT; i++)
350 {
351 gint r, s, v, t;
352 gint64 diff;
353
354 reset_fds (fds, NUM_POLLEES);
355 reset_fds_msg (fds, NUM_POLLFDS);
356 s = send (opp_sockets[activatable], (const char *) &t, 1, 0);
357
358 g_usleep (G_USEC_PER_SEC / 1000);
359
360 times[i][0] = g_get_monotonic_time ();
361 r = g_poll (fds, NUM_POLLFDS, 1000);
362 times[i][1] = g_get_monotonic_time ();
363
364 check_fds (sockets, fds, NUM_POLLEES);
365 v = recv (sockets[activatable], (char *) &t, 1, 0);
366 g_assert (s == 1);
367 g_assert (r == 1);
368 g_assert (v == 1);
369
370 reset_fds (fds, NUM_POLLEES);
371 reset_fds_msg (fds, NUM_POLLFDS);
372 r = g_poll (fds, NUM_POLLFDS, 0);
373 check_fds (sockets, fds, NUM_POLLEES);
374 g_assert (r == 0);
375
376 diff = times[i][1] - times[i][0];
377 if (times_min > diff)
378 times_min = diff;
379 if (times_max < diff)
380 times_max = diff;
381 times_avg += diff;
382 activatable = (activatable + 1) % NUM_POLLEES;
383 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
384 }
385
386 times_avg /= NUM_POLLEES;
387 g_printerr ("1-socket poll time:\n%4lldns - %4lldns, average %4lldns\n", times_min, times_max, times_avg);
388 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
389
390 times_avg = 0;
391 times_min = G_MAXINT64;
392 times_max = 0;
393 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
394
395 for (i = 0; i < REPEAT; i++)
396 {
397 gint r, s, v, t;
398 gint64 diff;
399 gint j;
400
401 reset_fds (fds, NUM_POLLEES);
402 reset_fds_msg (fds, NUM_POLLFDS);
403 s = v = 0;
404
405 for (j = 0; j < NUM_POLLEES / 2; j++)
406 s += send (opp_sockets[j], (const char *) &t, 1, 0) == 1 ? 1 : 0;
407
408 g_usleep (G_USEC_PER_SEC / 1000);
409
410 times[i][0] = g_get_monotonic_time ();
411 r = g_poll (fds, NUM_POLLFDS, 1000);
412 times[i][1] = g_get_monotonic_time ();
413 check_fds (sockets, fds, NUM_POLLEES);
414 for (j = 0; j < NUM_POLLEES / 2; j++)
415 v += recv (sockets[j], (char *) &t, 1, 0) == 1 ? 1 : 0;
416 g_assert (s == NUM_POLLEES / 2);
417 g_assert (r == NUM_POLLEES / 2);
418 g_assert (v == NUM_POLLEES / 2);
419
420 reset_fds (fds, NUM_POLLEES);
421 reset_fds_msg (fds, NUM_POLLFDS);
422 r = g_poll (fds, NUM_POLLFDS, 0);
423 check_fds (sockets, fds, NUM_POLLEES);
424 g_assert (r == 0);
425
426 diff = times[i][1] - times[i][0];
427 if (times_min > diff)
428 times_min = diff;
429 if (times_max < diff)
430 times_max = diff;
431 times_avg += diff;
432 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
433 }
434
435 times_avg /= NUM_POLLEES;
436 g_printerr ("half-socket poll time:\n%4lldns - %4lldns, average %4lldns\n", times_min, times_max, times_avg);
437 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
438
439 times_avg = 0;
440 times_min = G_MAXINT64;
441 times_max = 0;
442 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
443
444 for (i = 0; i < REPEAT; i++)
445 {
446 gint r, s, v, t;
447 gint64 diff;
448 gint j;
449 MSG msg;
450 gboolean found_app;
451
452 reset_fds (fds, NUM_POLLEES);
453 reset_fds_msg (fds, NUM_POLLFDS);
454 s = v = 0;
455
456 for (j = 0; j < NUM_POLLEES / 2; j++)
457 s += send (opp_sockets[j], (const char *) &t, 1, 0) == 1 ? 1 : 0;
458 g_assert (PostMessage (NULL, WM_APP, 1, 2));
459
460 /* This is to ensure that all sockets catch up, otherwise some might not poll active */
461 g_usleep (G_USEC_PER_SEC / 1000);
462
463 times[i][0] = g_get_monotonic_time ();
464 r = g_poll (fds, NUM_POLLFDS, 1000);
465 times[i][1] = g_get_monotonic_time ();
466 check_fds (sockets, fds, NUM_POLLEES);
467 for (j = 0; j < NUM_POLLEES / 2; j++)
468 v += recv (sockets[j], (char *) &t, 1, 0) == 1 ? 1 : 0;
469 found_app = FALSE;
470 while (!found_app && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
471 if (msg.message == WM_APP && msg.wParam == 1 && msg.lParam == 2)
472 found_app = TRUE;
473 g_assert (s == NUM_POLLEES / 2);
474 g_assert (r == NUM_POLLEES / 2 + 1);
475 g_assert (v == NUM_POLLEES / 2);
476 g_assert (found_app);
477
478 reset_fds (fds, NUM_POLLEES);
479 reset_fds_msg (fds, NUM_POLLFDS);
480 r = g_poll (fds, NUM_POLLFDS, 0);
481 check_fds (sockets, fds, NUM_POLLEES);
482 g_assert (r == 0);
483
484 diff = times[i][1] - times[i][0];
485 if (times_min > diff)
486 times_min = diff;
487 if (times_max < diff)
488 times_max = diff;
489 times_avg += diff;
490 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
491 }
492
493 times_avg /= NUM_POLLEES;
494 g_printerr ("half-socket + msg poll time:\n%4lldns - %4lldns, average %4lldns\n", times_min, times_max, times_avg);
495 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
496
497 times_avg = 0;
498 times_min = G_MAXINT64;
499 times_max = 0;
500 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
501
502 for (i = 0; i < REPEAT; i++)
503 {
504 gint r, s, v, t;
505 gint64 diff;
506 gint j;
507
508 reset_fds (fds, NUM_POLLEES);
509 reset_fds_msg (fds, NUM_POLLFDS);
510 s = v = 0;
511
512 for (j = 0; j < NUM_POLLEES; j++)
513 s += send (opp_sockets[j], (const char *) &t, 1, 0) == 1 ? 1 : 0;
514
515 g_usleep (G_USEC_PER_SEC / 1000);
516
517 times[i][0] = g_get_monotonic_time ();
518 r = g_poll (fds, NUM_POLLFDS, 1000);
519 times[i][1] = g_get_monotonic_time ();
520 check_fds (sockets, fds, NUM_POLLEES);
521 for (j = 0; j < NUM_POLLEES; j++)
522 v += recv (sockets[j], (char *) &t, 1, 0) == 1 ? 1 : 0;
523 g_assert (s == NUM_POLLEES);
524 g_assert (r == NUM_POLLEES);
525 g_assert (v == NUM_POLLEES);
526
527 reset_fds (fds, NUM_POLLEES);
528 reset_fds_msg (fds, NUM_POLLFDS);
529 r = g_poll (fds, NUM_POLLFDS, 0);
530 check_fds (sockets, fds, NUM_POLLEES);
531 g_assert (r == 0);
532
533 diff = times[i][1] - times[i][0];
534 if (times_min > diff)
535 times_min = diff;
536 if (times_max < diff)
537 times_max = diff;
538 times_avg += diff;
539 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
540 }
541
542 times_avg /= NUM_POLLEES;
543 g_printerr ("%d-socket poll time: \n%4lldns - %4lldns, average %4lldns\n", NUM_POLLEES, times_min, times_max, times_avg);
544 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
545
546 activatable = 0;
547 times_avg = 0;
548 times_min = G_MAXINT64;
549 times_max = 0;
550 memset (buckets, 0, sizeof (gint) * BUCKET_COUNT);
551
552 for (i = 0; i < REPEAT; i++)
553 {
554 gint r, s, v, t;
555 gint64 diff;
556 gint j;
557 MSG msg;
558 gboolean found_app;
559
560 reset_fds (fds, NUM_POLLEES);
561 reset_fds_msg (fds, NUM_POLLFDS);
562 s = v = 0;
563
564 for (j = 0; j < activatable; j++)
565 s += send (opp_sockets[j], (const char *) &t, 1, 0) == 1 ? 1 : 0;
566 g_assert (PostMessage (NULL, WM_APP, 1, 2));
567
568 g_usleep (G_USEC_PER_SEC / 1000);
569
570 times[i][0] = g_get_monotonic_time ();
571 r = g_poll (fds, NUM_POLLFDS, 1000);
572 times[i][1] = g_get_monotonic_time ();
573 check_fds (sockets, fds, NUM_POLLEES);
574 for (j = 0; j < activatable; j++)
575 v += recv (sockets[j], (char *) &t, 1, 0) == 1 ? 1 : 0;
576 found_app = FALSE;
577 while (!found_app && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
578 if (msg.message == WM_APP && msg.wParam == 1 && msg.lParam == 2)
579 found_app = TRUE;
580 g_assert (s == activatable);
581 g_assert (r == activatable + 1);
582 g_assert (v == activatable);
583 g_assert (found_app);
584
585 reset_fds (fds, NUM_POLLEES);
586 reset_fds_msg (fds, NUM_POLLFDS);
587 r = g_poll (fds, NUM_POLLFDS, 0);
588 check_fds (sockets, fds, NUM_POLLEES);
589 g_assert (r == 0);
590
591 diff = times[i][1] - times[i][0];
592 if (times_min > diff)
593 times_min = diff;
594 if (times_max < diff)
595 times_max = diff;
596 times_avg += diff;
597 bucketize (diff, buckets, bucket_limits, BUCKET_COUNT);
598 activatable = (activatable + 1) % NUM_POLLEES;
599 }
600
601 times_avg /= NUM_POLLEES;
602 g_printerr ("variable socket number + msg poll time: \n%4lldns - %4lldns, average %4lldns\n", times_min, times_max, times_avg);
603 print_buckets (buckets, bucket_limits, BUCKET_COUNT);
604
605 cleanup_sockets (sockets, opp_sockets, NUM_POLLEES);
606 }
607
608 int
609 main (int argc,
610 char *argv[])
611 {
612 int result;
613 GMainContext *ctx;
614
615 g_test_init (&argc, &argv, NULL);
616 init_networking ();
617 ctx = g_main_context_new ();
618
619 g_test_add_func ("/gpoll/gpoll", test_gpoll);
620
621 result = g_test_run ();
622
623 g_main_context_unref (ctx);
624
625 return result;
626 }