2 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "util-internal.h"
28 /* The old tests here need assertions to work. */
31 #include "event2/event-config.h"
33 #include <sys/types.h>
37 #ifdef EVENT__HAVE_UNISTD_H
40 #ifdef EVENT__HAVE_SYS_WAIT_H
44 #ifdef EVENT__HAVE_PTHREADS
50 #ifdef EVENT__HAVE_UNISTD_H
55 #include "sys/queue.h"
57 #include "event2/event.h"
58 #include "event2/event_struct.h"
59 #include "event2/thread.h"
60 #include "event2/util.h"
61 #include "evthread-internal.h"
62 #include "event-internal.h"
63 #include "defer-internal.h"
65 #include "tinytest_macros.h"
66 #include "time-internal.h"
67 #include "regress_thread.h"
75 wake_all_timeout(evutil_socket_t fd, short what, void *arg)
77 struct cond_wait *cw = arg;
78 EVLOCK_LOCK(cw->lock, 0);
79 EVTHREAD_COND_BROADCAST(cw->cond);
80 EVLOCK_UNLOCK(cw->lock, 0);
85 wake_one_timeout(evutil_socket_t fd, short what, void *arg)
87 struct cond_wait *cw = arg;
88 EVLOCK_LOCK(cw->lock, 0);
89 EVTHREAD_COND_SIGNAL(cw->cond);
90 EVLOCK_UNLOCK(cw->lock, 0);
93 #define NUM_THREADS 100
94 #define NUM_ITERATIONS 100
99 basic_thread(void *arg)
102 struct event_base *base = arg;
106 EVTHREAD_ALLOC_LOCK(cw.lock, 0);
107 EVTHREAD_ALLOC_COND(cw.cond);
111 evtimer_assign(&ev, base, wake_all_timeout, &cw);
112 for (i = 0; i < NUM_ITERATIONS; i++) {
114 evutil_timerclear(&tv);
118 EVLOCK_LOCK(cw.lock, 0);
119 /* we need to make sure that event does not happen before
120 * we get to wait on the conditional variable */
121 assert(evtimer_add(&ev, &tv) == 0);
123 assert(EVTHREAD_COND_WAIT(cw.cond, cw.lock) == 0);
124 EVLOCK_UNLOCK(cw.lock, 0);
126 EVLOCK_LOCK(count_lock, 0);
128 EVLOCK_UNLOCK(count_lock, 0);
131 /* exit the loop only if all threads fired all timeouts */
132 EVLOCK_LOCK(count_lock, 0);
133 if (count >= NUM_THREADS * NUM_ITERATIONS)
134 event_base_loopexit(base, NULL);
135 EVLOCK_UNLOCK(count_lock, 0);
137 EVTHREAD_FREE_LOCK(cw.lock, 0);
138 EVTHREAD_FREE_COND(cw.cond);
143 static int notification_fd_used = 0;
145 static int got_sigchld = 0;
147 sigchld_cb(evutil_socket_t fd, short event, void *arg)
150 struct event_base *base = arg;
155 event_base_loopexit(base, &tv);
160 notify_fd_cb(evutil_socket_t fd, short event, void *arg)
162 ++notification_fd_used;
167 thread_basic(void *arg)
169 THREAD_T threads[NUM_THREADS];
173 struct basic_test_data *data = arg;
174 struct event_base *base = data->base;
176 struct event *notification_event = NULL;
177 struct event *sigchld_event = NULL;
179 EVTHREAD_ALLOC_LOCK(count_lock, 0);
180 tt_assert(count_lock);
183 if (evthread_make_base_notifiable(base)<0) {
184 tt_abort_msg("Couldn't make base notifiable!");
188 if (data->setup_data && !strcmp(data->setup_data, "forking")) {
191 sigchld_event = evsignal_new(base, SIGCHLD, sigchld_cb, base);
192 /* This piggybacks on the th_notify_fd weirdly, and looks
193 * inside libevent internals. Not a good idea in non-testing
195 notification_event = event_new(base,
196 base->th_notify_fd[0], EV_READ|EV_PERSIST, notify_fd_cb,
198 event_add(sigchld_event, NULL);
199 event_add(notification_event, NULL);
201 if ((pid = fork()) == 0) {
202 event_del(notification_event);
203 if (event_reinit(base) < 0) {
207 event_assign(notification_event, base,
208 base->th_notify_fd[0], EV_READ|EV_PERSIST,
210 event_add(notification_event, NULL);
214 event_base_dispatch(base);
216 if (waitpid(pid, &status, 0) == -1)
217 tt_abort_perror("waitpid");
218 TT_BLATHER(("Waitpid okay\n"));
220 tt_assert(got_sigchld);
221 tt_int_op(notification_fd_used, ==, 0);
228 for (i = 0; i < NUM_THREADS; ++i)
229 THREAD_START(threads[i], basic_thread, base);
231 evtimer_assign(&ev, base, NULL, NULL);
232 evutil_timerclear(&tv);
236 event_base_dispatch(base);
238 for (i = 0; i < NUM_THREADS; ++i)
239 THREAD_JOIN(threads[i]);
243 tt_int_op(count, ==, NUM_THREADS * NUM_ITERATIONS);
245 EVTHREAD_FREE_LOCK(count_lock, 0);
247 TT_BLATHER(("notifiations==%d", notification_fd_used));
251 if (notification_event)
252 event_free(notification_event);
254 event_free(sigchld_event);
258 #define NUM_THREADS 10
260 struct alerted_record {
261 struct cond_wait *cond;
262 struct timeval delay;
263 struct timeval alerted_at;
268 wait_for_condition(void *arg)
270 struct alerted_record *rec = arg;
273 EVLOCK_LOCK(rec->cond->lock, 0);
274 if (rec->delay.tv_sec || rec->delay.tv_usec) {
275 r = EVTHREAD_COND_WAIT_TIMED(rec->cond->cond, rec->cond->lock,
278 r = EVTHREAD_COND_WAIT(rec->cond->cond, rec->cond->lock);
280 EVLOCK_UNLOCK(rec->cond->lock, 0);
282 evutil_gettimeofday(&rec->alerted_at, NULL);
290 thread_conditions_simple(void *arg)
292 struct timeval tv_signal, tv_timeout, tv_broadcast;
293 struct alerted_record alerted[NUM_THREADS];
294 THREAD_T threads[NUM_THREADS];
295 struct cond_wait cond;
297 struct timeval launched_at;
298 struct event wake_one;
299 struct event wake_all;
300 struct basic_test_data *data = arg;
301 struct event_base *base = data->base;
302 int n_timed_out=0, n_signal=0, n_broadcast=0;
304 tv_signal.tv_sec = tv_timeout.tv_sec = tv_broadcast.tv_sec = 0;
305 tv_signal.tv_usec = 30*1000;
306 tv_timeout.tv_usec = 150*1000;
307 tv_broadcast.tv_usec = 500*1000;
309 EVTHREAD_ALLOC_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE);
310 EVTHREAD_ALLOC_COND(cond.cond);
311 tt_assert(cond.lock);
312 tt_assert(cond.cond);
313 for (i = 0; i < NUM_THREADS; ++i) {
314 memset(&alerted[i], 0, sizeof(struct alerted_record));
315 alerted[i].cond = &cond;
318 /* Threads 5 and 6 will be allowed to time out */
319 memcpy(&alerted[5].delay, &tv_timeout, sizeof(tv_timeout));
320 memcpy(&alerted[6].delay, &tv_timeout, sizeof(tv_timeout));
322 evtimer_assign(&wake_one, base, wake_one_timeout, &cond);
323 evtimer_assign(&wake_all, base, wake_all_timeout, &cond);
325 evutil_gettimeofday(&launched_at, NULL);
327 /* Launch the threads... */
328 for (i = 0; i < NUM_THREADS; ++i) {
329 THREAD_START(threads[i], wait_for_condition, &alerted[i]);
332 /* Start the timers... */
333 tt_int_op(event_add(&wake_one, &tv_signal), ==, 0);
334 tt_int_op(event_add(&wake_all, &tv_broadcast), ==, 0);
336 /* And run for a bit... */
337 event_base_dispatch(base);
339 /* And wait till the threads are done. */
340 for (i = 0; i < NUM_THREADS; ++i)
341 THREAD_JOIN(threads[i]);
343 /* Now, let's see what happened. At least one of 5 or 6 should
345 n_timed_out = alerted[5].timed_out + alerted[6].timed_out;
346 tt_int_op(n_timed_out, >=, 1);
347 tt_int_op(n_timed_out, <=, 2);
349 for (i = 0; i < NUM_THREADS; ++i) {
350 const struct timeval *target_delay;
351 struct timeval target_time, actual_delay;
352 if (alerted[i].timed_out) {
353 TT_BLATHER(("%d looks like a timeout\n", i));
354 target_delay = &tv_timeout;
355 tt_assert(i == 5 || i == 6);
356 } else if (evutil_timerisset(&alerted[i].alerted_at)) {
358 evutil_timersub(&alerted[i].alerted_at,
359 &launched_at, &actual_delay);
360 diff1 = timeval_msec_diff(&actual_delay,
362 diff2 = timeval_msec_diff(&actual_delay,
364 if (labs(diff1) < labs(diff2)) {
365 TT_BLATHER(("%d looks like a signal\n", i));
366 target_delay = &tv_signal;
369 TT_BLATHER(("%d looks like a broadcast\n", i));
370 target_delay = &tv_broadcast;
374 TT_FAIL(("Thread %d never got woken", i));
377 evutil_timeradd(target_delay, &launched_at, &target_time);
378 test_timeval_diff_leq(&target_time, &alerted[i].alerted_at,
381 tt_int_op(n_broadcast + n_signal + n_timed_out, ==, NUM_THREADS);
382 tt_int_op(n_signal, ==, 1);
385 EVTHREAD_FREE_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE);
386 EVTHREAD_FREE_COND(cond.cond);
390 #define QUEUE_THREAD_COUNT 8
397 tv.tv_usec = (ms%1000)*1000;
401 struct deferred_test_data {
402 struct event_callback cbs[CB_COUNT];
403 struct event_base *queue;
406 static struct timeval timer_start = {0,0};
407 static struct timeval timer_end = {0,0};
408 static unsigned callback_count = 0;
409 static THREAD_T load_threads[QUEUE_THREAD_COUNT];
410 static struct deferred_test_data deferred_data[QUEUE_THREAD_COUNT];
413 deferred_callback(struct event_callback *cb, void *arg)
420 load_deferred_queue(void *arg)
422 struct deferred_test_data *data = arg;
425 for (i = 0; i < CB_COUNT; ++i) {
426 event_deferred_cb_init_(&data->cbs[i], 0, deferred_callback,
428 event_deferred_cb_schedule_(data->queue, &data->cbs[i]);
436 timer_callback(evutil_socket_t fd, short what, void *arg)
438 evutil_gettimeofday(&timer_end, NULL);
442 start_threads_callback(evutil_socket_t fd, short what, void *arg)
446 for (i = 0; i < QUEUE_THREAD_COUNT; ++i) {
447 THREAD_START(load_threads[i], load_deferred_queue,
453 thread_deferred_cb_skew(void *arg)
455 struct timeval tv_timer = {1, 0};
456 struct event_base *base = NULL;
457 struct event_config *cfg = NULL;
458 struct timeval elapsed;
462 cfg = event_config_new();
464 event_config_set_max_dispatch_interval(cfg, NULL, 16, 0);
466 base = event_base_new_with_config(cfg);
469 for (i = 0; i < QUEUE_THREAD_COUNT; ++i)
470 deferred_data[i].queue = base;
472 evutil_gettimeofday(&timer_start, NULL);
473 event_base_once(base, -1, EV_TIMEOUT, timer_callback, NULL,
475 event_base_once(base, -1, EV_TIMEOUT, start_threads_callback,
477 event_base_dispatch(base);
479 evutil_timersub(&timer_end, &timer_start, &elapsed);
480 TT_BLATHER(("callback count, %u", callback_count));
482 (unsigned)(elapsed.tv_sec*1000000 + elapsed.tv_usec);
483 TT_BLATHER(("elapsed time, %u usec", elapsed_usec));
485 /* XXX be more intelligent here. just make sure skew is
486 * within .4 seconds for now. */
487 tt_assert(elapsed_usec >= 600000 && elapsed_usec <= 1400000);
490 for (i = 0; i < QUEUE_THREAD_COUNT; ++i)
491 THREAD_JOIN(load_threads[i]);
493 event_base_free(base);
495 event_config_free(cfg);
498 static struct event time_events[5];
499 static struct timeval times[5];
500 static struct event_base *exit_base = NULL;
502 note_time_cb(evutil_socket_t fd, short what, void *arg)
504 evutil_gettimeofday(arg, NULL);
505 if (arg == ×[4]) {
506 event_base_loopbreak(exit_base);
510 register_events_subthread(void *arg)
512 struct timeval tv = {0,0};
514 event_active(&time_events[0], EV_TIMEOUT, 1);
516 event_active(&time_events[1], EV_TIMEOUT, 1);
518 tv.tv_usec = 100*1000;
519 event_add(&time_events[2], &tv);
520 tv.tv_usec = 150*1000;
521 event_add(&time_events[3], &tv);
523 event_active(&time_events[4], EV_TIMEOUT, 1);
529 thread_no_events(void *arg)
532 struct basic_test_data *data = arg;
533 struct timeval starttime, endtime;
535 exit_base = data->base;
537 memset(times,0,sizeof(times));
539 event_assign(&time_events[i], data->base,
540 -1, 0, note_time_cb, ×[i]);
543 evutil_gettimeofday(&starttime, NULL);
544 THREAD_START(thread, register_events_subthread, data->base);
545 event_base_loop(data->base, EVLOOP_NO_EXIT_ON_EMPTY);
546 evutil_gettimeofday(&endtime, NULL);
547 tt_assert(event_base_got_break(data->base));
549 for (i=0; i<5; ++i) {
552 evutil_timersub(×[i], &starttime, &diff);
553 sec = diff.tv_sec + diff.tv_usec/1.0e6;
554 TT_BLATHER(("event %d at %.4f seconds", i, sec));
556 test_timeval_diff_eq(&starttime, ×[0], 100);
557 test_timeval_diff_eq(&starttime, ×[1], 200);
558 test_timeval_diff_eq(&starttime, ×[2], 400);
559 test_timeval_diff_eq(&starttime, ×[3], 450);
560 test_timeval_diff_eq(&starttime, ×[4], 500);
561 test_timeval_diff_eq(&starttime, &endtime, 500);
568 { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \
571 struct testcase_t thread_testcases[] = {
572 { "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
573 &basic_setup, NULL },
575 { "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
576 &basic_setup, (char*)"forking" },
578 TEST(conditions_simple),
579 { "deferred_cb_skew", thread_deferred_cb_skew,
580 TT_FORK|TT_NEED_THREADS|TT_OFF_BY_DEFAULT,
581 &basic_setup, NULL },