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