blob: 9d2fa6524580500be847fba6bc80f64734da7457 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include "test.h"
21
22#if INCLUDE_CONCUR_TEST
23
24#define THIS_FILE "concur_test.c"
25
26/****************************************************************************/
27#define WORKER_THREAD_CNT 4
28#define SERVER_THREAD_CNT 4
29#define MAX_SOCK_CLIENTS 80
30
31struct stun_test_session
32{
33 pj_stun_config stun_cfg;
34
35 pj_lock_t *lock;
36
37 pj_thread_t *worker_threads[WORKER_THREAD_CNT];
38
39 pj_sock_t server_sock;
40 int server_port;
41 pj_thread_t *server_threads[SERVER_THREAD_CNT];
42 pj_event_t *server_event;
43
44 pj_bool_t thread_quit_flag;
45
46 /* Test parameters: */
47 struct {
48 int client_got_response;
49
50 pj_bool_t server_wait_for_event;
51 pj_bool_t server_drop_request;
52 int client_sleep_after_start;
53 int client_sleep_before_destroy;
54 } param;
55};
56
57static int server_thread_proc(void *p)
58{
59 struct stun_test_session *test_sess = (struct stun_test_session*)p;
60 pj_pool_t *pool;
61 pj_status_t status;
62
63 PJ_LOG(4,(THIS_FILE, "Server thread running"));
64
65 pool = pj_pool_create(test_sess->stun_cfg.pf, "server", 512, 512, NULL);
66
67 while (!test_sess->thread_quit_flag) {
68 pj_time_val timeout = {0, 10};
69 pj_fd_set_t rdset;
70 int n;
71
72 /* Serve client */
73 PJ_FD_ZERO(&rdset);
74 PJ_FD_SET(test_sess->server_sock, &rdset);
75 n = pj_sock_select((int)test_sess->server_sock+1, &rdset,
76 NULL, NULL, &timeout);
77 if (n==1 && PJ_FD_ISSET(test_sess->server_sock, &rdset)) {
78 pj_uint8_t pkt[512];
79 pj_ssize_t pkt_len;
80 pj_size_t res_len;
81 pj_sockaddr client_addr;
82 int addr_len;
83
84 pj_stun_msg *stun_req, *stun_res;
85
86 pj_pool_reset(pool);
87
88 /* Got query */
89 pkt_len = sizeof(pkt);
90 addr_len = sizeof(client_addr);
91 status = pj_sock_recvfrom(test_sess->server_sock, pkt, &pkt_len,
92 0, &client_addr, &addr_len);
93 if (status != PJ_SUCCESS) {
94 continue;
95 }
96
97 status = pj_stun_msg_decode(pool, pkt, pkt_len,
98 PJ_STUN_IS_DATAGRAM,
99 &stun_req, NULL, NULL);
100 if (status != PJ_SUCCESS) {
101 PJ_PERROR(1,(THIS_FILE, status, "STUN request decode error"));
102 continue;
103 }
104
105 status = pj_stun_msg_create_response(pool, stun_req,
106 PJ_STUN_SC_BAD_REQUEST, NULL,
107 &stun_res);
108 if (status != PJ_SUCCESS) {
109 PJ_PERROR(1,(THIS_FILE, status, "STUN create response error"));
110 continue;
111 }
112
113 status = pj_stun_msg_encode(stun_res, pkt, sizeof(pkt), 0,
114 NULL, &res_len);
115 if (status != PJ_SUCCESS) {
116 PJ_PERROR(1,(THIS_FILE, status, "STUN encode error"));
117 continue;
118 }
119
120 /* Ignore request */
121 if (test_sess->param.server_drop_request)
122 continue;
123
124 /* Wait for signal to continue */
125 if (test_sess->param.server_wait_for_event)
126 pj_event_wait(test_sess->server_event);
127
128 pkt_len = res_len;
129 pj_sock_sendto(test_sess->server_sock, pkt, &pkt_len, 0,
130 &client_addr, pj_sockaddr_get_len(&client_addr));
131 }
132 }
133
134 pj_pool_release(pool);
135
136 PJ_LOG(4,(THIS_FILE, "Server thread quitting"));
137 return 0;
138}
139
140static int worker_thread_proc(void *p)
141{
142 struct stun_test_session *test_sess = (struct stun_test_session*)p;
143
144 PJ_LOG(4,(THIS_FILE, "Worker thread running"));
145
146 while (!test_sess->thread_quit_flag) {
147 pj_time_val timeout = {0, 10};
148 pj_timer_heap_poll(test_sess->stun_cfg.timer_heap, NULL);
149 pj_ioqueue_poll(test_sess->stun_cfg.ioqueue, &timeout);
150 }
151
152 PJ_LOG(4,(THIS_FILE, "Worker thread quitting"));
153 return 0;
154}
155
156static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
157 pj_stun_sock_op op,
158 pj_status_t status)
159{
160 struct stun_test_session *test_sess = (struct stun_test_session*)pj_stun_sock_get_user_data(stun_sock);
161
162 PJ_UNUSED_ARG(op);
163 PJ_UNUSED_ARG(status);
164
165 test_sess->param.client_got_response++;
166 return PJ_TRUE;
167}
168
169static int stun_destroy_test_session(struct stun_test_session *test_sess)
170{
171
172 unsigned i;
173 pj_stun_sock_cb stun_cb;
174 pj_status_t status;
175 pj_stun_sock *stun_sock[MAX_SOCK_CLIENTS];
176
177 pj_bzero(&stun_cb, sizeof(stun_cb));
178 stun_cb.on_status = &stun_sock_on_status;
179
180 pj_event_reset(test_sess->server_event);
181
182 /* Create all clients first */
183 for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
184 char name[10];
185 sprintf(name, "stun%02d", i);
186 status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(),
187 &stun_cb, NULL, test_sess,
188 &stun_sock[i]);
189 if (status != PJ_SUCCESS) {
190 PJ_PERROR(1,(THIS_FILE, status, "Error creating stun socket"));
191 return -10;
192 }
193 }
194
195 /* Start resolution */
196 for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
197 pj_str_t server_ip = pj_str("127.0.0.1");
198 status = pj_stun_sock_start(stun_sock[i], &server_ip,
199 (pj_uint16_t)test_sess->server_port, NULL);
200 if (status != PJ_SUCCESS) {
201 PJ_PERROR(1,(THIS_FILE, status, "Error starting stun socket"));
202 return -20;
203 }
204 }
205
206 /* settle down */
207 pj_thread_sleep(test_sess->param.client_sleep_after_start);
208
209 /* Resume server threads */
210 pj_event_set(test_sess->server_event);
211
212 pj_thread_sleep(test_sess->param.client_sleep_before_destroy);
213
214 /* Destroy clients */
215 for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
216 status = pj_stun_sock_destroy(stun_sock[i]);
217 if (status != PJ_SUCCESS) {
218 PJ_PERROR(1,(THIS_FILE, status, "Error destroying stun socket"));
219 }
220 }
221
222 return 0;
223}
224
225static int stun_destroy_test(void)
226{
227 enum { LOOP = 500 };
228 struct stun_test_session test_sess;
229 pj_sockaddr bind_addr;
230 int addr_len;
231 pj_caching_pool cp;
232 pj_pool_t *pool;
233 unsigned i;
234 pj_status_t status;
235 int rc = 0;
236
237 PJ_LOG(3,(THIS_FILE, " STUN destroy concurrency test"));
238
239 pj_bzero(&test_sess, sizeof(test_sess));
240
241 pj_caching_pool_init(&cp, NULL, 0);
242 pool = pj_pool_create(&cp.factory, "testsess", 512, 512, NULL);
243
244 pj_stun_config_init(&test_sess.stun_cfg, &cp.factory, 0, NULL, NULL);
245
246 status = pj_timer_heap_create(pool, 1023, &test_sess.stun_cfg.timer_heap);
247 pj_assert(status == PJ_SUCCESS);
248
249 status = pj_lock_create_recursive_mutex(pool, NULL, &test_sess.lock);
250 pj_assert(status == PJ_SUCCESS);
251
252 pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, PJ_TRUE);
253 pj_assert(status == PJ_SUCCESS);
254
255 status = pj_ioqueue_create(pool, 512, &test_sess.stun_cfg.ioqueue);
256 pj_assert(status == PJ_SUCCESS);
257
258 pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &test_sess.server_sock);
259 pj_sockaddr_init(pj_AF_INET(), &bind_addr, NULL, 0);
260 status = pj_sock_bind(test_sess.server_sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
261 pj_assert(status == PJ_SUCCESS);
262
263 addr_len = sizeof(bind_addr);
264 status = pj_sock_getsockname(test_sess.server_sock, &bind_addr, &addr_len);
265 pj_assert(status == PJ_SUCCESS);
266
267 test_sess.server_port = pj_sockaddr_get_port(&bind_addr);
268
269 status = pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, &test_sess.server_event);
270 pj_assert(status == PJ_SUCCESS);
271
272 for (i=0; i<SERVER_THREAD_CNT; ++i) {
273 status = pj_thread_create(pool, NULL,
274 &server_thread_proc, &test_sess,
275 0, 0, &test_sess.server_threads[i]);
276 pj_assert(status == PJ_SUCCESS);
277 }
278
279 for (i=0; i<WORKER_THREAD_CNT; ++i) {
280 status = pj_thread_create(pool, NULL,
281 &worker_thread_proc, &test_sess,
282 0, 0, &test_sess.worker_threads[i]);
283 pj_assert(status == PJ_SUCCESS);
284 }
285
286 /* Test 1: Main thread calls destroy while callback is processing response */
287 PJ_LOG(3,(THIS_FILE, " Destroy in main thread while callback is running"));
288 for (i=0; i<LOOP; ++i) {
289 int sleep = pj_rand() % 5;
290
291 PJ_LOG(3,(THIS_FILE, " Try %-3d of %d", i+1, LOOP));
292
293 /* Test 1: destroy at the same time when receiving response */
294 pj_bzero(&test_sess.param, sizeof(test_sess.param));
295 test_sess.param.client_sleep_after_start = 20;
296 test_sess.param.client_sleep_before_destroy = sleep;
297 test_sess.param.server_wait_for_event = PJ_TRUE;
298 stun_destroy_test_session(&test_sess);
299 PJ_LOG(3,(THIS_FILE,
300 " stun test a: sleep delay:%d: clients with response: %d",
301 sleep, test_sess.param.client_got_response));
302
303 /* Test 2: destroy at the same time with STUN retransmit timer */
304 test_sess.param.server_drop_request = PJ_TRUE;
305 test_sess.param.client_sleep_after_start = 0;
306 test_sess.param.client_sleep_before_destroy = PJ_STUN_RTO_VALUE;
307 test_sess.param.server_wait_for_event = PJ_FALSE;
308 stun_destroy_test_session(&test_sess);
309 PJ_LOG(3,(THIS_FILE, " stun test b: retransmit concurrency"));
310
311 /* Test 3: destroy at the same time with receiving response
312 * AND STUN retransmit timer */
313 test_sess.param.client_got_response = 0;
314 test_sess.param.server_drop_request = PJ_FALSE;
315 test_sess.param.client_sleep_after_start = PJ_STUN_RTO_VALUE;
316 test_sess.param.client_sleep_before_destroy = 0;
317 test_sess.param.server_wait_for_event = PJ_TRUE;
318 stun_destroy_test_session(&test_sess);
319 PJ_LOG(3,(THIS_FILE,
320 " stun test c: clients with response: %d",
321 test_sess.param.client_got_response));
322
323 pj_thread_sleep(10);
324
325 ice_one_conc_test(&test_sess.stun_cfg, PJ_FALSE);
326
327 pj_thread_sleep(10);
328 }
329
330 /* Avoid compiler warning */
331 goto on_return;
332
333
334on_return:
335 test_sess.thread_quit_flag = PJ_TRUE;
336
337 for (i=0; i<SERVER_THREAD_CNT; ++i) {
338 pj_thread_join(test_sess.server_threads[i]);
339 }
340
341 for (i=0; i<WORKER_THREAD_CNT; ++i) {
342 pj_thread_join(test_sess.worker_threads[i]);
343 }
344
345 pj_event_destroy(test_sess.server_event);
346 pj_sock_close(test_sess.server_sock);
347 pj_ioqueue_destroy(test_sess.stun_cfg.ioqueue);
348 pj_timer_heap_destroy(test_sess.stun_cfg.timer_heap);
349
350 pj_pool_release(pool);
351 pj_caching_pool_destroy(&cp);
352
353 PJ_LOG(3,(THIS_FILE, " Done. rc=%d", rc));
354 return rc;
355}
356
357
358int concur_test(void)
359{
360 int rc = 0;
361
362 rc += stun_destroy_test();
363
364 return 0;
365}
366
367#endif /* INCLUDE_CONCUR_TEST */