blob: 7d00690b3cd592e1f917cb7dc2de095b11ea0f6b [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
3 * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20
21
22/**
23 * \page page_pjlib_ioqueue_udp_test Test: I/O Queue (UDP)
24 *
25 * This file provides implementation to test the
26 * functionality of the I/O queue when UDP socket is used.
27 *
28 *
29 * This file is <b>pjlib-test/ioq_udp.c</b>
30 *
31 * \include pjlib-test/ioq_udp.c
32 */
33
34
35#if INCLUDE_UDP_IOQUEUE_TEST
36
37#include <pjlib.h>
38
39#include <pj/compat/socket.h>
40
41#define THIS_FILE "test_udp"
42#define PORT 51233
43#define LOOP 100
Benny Prijono42c5b9e2006-05-10 19:24:40 +000044///#define LOOP 2
Benny Prijono5dcb38d2005-11-21 01:55:47 +000045#define BUF_MIN_SIZE 32
46#define BUF_MAX_SIZE 2048
47#define SOCK_INACTIVE_MIN (1)
48#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
49#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
50
51#undef TRACE_
52#define TRACE_(msg) PJ_LOG(3,(THIS_FILE,"....." msg))
53
Benny Prijono42c5b9e2006-05-10 19:24:40 +000054#if 0
55# define TRACE__(args) PJ_LOG(3,args)
56#else
57# define TRACE__(args)
58#endif
59
60
Benny Prijono5dcb38d2005-11-21 01:55:47 +000061static pj_ssize_t callback_read_size,
62 callback_write_size,
63 callback_accept_status,
64 callback_connect_status;
65static pj_ioqueue_key_t *callback_read_key,
66 *callback_write_key,
67 *callback_accept_key,
68 *callback_connect_key;
69static pj_ioqueue_op_key_t *callback_read_op,
70 *callback_write_op,
71 *callback_accept_op;
72
73static void on_ioqueue_read(pj_ioqueue_key_t *key,
74 pj_ioqueue_op_key_t *op_key,
75 pj_ssize_t bytes_read)
76{
77 callback_read_key = key;
78 callback_read_op = op_key;
79 callback_read_size = bytes_read;
Benny Prijono42c5b9e2006-05-10 19:24:40 +000080 TRACE__((THIS_FILE, " callback_read_key = %p, bytes=%d",
81 key, bytes_read));
Benny Prijono5dcb38d2005-11-21 01:55:47 +000082}
83
84static void on_ioqueue_write(pj_ioqueue_key_t *key,
85 pj_ioqueue_op_key_t *op_key,
86 pj_ssize_t bytes_written)
87{
88 callback_write_key = key;
89 callback_write_op = op_key;
90 callback_write_size = bytes_written;
91}
92
93static void on_ioqueue_accept(pj_ioqueue_key_t *key,
94 pj_ioqueue_op_key_t *op_key,
95 pj_sock_t sock, int status)
96{
97 PJ_UNUSED_ARG(sock);
98 callback_accept_key = key;
99 callback_accept_op = op_key;
100 callback_accept_status = status;
101}
102
103static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
104{
105 callback_connect_key = key;
106 callback_connect_status = status;
107}
108
109static pj_ioqueue_callback test_cb =
110{
111 &on_ioqueue_read,
112 &on_ioqueue_write,
113 &on_ioqueue_accept,
114 &on_ioqueue_connect,
115};
116
117#ifdef PJ_WIN32
118# define S_ADDR S_un.S_addr
119#else
120# define S_ADDR s_addr
121#endif
122
123/*
124 * compliance_test()
125 * To test that the basic IOQueue functionality works. It will just exchange
126 * data between two sockets.
127 */
128static int compliance_test(void)
129{
130 pj_sock_t ssock=-1, csock=-1;
Benny Prijono37e8d332006-01-20 21:03:36 +0000131 pj_sockaddr_in addr, dst_addr;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000132 int addrlen;
133 pj_pool_t *pool = NULL;
134 char *send_buf, *recv_buf;
135 pj_ioqueue_t *ioque = NULL;
136 pj_ioqueue_key_t *skey, *ckey;
137 pj_ioqueue_op_key_t read_op, write_op;
138 int bufsize = BUF_MIN_SIZE;
139 pj_ssize_t bytes, status = -1;
140 pj_str_t temp;
141 pj_bool_t send_pending, recv_pending;
142 pj_status_t rc;
143
144 pj_set_os_error(PJ_SUCCESS);
145
146 // Create pool.
147 pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
148
149 // Allocate buffers for send and receive.
150 send_buf = (char*)pj_pool_alloc(pool, bufsize);
151 recv_buf = (char*)pj_pool_alloc(pool, bufsize);
152
153 // Allocate sockets for sending and receiving.
154 TRACE_("creating sockets...");
155 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock);
156 if (rc==PJ_SUCCESS)
157 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock);
158 else
159 csock = PJ_INVALID_SOCKET;
160 if (rc != PJ_SUCCESS) {
161 app_perror("...ERROR in pj_sock_socket()", rc);
162 status=-1; goto on_error;
163 }
164
165 // Bind server socket.
166 TRACE_("bind socket...");
Benny Prijono37e8d332006-01-20 21:03:36 +0000167 pj_memset(&addr, 0, sizeof(addr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000168 addr.sin_family = PJ_AF_INET;
169 addr.sin_port = pj_htons(PORT);
170 if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
171 status=-10; goto on_error;
172 }
173
174 // Create I/O Queue.
175 TRACE_("create ioqueue...");
176 rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
177 if (rc != PJ_SUCCESS) {
178 status=-20; goto on_error;
179 }
180
181 // Register server and client socket.
182 // We put this after inactivity socket, hopefully this can represent the
183 // worst waiting time.
184 TRACE_("registering first sockets...");
185 rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
186 &test_cb, &skey);
187 if (rc != PJ_SUCCESS) {
188 app_perror("...error(10): ioqueue_register error", rc);
189 status=-25; goto on_error;
190 }
191 TRACE_("registering second sockets...");
192 rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL,
193 &test_cb, &ckey);
194 if (rc != PJ_SUCCESS) {
195 app_perror("...error(11): ioqueue_register error", rc);
196 status=-26; goto on_error;
197 }
198
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000199 // Randomize send_buf.
200 pj_create_random_string(send_buf, bufsize);
201
202 // Register reading from ioqueue.
203 TRACE_("start recvfrom...");
Benny Prijono37e8d332006-01-20 21:03:36 +0000204 pj_memset(&addr, 0, sizeof(addr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000205 addrlen = sizeof(addr);
206 bytes = bufsize;
207 rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0,
208 &addr, &addrlen);
209 if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
210 app_perror("...error: pj_ioqueue_recvfrom", rc);
211 status=-28; goto on_error;
212 } else if (rc == PJ_EPENDING) {
213 recv_pending = 1;
214 PJ_LOG(3, (THIS_FILE,
215 "......ok: recvfrom returned pending"));
216 } else {
217 PJ_LOG(3, (THIS_FILE,
218 "......error: recvfrom returned immediate ok!"));
219 status=-29; goto on_error;
220 }
221
Benny Prijono37e8d332006-01-20 21:03:36 +0000222 // Set destination address to send the packet.
223 TRACE_("set destination address...");
224 temp = pj_str("127.0.0.1");
225 if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, PORT)) != 0) {
226 app_perror("...error: unable to resolve 127.0.0.1", rc);
227 status=-290; goto on_error;
228 }
229
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000230 // Write must return the number of bytes.
231 TRACE_("start sendto...");
232 bytes = bufsize;
Benny Prijono37e8d332006-01-20 21:03:36 +0000233 rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &dst_addr,
234 sizeof(dst_addr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000235 if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
236 app_perror("...error: pj_ioqueue_sendto", rc);
237 status=-30; goto on_error;
238 } else if (rc == PJ_EPENDING) {
239 send_pending = 1;
240 PJ_LOG(3, (THIS_FILE,
241 "......ok: sendto returned pending"));
242 } else {
243 send_pending = 0;
244 PJ_LOG(3, (THIS_FILE,
245 "......ok: sendto returned immediate success"));
246 }
247
248 // reset callback variables.
249 callback_read_size = callback_write_size = 0;
250 callback_accept_status = callback_connect_status = -2;
251 callback_read_key = callback_write_key =
252 callback_accept_key = callback_connect_key = NULL;
253 callback_read_op = callback_write_op = NULL;
254
255 // Poll if pending.
256 while (send_pending || recv_pending) {
257 int rc;
258 pj_time_val timeout = { 5, 0 };
259
260 TRACE_("poll...");
261 rc = pj_ioqueue_poll(ioque, &timeout);
262
263 if (rc == 0) {
264 PJ_LOG(1,(THIS_FILE, "...ERROR: timed out..."));
265 status=-45; goto on_error;
266 } else if (rc < 0) {
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000267 app_perror("...ERROR in ioqueue_poll()", -rc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000268 status=-50; goto on_error;
269 }
270
271 if (callback_read_key != NULL) {
272 if (callback_read_size != bufsize) {
273 status=-61; goto on_error;
274 }
275 if (callback_read_key != skey) {
276 status=-65; goto on_error;
277 }
278 if (callback_read_op != &read_op) {
279 status=-66; goto on_error;
280 }
281
Benny Prijono37e8d332006-01-20 21:03:36 +0000282 if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
283 status=-67; goto on_error;
284 }
285 if (addrlen != sizeof(pj_sockaddr_in)) {
286 status=-68; goto on_error;
287 }
288 if (addr.sin_family != PJ_AF_INET) {
289 status=-69; goto on_error;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000290 }
291
292
293 recv_pending = 0;
294 }
295
296 if (callback_write_key != NULL) {
297 if (callback_write_size != bufsize) {
298 status=-73; goto on_error;
299 }
300 if (callback_write_key != ckey) {
301 status=-75; goto on_error;
302 }
303 if (callback_write_op != &write_op) {
304 status=-76; goto on_error;
305 }
306
307 send_pending = 0;
308 }
309 }
310
311 // Success
312 status = 0;
313
314on_error:
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000315 if (ssock)
316 pj_sock_close(ssock);
317 if (csock)
318 pj_sock_close(csock);
319 if (ioque != NULL)
320 pj_ioqueue_destroy(ioque);
321 pj_pool_release(pool);
322 return status;
323
324}
325
Benny Prijono8d317a02006-03-22 11:49:19 +0000326
327static void on_read_complete(pj_ioqueue_key_t *key,
328 pj_ioqueue_op_key_t *op_key,
329 pj_ssize_t bytes_read)
330{
331 unsigned *p_packet_cnt = pj_ioqueue_get_user_data(key);
332
333 PJ_UNUSED_ARG(op_key);
334 PJ_UNUSED_ARG(bytes_read);
335
336 (*p_packet_cnt)++;
337}
338
339/*
340 * unregister_test()
341 * Check if callback is still called after socket has been unregistered or
342 * closed.
343 */
344static int unregister_test(void)
345{
346 enum { RPORT = 50000, SPORT = 50001 };
347 pj_pool_t *pool;
348 pj_ioqueue_t *ioqueue;
349 pj_sock_t ssock;
350 pj_sock_t rsock;
351 int addrlen;
352 pj_sockaddr_in addr;
353 pj_ioqueue_key_t *key;
354 pj_ioqueue_op_key_t opkey;
355 pj_ioqueue_callback cb;
356 unsigned packet_cnt;
357 char sendbuf[10], recvbuf[10];
358 pj_ssize_t bytes;
359 pj_time_val timeout;
360 pj_status_t status;
361
362 pool = pj_pool_create(mem, "test", 4000, 4000, NULL);
363 if (!pool) {
364 app_perror("Unable to create pool", PJ_ENOMEM);
365 return -100;
366 }
367
368 status = pj_ioqueue_create(pool, 16, &ioqueue);
369 if (status != PJ_SUCCESS) {
370 app_perror("Error creating ioqueue", status);
371 return -110;
372 }
373
374 /* Create sender socket */
375 status = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, SPORT, &ssock);
376 if (status != PJ_SUCCESS) {
377 app_perror("Error initializing socket", status);
378 return -120;
379 }
380
381 /* Create receiver socket. */
382 status = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, RPORT, &rsock);
383 if (status != PJ_SUCCESS) {
384 app_perror("Error initializing socket", status);
385 return -130;
386 }
387
388 /* Register rsock to ioqueue. */
389 pj_memset(&cb, 0, sizeof(cb));
390 cb.on_read_complete = &on_read_complete;
391 packet_cnt = 0;
392 status = pj_ioqueue_register_sock(pool, ioqueue, rsock, &packet_cnt,
393 &cb, &key);
394 if (status != PJ_SUCCESS) {
395 app_perror("Error registering to ioqueue", status);
396 return -140;
397 }
398
399 /* Init operation key. */
400 pj_ioqueue_op_key_init(&opkey, sizeof(opkey));
401
402 /* Start reading. */
403 bytes = sizeof(recvbuf);
404 status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0);
405 if (status != PJ_EPENDING) {
406 app_perror("Expecting PJ_EPENDING, but got this", status);
407 return -150;
408 }
409
410 /* Init destination address. */
411 addrlen = sizeof(addr);
412 status = pj_sock_getsockname(rsock, &addr, &addrlen);
413 if (status != PJ_SUCCESS) {
414 app_perror("getsockname error", status);
415 return -160;
416 }
417
418 /* Override address with 127.0.0.1, since getsockname will return
419 * zero in the address field.
420 */
421 addr.sin_addr = pj_inet_addr2("127.0.0.1");
422
423 /* Init buffer to send */
424 pj_ansi_strcpy(sendbuf, "Hello0123");
425
426 /* Send one packet. */
427 bytes = sizeof(sendbuf);
428 status = pj_sock_sendto(ssock, sendbuf, &bytes, 0,
429 &addr, sizeof(addr));
430
431 if (status != PJ_SUCCESS) {
432 app_perror("sendto error", status);
433 return -170;
434 }
435
436 /* Check if packet is received. */
437 timeout.sec = 1; timeout.msec = 0;
438 pj_ioqueue_poll(ioqueue, &timeout);
439
440 if (packet_cnt != 1) {
441 return -180;
442 }
443
444 /* Just to make sure things are settled.. */
445 pj_thread_sleep(100);
446
447 /* Start reading again. */
448 bytes = sizeof(recvbuf);
449 status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0);
450 if (status != PJ_EPENDING) {
451 app_perror("Expecting PJ_EPENDING, but got this", status);
452 return -190;
453 }
454
455 /* Reset packet counter */
456 packet_cnt = 0;
457
458 /* Send one packet. */
459 bytes = sizeof(sendbuf);
460 status = pj_sock_sendto(ssock, sendbuf, &bytes, 0,
461 &addr, sizeof(addr));
462
463 if (status != PJ_SUCCESS) {
464 app_perror("sendto error", status);
465 return -200;
466 }
467
468 /* Now unregister and close socket. */
469 pj_ioqueue_unregister(key);
Benny Prijono8d317a02006-03-22 11:49:19 +0000470
471 /* Poll ioqueue. */
472 timeout.sec = 1; timeout.msec = 0;
473 pj_ioqueue_poll(ioqueue, &timeout);
474
475 /* Must NOT receive any packets after socket is closed! */
476 if (packet_cnt > 0) {
477 PJ_LOG(3,(THIS_FILE, "....errror: not expecting to receive packet "
478 "after socket has been closed"));
479 return -210;
480 }
481
482 /* Success */
483 pj_sock_close(ssock);
484 pj_ioqueue_destroy(ioqueue);
485
486 pj_pool_release(pool);
487
488 return 0;
489}
490
491
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000492/*
493 * Testing with many handles.
494 * This will just test registering PJ_IOQUEUE_MAX_HANDLES count
495 * of sockets to the ioqueue.
496 */
497static int many_handles_test(void)
498{
499 enum { MAX = PJ_IOQUEUE_MAX_HANDLES };
500 pj_pool_t *pool;
501 pj_ioqueue_t *ioqueue;
502 pj_sock_t *sock;
503 pj_ioqueue_key_t **key;
504 pj_status_t rc;
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000505 int count, i; /* must be signed */
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000506
507 PJ_LOG(3,(THIS_FILE,"...testing with so many handles"));
508
509 pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
510 if (!pool)
511 return PJ_ENOMEM;
512
513 key = pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*));
514 sock = pj_pool_alloc(pool, MAX*sizeof(pj_sock_t));
515
516 /* Create IOQueue */
517 rc = pj_ioqueue_create(pool, MAX, &ioqueue);
518 if (rc != PJ_SUCCESS || ioqueue == NULL) {
519 app_perror("...error in pj_ioqueue_create", rc);
520 return -10;
521 }
522
523 /* Register as many sockets. */
524 for (count=0; count<MAX; ++count) {
525 sock[count] = PJ_INVALID_SOCKET;
526 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[count]);
527 if (rc != PJ_SUCCESS || sock[count] == PJ_INVALID_SOCKET) {
528 PJ_LOG(3,(THIS_FILE, "....unable to create %d-th socket, rc=%d",
529 count, rc));
530 break;
531 }
532 key[count] = NULL;
533 rc = pj_ioqueue_register_sock(pool, ioqueue, sock[count],
534 NULL, &test_cb, &key[count]);
535 if (rc != PJ_SUCCESS || key[count] == NULL) {
536 PJ_LOG(3,(THIS_FILE, "....unable to register %d-th socket, rc=%d",
537 count, rc));
538 return -30;
539 }
540 }
541
542 /* Test complete. */
543
544 /* Now deregister and close all handles. */
545
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000546 /* NOTE for RTEMS:
547 * It seems that the order of close(sock) is pretty important here.
548 * If we close the sockets with the same order as when they were created,
549 * RTEMS doesn't seem to reuse the sockets, thus next socket created
550 * will have descriptor higher than the last socket created.
551 * If we close the sockets in the reverse order, then the descriptor will
552 * get reused.
553 * This used to cause problem with select ioqueue, since the ioqueue
554 * always gives FD_SETSIZE for the first select() argument. This ioqueue
555 * behavior can be changed with setting PJ_SELECT_NEEDS_NFDS macro.
556 */
557 for (i=count-1; i>=0; --i) {
558 ///for (i=0; i<count; ++i) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000559 rc = pj_ioqueue_unregister(key[i]);
560 if (rc != PJ_SUCCESS) {
561 app_perror("...error in pj_ioqueue_unregister", rc);
562 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000563 }
564
565 rc = pj_ioqueue_destroy(ioqueue);
566 if (rc != PJ_SUCCESS) {
567 app_perror("...error in pj_ioqueue_destroy", rc);
568 }
569
570 pj_pool_release(pool);
571
572 PJ_LOG(3,(THIS_FILE,"....many_handles_test() ok"));
573
574 return 0;
575}
576
577/*
578 * Multi-operation test.
579 */
580
581/*
582 * Benchmarking IOQueue
583 */
584static int bench_test(int bufsize, int inactive_sock_count)
585{
586 pj_sock_t ssock=-1, csock=-1;
587 pj_sockaddr_in addr;
588 pj_pool_t *pool = NULL;
589 pj_sock_t *inactive_sock=NULL;
590 pj_ioqueue_op_key_t *inactive_read_op;
591 char *send_buf, *recv_buf;
592 pj_ioqueue_t *ioque = NULL;
593 pj_ioqueue_key_t *skey, *ckey, *key;
594 pj_timestamp t1, t2, t_elapsed;
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000595 int rc=0, i; /* i must be signed */
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000596 pj_str_t temp;
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000597 char errbuf[PJ_ERR_MSG_SIZE];
598
599 TRACE__((THIS_FILE, " bench test %d", inactive_sock_count));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000600
601 // Create pool.
602 pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
603
604 // Allocate buffers for send and receive.
605 send_buf = (char*)pj_pool_alloc(pool, bufsize);
606 recv_buf = (char*)pj_pool_alloc(pool, bufsize);
607
608 // Allocate sockets for sending and receiving.
609 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock);
610 if (rc == PJ_SUCCESS) {
611 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock);
612 } else
613 csock = PJ_INVALID_SOCKET;
614 if (rc != PJ_SUCCESS) {
615 app_perror("...error: pj_sock_socket()", rc);
616 goto on_error;
617 }
618
619 // Bind server socket.
Benny Prijono37e8d332006-01-20 21:03:36 +0000620 pj_memset(&addr, 0, sizeof(addr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000621 addr.sin_family = PJ_AF_INET;
622 addr.sin_port = pj_htons(PORT);
623 if (pj_sock_bind(ssock, &addr, sizeof(addr)))
624 goto on_error;
625
626 pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES);
627
628 // Create I/O Queue.
629 rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
630 if (rc != PJ_SUCCESS) {
631 app_perror("...error: pj_ioqueue_create()", rc);
632 goto on_error;
633 }
634
635 // Allocate inactive sockets, and bind them to some arbitrary address.
636 // Then register them to the I/O queue, and start a read operation.
637 inactive_sock = (pj_sock_t*)pj_pool_alloc(pool,
638 inactive_sock_count*sizeof(pj_sock_t));
639 inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool,
640 inactive_sock_count*sizeof(pj_ioqueue_op_key_t));
Benny Prijono37e8d332006-01-20 21:03:36 +0000641 pj_memset(&addr, 0, sizeof(addr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000642 addr.sin_family = PJ_AF_INET;
643 for (i=0; i<inactive_sock_count; ++i) {
644 pj_ssize_t bytes;
645
646 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &inactive_sock[i]);
647 if (rc != PJ_SUCCESS || inactive_sock[i] < 0) {
648 app_perror("...error: pj_sock_socket()", rc);
649 goto on_error;
650 }
651 if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) {
652 pj_sock_close(inactive_sock[i]);
653 inactive_sock[i] = PJ_INVALID_SOCKET;
654 app_perror("...error: pj_sock_bind()", rc);
655 goto on_error;
656 }
657 rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i],
658 NULL, &test_cb, &key);
659 if (rc != PJ_SUCCESS) {
660 pj_sock_close(inactive_sock[i]);
661 inactive_sock[i] = PJ_INVALID_SOCKET;
662 app_perror("...error(1): pj_ioqueue_register_sock()", rc);
663 PJ_LOG(3,(THIS_FILE, "....i=%d", i));
664 goto on_error;
665 }
666 bytes = bufsize;
667 rc = pj_ioqueue_recv(key, &inactive_read_op[i], recv_buf, &bytes, 0);
Benny Prijono37e8d332006-01-20 21:03:36 +0000668 if (rc != PJ_EPENDING) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000669 pj_sock_close(inactive_sock[i]);
670 inactive_sock[i] = PJ_INVALID_SOCKET;
671 app_perror("...error: pj_ioqueue_read()", rc);
672 goto on_error;
673 }
674 }
675
676 // Register server and client socket.
677 // We put this after inactivity socket, hopefully this can represent the
678 // worst waiting time.
679 rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
680 &test_cb, &skey);
681 if (rc != PJ_SUCCESS) {
682 app_perror("...error(2): pj_ioqueue_register_sock()", rc);
683 goto on_error;
684 }
685
686 rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL,
687 &test_cb, &ckey);
688 if (rc != PJ_SUCCESS) {
689 app_perror("...error(3): pj_ioqueue_register_sock()", rc);
690 goto on_error;
691 }
692
693 // Set destination address to send the packet.
694 pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT);
695
696 // Test loop.
697 t_elapsed.u64 = 0;
698 for (i=0; i<LOOP; ++i) {
699 pj_ssize_t bytes;
700 pj_ioqueue_op_key_t read_op, write_op;
701
702 // Randomize send buffer.
703 pj_create_random_string(send_buf, bufsize);
704
705 // Start reading on the server side.
706 bytes = bufsize;
707 rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
Benny Prijono37e8d332006-01-20 21:03:36 +0000708 if (rc != PJ_EPENDING) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000709 app_perror("...error: pj_ioqueue_read()", rc);
710 break;
711 }
712
713 // Starts send on the client side.
714 bytes = bufsize;
715 rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0,
716 &addr, sizeof(addr));
717 if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000718 app_perror("...error: pj_ioqueue_write()", rc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000719 break;
720 }
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000721 if (rc == PJ_SUCCESS) {
722 if (bytes < 0) {
723 app_perror("...error: pj_ioqueue_sendto()", -bytes);
724 break;
725 }
726 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000727
728 // Begin time.
729 pj_get_timestamp(&t1);
730
731 // Poll the queue until we've got completion event in the server side.
732 callback_read_key = NULL;
733 callback_read_size = 0;
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000734 TRACE__((THIS_FILE, " waiting for key = %p", skey));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000735 do {
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000736 pj_time_val timeout = { 1, 0 };
737 rc = pj_ioqueue_poll(ioque, &timeout);
738 TRACE__((THIS_FILE, " poll rc=%d", rc));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000739 } while (rc >= 0 && callback_read_key != skey);
740
741 // End time.
742 pj_get_timestamp(&t2);
743 t_elapsed.u64 += (t2.u64 - t1.u64);
744
Benny Prijono37e8d332006-01-20 21:03:36 +0000745 if (rc < 0) {
746 app_perror(" error: pj_ioqueue_poll", -rc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000747 break;
Benny Prijono37e8d332006-01-20 21:03:36 +0000748 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000749
750 // Compare recv buffer with send buffer.
751 if (callback_read_size != bufsize ||
Benny Prijono37e8d332006-01-20 21:03:36 +0000752 pj_memcmp(send_buf, recv_buf, bufsize))
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000753 {
Benny Prijono37e8d332006-01-20 21:03:36 +0000754 rc = -10;
755 PJ_LOG(3,(THIS_FILE, " error: size/buffer mismatch"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000756 break;
757 }
758
759 // Poll until all events are exhausted, before we start the next loop.
760 do {
761 pj_time_val timeout = { 0, 10 };
762 rc = pj_ioqueue_poll(ioque, &timeout);
763 } while (rc>0);
764
765 rc = 0;
766 }
767
768 // Print results
769 if (rc == 0) {
770 pj_timestamp tzero;
771 pj_uint32_t usec_delay;
772
773 tzero.u32.hi = tzero.u32.lo = 0;
774 usec_delay = pj_elapsed_usec( &tzero, &t_elapsed);
775
776 PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d",
777 bufsize, inactive_sock_count, usec_delay));
778
779 } else {
Benny Prijono37e8d332006-01-20 21:03:36 +0000780 PJ_LOG(2, (THIS_FILE, "...ERROR rc=%d (buf:%d, fds:%d)",
781 rc, bufsize, inactive_sock_count+2));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000782 }
783
784 // Cleaning up.
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000785 for (i=inactive_sock_count-1; i>=0; --i) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000786 pj_sock_close(inactive_sock[i]);
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000787 }
788
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000789 pj_sock_close(ssock);
790 pj_sock_close(csock);
791
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000792
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000793 pj_ioqueue_destroy(ioque);
794 pj_pool_release( pool);
Benny Prijono37e8d332006-01-20 21:03:36 +0000795 return rc;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000796
797on_error:
798 PJ_LOG(1,(THIS_FILE, "...ERROR: %s",
799 pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
800 if (ssock)
801 pj_sock_close(ssock);
802 if (csock)
803 pj_sock_close(csock);
804 for (i=0; i<inactive_sock_count && inactive_sock &&
805 inactive_sock[i]!=PJ_INVALID_SOCKET; ++i)
806 {
807 pj_sock_close(inactive_sock[i]);
808 }
809 if (ioque != NULL)
810 pj_ioqueue_destroy(ioque);
811 pj_pool_release( pool);
812 return -1;
813}
814
815int udp_ioqueue_test()
816{
817 int status;
818 int bufsize, sock_count;
819
820 PJ_LOG(3, (THIS_FILE, "...compliance test (%s)", pj_ioqueue_name()));
821 if ((status=compliance_test()) != 0) {
822 return status;
823 }
824 PJ_LOG(3, (THIS_FILE, "....compliance test ok"));
825
Benny Prijono8d317a02006-03-22 11:49:19 +0000826
827 PJ_LOG(3, (THIS_FILE, "...unregister test (%s)", pj_ioqueue_name()));
828 if ((status=unregister_test()) != 0) {
829 return status;
830 }
831 PJ_LOG(3, (THIS_FILE, "....unregister test ok"));
832
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000833 if ((status=many_handles_test()) != 0) {
834 return status;
835 }
836
837 PJ_LOG(4, (THIS_FILE, "...benchmarking different buffer size:"));
838 PJ_LOG(4, (THIS_FILE, "... note: buf=bytes sent, fds=# of fds, "
839 "elapsed=in timer ticks"));
840
841 PJ_LOG(3, (THIS_FILE, "...Benchmarking poll times for %s:", pj_ioqueue_name()));
842 PJ_LOG(3, (THIS_FILE, "...====================================="));
843 PJ_LOG(3, (THIS_FILE, "...Buf.size #inactive-socks Time/poll"));
844 PJ_LOG(3, (THIS_FILE, "... (bytes) (nanosec)"));
845 PJ_LOG(3, (THIS_FILE, "...====================================="));
846
847 for (bufsize=BUF_MIN_SIZE; bufsize <= BUF_MAX_SIZE; bufsize *= 2) {
Benny Prijono37e8d332006-01-20 21:03:36 +0000848 if ((status=bench_test(bufsize, SOCK_INACTIVE_MIN)) != 0)
849 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000850 }
851 bufsize = 512;
852 for (sock_count=SOCK_INACTIVE_MIN+2;
853 sock_count<=SOCK_INACTIVE_MAX+2;
854 sock_count *= 2)
855 {
856 //PJ_LOG(3,(THIS_FILE, "...testing with %d fds", sock_count));
Benny Prijono37e8d332006-01-20 21:03:36 +0000857 if ((status=bench_test(bufsize, sock_count-2)) != 0)
858 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000859 }
860 return 0;
861}
862
863#else
864/* To prevent warning about "translation unit is empty"
865 * when this test is disabled.
866 */
867int dummy_uiq_udp;
868#endif /* INCLUDE_UDP_IOQUEUE_TEST */
869
870