blob: c1c7cf13cbd484044703d0a9fb4d5024d96067d4 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2013, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36#include "tests.h"
37#include <unistd.h>
38#include <limits.h>
39
40/* Wrapper for pthread_barrier stuff on Mac OS X */
41#ifndef HAVE_PTHREAD_BAR
42
43#define PTHREAD_BARRIER_SERIAL_THREAD 1
44typedef struct {
45 int count;
46 int entered;
47 int serial;
48 pthread_mutex_t mutex;
49 pthread_cond_t cond;
50} pthread_barrier_t;
51
52int pthread_barrier_init(pthread_barrier_t * barrier, int * barrier_attr, int count)
53{
54 memset(barrier, 0, sizeof(pthread_barrier_t));
55 barrier->count = count;
56 pthread_mutex_init(&barrier->mutex, NULL);
57 pthread_cond_init(&barrier->cond, NULL);
58 return 0;
59}
60
61int pthread_barrier_destroy(pthread_barrier_t * barrier)
62{
63 pthread_mutex_destroy(&barrier->mutex);
64 pthread_cond_destroy(&barrier->cond);
65 return 0;
66}
67
68int pthread_barrier_wait(pthread_barrier_t * barrier)
69{
70 int ret = 0;
71 int serial;
72 pthread_mutex_lock(&barrier->mutex);
73 serial = barrier->serial;
74
75 /* first thread gets the special value */
76 if (barrier->entered++ == 0)
77 ret = PTHREAD_BARRIER_SERIAL_THREAD;
78
79 /* Count was achieved? */
80 if (barrier->entered == barrier->count) {
81 /* Ok, increase serial, reset number of threads, and signal everyone */
82 barrier->entered = 0;
83 barrier->serial++;
84 pthread_cond_broadcast(&barrier->cond);
85 } else {
86 do {
87 pthread_cond_wait(&barrier->cond, &barrier->mutex);
88 } while (barrier->serial == serial);
89 /* this protects against spurious wakes */
90 }
91 pthread_mutex_unlock(&barrier->mutex);
92 return 0;
93}
94
95#endif /* HAVE_PTHREAD_BAR */
96
97/* Structure for testing threshold function */
98static struct thrh_test {
99 struct fifo * queue; /* pointer to the queue */
100 int h_calls; /* number of calls of h_cb */
101 int l_calls; /* number of calls of l_cb */
102} thrh_td;
103
104/* Callbacks for threasholds test */
105void thrh_cb_h(struct fifo *queue, void **data)
106{
107 if (thrh_td.h_calls == thrh_td.l_calls) {
108 CHECK( NULL, *data );
109 *data = &thrh_td;
110 } else {
111 CHECK( *data, &thrh_td );
112 }
113 CHECK( queue, thrh_td.queue );
114
115 /* Update the count */
116 thrh_td.h_calls ++;
117}
118void thrh_cb_l(struct fifo *queue, void **data)
119{
120 CHECK( 1, data ? 1 : 0 );
121 CHECK( *data, &thrh_td );
122
123 /* Check the queue parameter is correct */
124 CHECK( queue, thrh_td.queue );
125
126 /* Update the count */
127 thrh_td.l_calls ++;
128 /* Cleanup the data ptr if needed */
129 if (thrh_td.l_calls == thrh_td.h_calls)
130 *data = NULL;
131 /* done */
132}
133
134
135/* Structure that is passed to the test function */
136struct test_data {
137 struct fifo * queue; /* pointer to the queue */
138 pthread_barrier_t * bar; /* if not NULL, barrier to synchronize before getting messages */
139 struct timespec * ts; /* if not NULL, use a timedget instead of a get */
140 int nbr; /* number of messages to retrieve from the queue */
141};
142
143/* The test function, to be threaded */
144static void * test_fct(void * data)
145{
146 int ret = 0, i;
147 struct msg * msg = NULL;
148 struct test_data * td = (struct test_data *) data;
149
150 if (td->bar != NULL) {
151 ret = pthread_barrier_wait(td->bar);
152 if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
153 CHECK( 0, ret);
154 } else {
155 CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* just for the traces */
156 }
157 }
158
159 for (i=0; i< td->nbr; i++) {
160 if (td->ts != NULL) {
161 CHECK( 0, fd_fifo_timedget(td->queue, &msg, td->ts) );
162 } else {
163 CHECK( 0, fd_fifo_get(td->queue, &msg) );
164 }
165 }
166
167 return NULL;
168}
169
170/* The test function, to be threaded */
171static int iter = 0;
172static void * test_fct2(void * data)
173{
174 int i;
175 int * item;
176 struct test_data * td = (struct test_data *) data;
177
178 for (i=0; i< td->nbr; i++) {
179 item = malloc(sizeof(int));
180 CHECK( 1, item ? 1 : 0 );
181 *item = i;
182 CHECK( 0, fd_fifo_post(td->queue, &item) );
183 iter++;
184 }
185
186 return NULL;
187}
188
189
190/* Main test routine */
191int main(int argc, char *argv[])
192{
193 struct timespec ts;
194
195 struct msg * msg1 = NULL;
196 struct msg * msg2 = NULL;
197 struct msg * msg3 = NULL;
198
199 /* First, initialize the daemon modules */
200 INIT_FD();
201
202 /* Prolog: create the messages */
203 {
204 struct dict_object * acr_model = NULL;
205 struct dict_object * cer_model = NULL;
206 struct dict_object * dwr_model = NULL;
207
208 CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
209 CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model, ENOENT ) );
210 CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &dwr_model, ENOENT ) );
211 CHECK( 0, fd_msg_new ( acr_model, 0, &msg1 ) );
212 CHECK( 0, fd_msg_new ( cer_model, 0, &msg2 ) );
213 CHECK( 0, fd_msg_new ( dwr_model, 0, &msg3 ) );
214 }
215
216 /* Basic operation */
217 {
218 struct fifo * queue = NULL;
219 struct msg * msg = NULL;
220 int max;
221 long long count;
222
223 /* Create the queue */
224 CHECK( 0, fd_fifo_new(&queue, 0) );
225
226 /* Check the count is 0 */
227 CHECK( 0, fd_fifo_length(queue) );
228
229 /* Now enqueue */
230 msg = msg1;
231 CHECK( 0, fd_fifo_post(queue, &msg) );
232 msg = msg2;
233 CHECK( 0, fd_fifo_post(queue, &msg) );
234 msg = msg3;
235 CHECK( 0, fd_fifo_post(queue, &msg) );
236
237 /* Check the count is 3 */
238 CHECK( 3, fd_fifo_length(queue) );
239
240 /* Retrieve the first message using fd_fifo_get */
241 CHECK( 0, fd_fifo_get(queue, &msg) );
242 CHECK( msg1, msg);
243 CHECK( 2, fd_fifo_length(queue) );
244
245 /* Retrieve the second message using fd_fifo_timedget */
246 CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
247 ts.tv_sec += 1; /* Set the timeout to 1 second */
248 CHECK( 0, fd_fifo_timedget(queue, &msg, &ts) );
249 CHECK( msg2, msg);
250 CHECK( 1, fd_fifo_length(queue) );
251
252 /* Retrieve the third message using meq_tryget */
253 CHECK( 0, fd_fifo_tryget(queue, &msg) );
254 CHECK( msg3, msg);
255 CHECK( 0, fd_fifo_length(queue) );
256
257 /* Check that another meq_tryget does not block */
258 CHECK( EWOULDBLOCK, fd_fifo_tryget(queue, &msg) );
259 CHECK( 0, fd_fifo_length(queue) );
260
261 /* Check the timedget actually timesout */
262 CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
263 ts.tv_nsec += 1000000; /* 1 millisecond */
264 if (ts.tv_nsec >= 1000000000L) {
265 ts.tv_nsec -= 1000000000L;
266 ts.tv_sec += 1;
267 }
268 CHECK( ETIMEDOUT, fd_fifo_timedget(queue, &msg, &ts) );
269 CHECK( 0, fd_fifo_length(queue) );
270
271 /* Post & get another message */
272 msg = msg1;
273 CHECK( 0, fd_fifo_post(queue, &msg) );
274 CHECK( 0, fd_fifo_timedget(queue, &msg, &ts) );
275 CHECK( msg1, msg);
276
277 /* Check some statistics */
278 CHECK( 0, fd_fifo_getstats(queue, NULL, NULL, &max, &count, NULL, NULL, NULL) );
279 CHECK( 3, max );
280 CHECK( 4, count );
281
282 /* We're done for basic tests */
283 CHECK( 0, fd_fifo_del(&queue) );
284 }
285
286 /* Test robustness, ensure no messages are lost */
287 {
288#define NBR_MSG 200
289#define NBR_THREADS 60
290 struct fifo *queue = NULL;
291 pthread_barrier_t bar;
292 struct test_data td_1;
293 struct test_data td_2;
294 struct msg *msgs[NBR_MSG * NBR_THREADS * 2], *msg;
295 pthread_t thr [NBR_THREADS * 2];
296 struct dict_object *dwr_model = NULL;
297 int i;
298 int nbr_threads;
299#ifdef _POSIX_THREAD_THREADS_MAX
300 nbr_threads = _POSIX_THREAD_THREADS_MAX;
301#else /* _POSIX_THREAD_THREADS_MAX */
302 nbr_threads = sysconf(_SC_THREAD_THREADS_MAX);
303#endif /* _POSIX_THREAD_THREADS_MAX */
304 if ((nbr_threads <= 0) || (nbr_threads > NBR_THREADS * 2)) {
305 nbr_threads = NBR_THREADS;
306 } else {
307 TRACE_DEBUG(INFO, "Local limit on number of threads: %d", nbr_threads);
308 /* The local limit is below NBR_THREADS */
309 nbr_threads = (nbr_threads / 2) - 1;
310 /* Ensure we create at least a few threads! */
311 CHECK( 1, nbr_threads >= 10 ? 1 : 0 );
312 }
313
314 /* Create the queue */
315 CHECK( 0, fd_fifo_new(&queue, 0) );
316
317 /* Create the barrier */
318 CHECK( 0, pthread_barrier_init(&bar, NULL, nbr_threads * 2 + 1) );
319
320 /* Initialize the ts */
321 CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
322 ts.tv_sec += 20; /* Set the timeout to 20 second */
323
324 /* Create the messages */
325 CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &dwr_model, ENOENT ) );
326 for (i = 0; i < NBR_MSG * nbr_threads * 2; i++) {
327 CHECK( 0, fd_msg_new ( dwr_model, 0, &msgs[i] ) );
328 }
329
330 /* Initialize the test data structures */
331 td_1.queue = queue;
332 td_1.bar = &bar;
333 td_1.ts = &ts;
334 td_1.nbr = NBR_MSG;
335 td_2.queue = queue;
336 td_2.bar = &bar;
337 td_2.ts = NULL;
338 td_2.nbr = NBR_MSG;
339
340 /* Create the threads */
341 for (i=0; i < nbr_threads * 2; i++) {
342 CHECK( 0, pthread_create( &thr[i], NULL, test_fct, (i & 1) ? &td_1 : &td_2 ) );
343 }
344
345 /* Synchronize everyone */
346 {
347 int ret = pthread_barrier_wait(&bar);
348 if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
349 CHECK( 0, ret);
350 } else {
351 CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* for trace only */
352 }
353 }
354
355 /* Now post all the messages */
356 for (i=0; i < NBR_MSG * nbr_threads * 2; i++) {
357 msg = msgs[i];
358 CHECK( 0, fd_fifo_post(queue, &msg) );
359 }
360
361 /* Join all threads. This blocks if messages are lost... */
362 for (i=0; i < nbr_threads * 2; i++) {
363 CHECK( 0, pthread_join( thr[i], NULL ) );
364 }
365
366 /* Check the count of the queue is back to 0 */
367 CHECK( 0, fd_fifo_length(queue) );
368
369 /* Destroy this queue and the messages */
370 CHECK( 0, fd_fifo_del(&queue) );
371 for (i=0; i < NBR_MSG * nbr_threads * 2; i++) {
372 CHECK( 0, fd_msg_free( msgs[i] ) );
373 }
374 }
375
376 /* Test thread cancelation */
377 {
378 struct fifo *queue = NULL;
379 pthread_barrier_t bar;
380 struct test_data td;
381 pthread_t th;
382
383 /* Create the queue */
384 CHECK( 0, fd_fifo_new(&queue, 0) );
385
386 /* Create the barrier */
387 CHECK( 0, pthread_barrier_init(&bar, NULL, 2) );
388
389 /* Initialize the ts */
390 CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
391 ts.tv_sec += 10; /* Set the timeout to 10 second */
392
393 /* Initialize the test data structures */
394 td.queue = queue;
395 td.bar = &bar;
396 td.ts = &ts;
397 td.nbr = 1;
398
399 /* Create the thread */
400 CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) );
401
402 /* Wait for the thread to be running */
403 {
404 int ret = pthread_barrier_wait(&bar);
405 if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
406 CHECK( 0, ret);
407 } else {
408 CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret );
409 }
410 }
411
412 /* Now cancel the thread */
413 CHECK( 0, pthread_cancel( th ) );
414
415 /* Join it */
416 CHECK( 0, pthread_join( th, NULL ) );
417
418 /* Do the same with the other function */
419 td.ts = NULL;
420
421 /* Create the thread */
422 CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) );
423
424 /* Wait for the thread to be running */
425 {
426 int ret = pthread_barrier_wait(&bar);
427 if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
428 CHECK( 0, ret);
429 } else {
430 CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret );
431 }
432 }
433
434 /* Now cancel the thread */
435 CHECK( 0, pthread_cancel( th ) );
436
437 /* Join it */
438 CHECK( 0, pthread_join( th, NULL ) );
439
440 /* Destroy the queue */
441 CHECK( 0, fd_fifo_del(&queue) );
442 }
443
444 /* Test the threashold function */
445 {
446 struct fifo * queue = NULL;
447 int i;
448 struct msg * msg = NULL;
449
450 /* Create the queue */
451 CHECK( 0, fd_fifo_new(&queue, 0) );
452
453 /* Prepare the test data */
454 memset(&thrh_td, 0, sizeof(thrh_td));
455 thrh_td.queue = queue;
456
457 /* Set the thresholds for the queue */
458 CHECK( 0, fd_fifo_setthrhd ( queue, NULL, 6, thrh_cb_h, 4, thrh_cb_l ) );
459
460 /* Post 5 messages, no cb must be called. */
461 for (i=0; i<5; i++) {
462 msg = msg1;
463 CHECK( 0, fd_fifo_post(queue, &msg) );
464 } /* 5 msg in queue */
465 CHECK( 0, thrh_td.h_calls );
466 CHECK( 0, thrh_td.l_calls );
467
468 /* Get all these messages, and check again */
469 for (i=0; i<5; i++) {
470 CHECK( 0, fd_fifo_get(queue, &msg) );
471 } /* 0 msg in queue */
472 CHECK( 0, thrh_td.h_calls );
473 CHECK( 0, thrh_td.l_calls );
474
475 /* Now, post 6 messages, the high threashold */
476 for (i=0; i<6; i++) {
477 msg = msg1;
478 CHECK( 0, fd_fifo_post(queue, &msg) );
479 } /* 6 msg in queue */
480 CHECK( 1, thrh_td.h_calls );
481 CHECK( 0, thrh_td.l_calls );
482
483 /* Remove 2 messages, to reach the low threshold */
484 for (i=0; i<2; i++) {
485 CHECK( 0, fd_fifo_get(queue, &msg) );
486 } /* 4 msg in queue */
487 CHECK( 1, thrh_td.h_calls );
488 CHECK( 1, thrh_td.l_calls );
489
490 /* Come again at the high threshold */
491 for (i=0; i<2; i++) {
492 msg = msg1;
493 CHECK( 0, fd_fifo_post(queue, &msg) );
494 } /* 6 msg in queue */
495 CHECK( 2, thrh_td.h_calls );
496 CHECK( 1, thrh_td.l_calls );
497
498 /* Suppose the queue continues to grow */
499 for (i=0; i<6; i++) {
500 msg = msg1;
501 CHECK( 0, fd_fifo_post(queue, &msg) );
502 } /* 12 msg in queue */
503 CHECK( 3, thrh_td.h_calls );
504 CHECK( 1, thrh_td.l_calls );
505 for (i=0; i<5; i++) {
506 msg = msg1;
507 CHECK( 0, fd_fifo_post(queue, &msg) );
508 } /* 17 msg in queue */
509 CHECK( 3, thrh_td.h_calls );
510 CHECK( 1, thrh_td.l_calls );
511
512 /* Now the queue goes back to 0 messages */
513 for (i=0; i<17; i++) {
514 CHECK( 0, fd_fifo_get(queue, &msg) );
515 } /* 0 msg in queue */
516 CHECK( 3, thrh_td.h_calls );
517 CHECK( 3, thrh_td.l_calls );
518
519 /* We're done for this test */
520 CHECK( 0, fd_fifo_del(&queue) );
521 }
522
523 /* Test max queue limit */
524 {
525 struct fifo *queue = NULL;
526 struct test_data td;
527 pthread_t th;
528 int * item, i;
529
530 /* Create the queue */
531 CHECK( 0, fd_fifo_new(&queue, 10) );
532
533 /* Initialize the test data structures */
534 td.queue = queue;
535 td.nbr = 15;
536
537 CHECK( 0, pthread_create( &th, NULL, test_fct2, &td ) );
538
539 usleep(100000); /* 100 millisec */
540
541 CHECK( 10, iter );
542
543 CHECK( 0, fd_fifo_tryget(queue, &item) );
544 CHECK( 0, *item);
545 free(item);
546
547 usleep(100000); /* 100 millisec */
548
549 CHECK( 11, iter );
550
551 for (i=1; i<4; i++) {
552 CHECK( 0, fd_fifo_get(queue, &item) );
553 CHECK( i, *item);
554 free(item);
555 }
556
557 usleep(100000); /* 100 millisec */
558
559 CHECK( 14, iter );
560
561 for (; i < td.nbr; i++) {
562 CHECK( 0, fd_fifo_tryget(queue, &item) );
563 CHECK( i, *item);
564 free(item);
565 }
566
567 CHECK( 0, pthread_join( th, NULL ) );
568 CHECK( 15, iter );
569
570 }
571
572 /* Delete the messages */
573 CHECK( 0, fd_msg_free( msg1 ) );
574 CHECK( 0, fd_msg_free( msg2 ) );
575 CHECK( 0, fd_msg_free( msg3 ) );
576
577 /* That's all for the tests yet */
578 PASSTEST();
579}