1*eabc0478Schristos /* $NetBSD: regress_thread.c,v 1.7 2024/08/18 20:47:23 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 58585484eSchristos * 68585484eSchristos * Redistribution and use in source and binary forms, with or without 78585484eSchristos * modification, are permitted provided that the following conditions 88585484eSchristos * are met: 98585484eSchristos * 1. Redistributions of source code must retain the above copyright 108585484eSchristos * notice, this list of conditions and the following disclaimer. 118585484eSchristos * 2. Redistributions in binary form must reproduce the above copyright 128585484eSchristos * notice, this list of conditions and the following disclaimer in the 138585484eSchristos * documentation and/or other materials provided with the distribution. 148585484eSchristos * 3. The name of the author may not be used to endorse or promote products 158585484eSchristos * derived from this software without specific prior written permission. 168585484eSchristos * 178585484eSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 188585484eSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 198585484eSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 208585484eSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 218585484eSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 228585484eSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 238585484eSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 248585484eSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 258585484eSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 268585484eSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 278585484eSchristos */ 288585484eSchristos #include "util-internal.h" 298585484eSchristos 308585484eSchristos /* The old tests here need assertions to work. */ 318585484eSchristos #undef NDEBUG 328585484eSchristos 338585484eSchristos #include "event2/event-config.h" 348585484eSchristos 358585484eSchristos #include <sys/types.h> 368585484eSchristos #include <stdio.h> 378585484eSchristos #include <stdlib.h> 388585484eSchristos #include <string.h> 398585484eSchristos #ifdef EVENT__HAVE_UNISTD_H 408585484eSchristos #include <unistd.h> 418585484eSchristos #endif 428585484eSchristos #ifdef EVENT__HAVE_SYS_WAIT_H 438585484eSchristos #include <sys/wait.h> 448585484eSchristos #endif 458585484eSchristos 468585484eSchristos #ifdef EVENT__HAVE_PTHREADS 478585484eSchristos #include <pthread.h> 488585484eSchristos #elif defined(_WIN32) 498585484eSchristos #include <process.h> 508585484eSchristos #endif 518585484eSchristos #include <assert.h> 528585484eSchristos #ifdef EVENT__HAVE_UNISTD_H 538585484eSchristos #include <unistd.h> 548585484eSchristos #endif 558585484eSchristos #include <time.h> 568585484eSchristos 578585484eSchristos #include "sys/queue.h" 588585484eSchristos 598585484eSchristos #include "event2/event.h" 608585484eSchristos #include "event2/event_struct.h" 618585484eSchristos #include "event2/thread.h" 628585484eSchristos #include "event2/util.h" 638585484eSchristos #include "evthread-internal.h" 648585484eSchristos #include "event-internal.h" 658585484eSchristos #include "defer-internal.h" 668585484eSchristos #include "regress.h" 678585484eSchristos #include "tinytest_macros.h" 688585484eSchristos #include "time-internal.h" 69b8ecfcfeSchristos #include "regress_thread.h" 708585484eSchristos 718585484eSchristos struct cond_wait { 728585484eSchristos void *lock; 738585484eSchristos void *cond; 748585484eSchristos }; 758585484eSchristos 768585484eSchristos static void 778585484eSchristos wake_all_timeout(evutil_socket_t fd, short what, void *arg) 788585484eSchristos { 798585484eSchristos struct cond_wait *cw = arg; 808585484eSchristos EVLOCK_LOCK(cw->lock, 0); 818585484eSchristos EVTHREAD_COND_BROADCAST(cw->cond); 828585484eSchristos EVLOCK_UNLOCK(cw->lock, 0); 838585484eSchristos 848585484eSchristos } 858585484eSchristos 868585484eSchristos static void 878585484eSchristos wake_one_timeout(evutil_socket_t fd, short what, void *arg) 888585484eSchristos { 898585484eSchristos struct cond_wait *cw = arg; 908585484eSchristos EVLOCK_LOCK(cw->lock, 0); 918585484eSchristos EVTHREAD_COND_SIGNAL(cw->cond); 928585484eSchristos EVLOCK_UNLOCK(cw->lock, 0); 938585484eSchristos } 948585484eSchristos 958585484eSchristos #define NUM_THREADS 100 968585484eSchristos #define NUM_ITERATIONS 100 978585484eSchristos void *count_lock; 988585484eSchristos static int count; 998585484eSchristos 1008585484eSchristos static THREAD_FN 1018585484eSchristos basic_thread(void *arg) 1028585484eSchristos { 1038585484eSchristos struct cond_wait cw; 1048585484eSchristos struct event_base *base = arg; 1058585484eSchristos struct event ev; 1068585484eSchristos int i = 0; 1078585484eSchristos 1088585484eSchristos EVTHREAD_ALLOC_LOCK(cw.lock, 0); 1098585484eSchristos EVTHREAD_ALLOC_COND(cw.cond); 1108585484eSchristos assert(cw.lock); 1118585484eSchristos assert(cw.cond); 1128585484eSchristos 1138585484eSchristos evtimer_assign(&ev, base, wake_all_timeout, &cw); 1148585484eSchristos for (i = 0; i < NUM_ITERATIONS; i++) { 1158585484eSchristos struct timeval tv; 1168585484eSchristos evutil_timerclear(&tv); 1178585484eSchristos tv.tv_sec = 0; 1188585484eSchristos tv.tv_usec = 3000; 1198585484eSchristos 1208585484eSchristos EVLOCK_LOCK(cw.lock, 0); 1218585484eSchristos /* we need to make sure that event does not happen before 1228585484eSchristos * we get to wait on the conditional variable */ 1238585484eSchristos assert(evtimer_add(&ev, &tv) == 0); 1248585484eSchristos 1258585484eSchristos assert(EVTHREAD_COND_WAIT(cw.cond, cw.lock) == 0); 1268585484eSchristos EVLOCK_UNLOCK(cw.lock, 0); 1278585484eSchristos 1288585484eSchristos EVLOCK_LOCK(count_lock, 0); 1298585484eSchristos ++count; 1308585484eSchristos EVLOCK_UNLOCK(count_lock, 0); 1318585484eSchristos } 1328585484eSchristos 1338585484eSchristos /* exit the loop only if all threads fired all timeouts */ 1348585484eSchristos EVLOCK_LOCK(count_lock, 0); 1358585484eSchristos if (count >= NUM_THREADS * NUM_ITERATIONS) 1368585484eSchristos event_base_loopexit(base, NULL); 1378585484eSchristos EVLOCK_UNLOCK(count_lock, 0); 1388585484eSchristos 1398585484eSchristos EVTHREAD_FREE_LOCK(cw.lock, 0); 1408585484eSchristos EVTHREAD_FREE_COND(cw.cond); 1418585484eSchristos 1428585484eSchristos THREAD_RETURN(); 1438585484eSchristos } 1448585484eSchristos 1458585484eSchristos static int notification_fd_used = 0; 1468585484eSchristos #ifndef _WIN32 1478585484eSchristos static int got_sigchld = 0; 1488585484eSchristos static void 1498585484eSchristos sigchld_cb(evutil_socket_t fd, short event, void *arg) 1508585484eSchristos { 1518585484eSchristos struct timeval tv; 1528585484eSchristos struct event_base *base = arg; 1538585484eSchristos 1548585484eSchristos got_sigchld++; 1558585484eSchristos tv.tv_usec = 100000; 1568585484eSchristos tv.tv_sec = 0; 1578585484eSchristos event_base_loopexit(base, &tv); 1588585484eSchristos } 1598585484eSchristos 1608585484eSchristos 1618585484eSchristos static void 1628585484eSchristos notify_fd_cb(evutil_socket_t fd, short event, void *arg) 1638585484eSchristos { 1648585484eSchristos ++notification_fd_used; 1658585484eSchristos } 1668585484eSchristos #endif 1678585484eSchristos 1688585484eSchristos static void 1698585484eSchristos thread_basic(void *arg) 1708585484eSchristos { 1718585484eSchristos THREAD_T threads[NUM_THREADS]; 1728585484eSchristos struct event ev; 1738585484eSchristos struct timeval tv; 1748585484eSchristos int i; 1758585484eSchristos struct basic_test_data *data = arg; 1768585484eSchristos struct event_base *base = data->base; 1778585484eSchristos 1788585484eSchristos struct event *notification_event = NULL; 1798585484eSchristos struct event *sigchld_event = NULL; 1808585484eSchristos 1818585484eSchristos EVTHREAD_ALLOC_LOCK(count_lock, 0); 1828585484eSchristos tt_assert(count_lock); 1838585484eSchristos 1848585484eSchristos tt_assert(base); 1858585484eSchristos if (evthread_make_base_notifiable(base)<0) { 1868585484eSchristos tt_abort_msg("Couldn't make base notifiable!"); 1878585484eSchristos } 1888585484eSchristos 1898585484eSchristos #ifndef _WIN32 1908585484eSchristos if (data->setup_data && !strcmp(data->setup_data, "forking")) { 1918585484eSchristos pid_t pid; 1928585484eSchristos int status; 1938585484eSchristos sigchld_event = evsignal_new(base, SIGCHLD, sigchld_cb, base); 1948585484eSchristos /* This piggybacks on the th_notify_fd weirdly, and looks 1958585484eSchristos * inside libevent internals. Not a good idea in non-testing 1968585484eSchristos * code! */ 1978585484eSchristos notification_event = event_new(base, 1988585484eSchristos base->th_notify_fd[0], EV_READ|EV_PERSIST, notify_fd_cb, 1998585484eSchristos NULL); 2008585484eSchristos event_add(sigchld_event, NULL); 2018585484eSchristos event_add(notification_event, NULL); 2028585484eSchristos 2038585484eSchristos if ((pid = fork()) == 0) { 2048585484eSchristos event_del(notification_event); 2058585484eSchristos if (event_reinit(base) < 0) { 2068585484eSchristos TT_FAIL(("reinit")); 2078585484eSchristos exit(1); 2088585484eSchristos } 2098585484eSchristos event_assign(notification_event, base, 2108585484eSchristos base->th_notify_fd[0], EV_READ|EV_PERSIST, 2118585484eSchristos notify_fd_cb, NULL); 2128585484eSchristos event_add(notification_event, NULL); 2138585484eSchristos goto child; 2148585484eSchristos } 2158585484eSchristos 2168585484eSchristos event_base_dispatch(base); 2178585484eSchristos 2188585484eSchristos if (waitpid(pid, &status, 0) == -1) 2198585484eSchristos tt_abort_perror("waitpid"); 2208585484eSchristos TT_BLATHER(("Waitpid okay\n")); 2218585484eSchristos 2228585484eSchristos tt_assert(got_sigchld); 2238585484eSchristos tt_int_op(notification_fd_used, ==, 0); 2248585484eSchristos 2258585484eSchristos goto end; 2268585484eSchristos } 2278585484eSchristos 2288585484eSchristos child: 2298585484eSchristos #endif 2308585484eSchristos for (i = 0; i < NUM_THREADS; ++i) 2318585484eSchristos THREAD_START(threads[i], basic_thread, base); 2328585484eSchristos 2338585484eSchristos evtimer_assign(&ev, base, NULL, NULL); 2348585484eSchristos evutil_timerclear(&tv); 2358585484eSchristos tv.tv_sec = 1000; 2368585484eSchristos event_add(&ev, &tv); 2378585484eSchristos 2388585484eSchristos event_base_dispatch(base); 2398585484eSchristos 2408585484eSchristos for (i = 0; i < NUM_THREADS; ++i) 2418585484eSchristos THREAD_JOIN(threads[i]); 2428585484eSchristos 2438585484eSchristos event_del(&ev); 2448585484eSchristos 2458585484eSchristos tt_int_op(count, ==, NUM_THREADS * NUM_ITERATIONS); 2468585484eSchristos 2478585484eSchristos EVTHREAD_FREE_LOCK(count_lock, 0); 2488585484eSchristos 2498585484eSchristos TT_BLATHER(("notifiations==%d", notification_fd_used)); 2508585484eSchristos 2518585484eSchristos end: 2528585484eSchristos 2538585484eSchristos if (notification_event) 2548585484eSchristos event_free(notification_event); 2558585484eSchristos if (sigchld_event) 2568585484eSchristos event_free(sigchld_event); 2578585484eSchristos } 2588585484eSchristos 2598585484eSchristos #undef NUM_THREADS 2608585484eSchristos #define NUM_THREADS 10 2618585484eSchristos 2628585484eSchristos struct alerted_record { 2638585484eSchristos struct cond_wait *cond; 2648585484eSchristos struct timeval delay; 2658585484eSchristos struct timeval alerted_at; 2668585484eSchristos int timed_out; 2678585484eSchristos }; 2688585484eSchristos 2698585484eSchristos static THREAD_FN 2708585484eSchristos wait_for_condition(void *arg) 2718585484eSchristos { 2728585484eSchristos struct alerted_record *rec = arg; 2738585484eSchristos int r; 2748585484eSchristos 2758585484eSchristos EVLOCK_LOCK(rec->cond->lock, 0); 2768585484eSchristos if (rec->delay.tv_sec || rec->delay.tv_usec) { 2778585484eSchristos r = EVTHREAD_COND_WAIT_TIMED(rec->cond->cond, rec->cond->lock, 2788585484eSchristos &rec->delay); 2798585484eSchristos } else { 2808585484eSchristos r = EVTHREAD_COND_WAIT(rec->cond->cond, rec->cond->lock); 2818585484eSchristos } 2828585484eSchristos EVLOCK_UNLOCK(rec->cond->lock, 0); 2838585484eSchristos 2848585484eSchristos evutil_gettimeofday(&rec->alerted_at, NULL); 2858585484eSchristos if (r == 1) 2868585484eSchristos rec->timed_out = 1; 2878585484eSchristos 2888585484eSchristos THREAD_RETURN(); 2898585484eSchristos } 2908585484eSchristos 2918585484eSchristos static void 2928585484eSchristos thread_conditions_simple(void *arg) 2938585484eSchristos { 2948585484eSchristos struct timeval tv_signal, tv_timeout, tv_broadcast; 2958585484eSchristos struct alerted_record alerted[NUM_THREADS]; 2968585484eSchristos THREAD_T threads[NUM_THREADS]; 2978585484eSchristos struct cond_wait cond; 2988585484eSchristos int i; 2998585484eSchristos struct timeval launched_at; 3008585484eSchristos struct event wake_one; 3018585484eSchristos struct event wake_all; 3028585484eSchristos struct basic_test_data *data = arg; 3038585484eSchristos struct event_base *base = data->base; 3048585484eSchristos int n_timed_out=0, n_signal=0, n_broadcast=0; 3058585484eSchristos 3068585484eSchristos tv_signal.tv_sec = tv_timeout.tv_sec = tv_broadcast.tv_sec = 0; 3078585484eSchristos tv_signal.tv_usec = 30*1000; 3088585484eSchristos tv_timeout.tv_usec = 150*1000; 3098585484eSchristos tv_broadcast.tv_usec = 500*1000; 3108585484eSchristos 3118585484eSchristos EVTHREAD_ALLOC_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE); 3128585484eSchristos EVTHREAD_ALLOC_COND(cond.cond); 3138585484eSchristos tt_assert(cond.lock); 3148585484eSchristos tt_assert(cond.cond); 3158585484eSchristos for (i = 0; i < NUM_THREADS; ++i) { 3168585484eSchristos memset(&alerted[i], 0, sizeof(struct alerted_record)); 3178585484eSchristos alerted[i].cond = &cond; 3188585484eSchristos } 3198585484eSchristos 3208585484eSchristos /* Threads 5 and 6 will be allowed to time out */ 3218585484eSchristos memcpy(&alerted[5].delay, &tv_timeout, sizeof(tv_timeout)); 3228585484eSchristos memcpy(&alerted[6].delay, &tv_timeout, sizeof(tv_timeout)); 3238585484eSchristos 3248585484eSchristos evtimer_assign(&wake_one, base, wake_one_timeout, &cond); 3258585484eSchristos evtimer_assign(&wake_all, base, wake_all_timeout, &cond); 3268585484eSchristos 3278585484eSchristos evutil_gettimeofday(&launched_at, NULL); 3288585484eSchristos 3298585484eSchristos /* Launch the threads... */ 3308585484eSchristos for (i = 0; i < NUM_THREADS; ++i) { 3318585484eSchristos THREAD_START(threads[i], wait_for_condition, &alerted[i]); 3328585484eSchristos } 3338585484eSchristos 3348585484eSchristos /* Start the timers... */ 3358585484eSchristos tt_int_op(event_add(&wake_one, &tv_signal), ==, 0); 3368585484eSchristos tt_int_op(event_add(&wake_all, &tv_broadcast), ==, 0); 3378585484eSchristos 3388585484eSchristos /* And run for a bit... */ 3398585484eSchristos event_base_dispatch(base); 3408585484eSchristos 3418585484eSchristos /* And wait till the threads are done. */ 3428585484eSchristos for (i = 0; i < NUM_THREADS; ++i) 3438585484eSchristos THREAD_JOIN(threads[i]); 3448585484eSchristos 3458585484eSchristos /* Now, let's see what happened. At least one of 5 or 6 should 3468585484eSchristos * have timed out. */ 3478585484eSchristos n_timed_out = alerted[5].timed_out + alerted[6].timed_out; 3488585484eSchristos tt_int_op(n_timed_out, >=, 1); 3498585484eSchristos tt_int_op(n_timed_out, <=, 2); 3508585484eSchristos 3518585484eSchristos for (i = 0; i < NUM_THREADS; ++i) { 3528585484eSchristos const struct timeval *target_delay; 3538585484eSchristos struct timeval target_time, actual_delay; 3548585484eSchristos if (alerted[i].timed_out) { 3558585484eSchristos TT_BLATHER(("%d looks like a timeout\n", i)); 3568585484eSchristos target_delay = &tv_timeout; 3578585484eSchristos tt_assert(i == 5 || i == 6); 3588585484eSchristos } else if (evutil_timerisset(&alerted[i].alerted_at)) { 3598585484eSchristos long diff1,diff2; 3608585484eSchristos evutil_timersub(&alerted[i].alerted_at, 3618585484eSchristos &launched_at, &actual_delay); 3628585484eSchristos diff1 = timeval_msec_diff(&actual_delay, 3638585484eSchristos &tv_signal); 3648585484eSchristos diff2 = timeval_msec_diff(&actual_delay, 3658585484eSchristos &tv_broadcast); 3667476e6e4Schristos if (labs(diff1) < labs(diff2)) { 3678585484eSchristos TT_BLATHER(("%d looks like a signal\n", i)); 3688585484eSchristos target_delay = &tv_signal; 3698585484eSchristos ++n_signal; 3708585484eSchristos } else { 3718585484eSchristos TT_BLATHER(("%d looks like a broadcast\n", i)); 3728585484eSchristos target_delay = &tv_broadcast; 3738585484eSchristos ++n_broadcast; 3748585484eSchristos } 3758585484eSchristos } else { 3768585484eSchristos TT_FAIL(("Thread %d never got woken", i)); 3778585484eSchristos continue; 3788585484eSchristos } 3798585484eSchristos evutil_timeradd(target_delay, &launched_at, &target_time); 3808585484eSchristos test_timeval_diff_leq(&target_time, &alerted[i].alerted_at, 381*eabc0478Schristos 0, 200); 3828585484eSchristos } 3838585484eSchristos tt_int_op(n_broadcast + n_signal + n_timed_out, ==, NUM_THREADS); 3848585484eSchristos tt_int_op(n_signal, ==, 1); 3858585484eSchristos 3868585484eSchristos end: 3877476e6e4Schristos EVTHREAD_FREE_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE); 3887476e6e4Schristos EVTHREAD_FREE_COND(cond.cond); 3898585484eSchristos } 3908585484eSchristos 3918585484eSchristos #define CB_COUNT 128 3928585484eSchristos #define QUEUE_THREAD_COUNT 8 3938585484eSchristos 3948585484eSchristos static void 3958585484eSchristos SLEEP_MS(int ms) 3968585484eSchristos { 3978585484eSchristos struct timeval tv; 3988585484eSchristos tv.tv_sec = ms/1000; 3998585484eSchristos tv.tv_usec = (ms%1000)*1000; 4008585484eSchristos evutil_usleep_(&tv); 4018585484eSchristos } 4028585484eSchristos 4038585484eSchristos struct deferred_test_data { 4048585484eSchristos struct event_callback cbs[CB_COUNT]; 4058585484eSchristos struct event_base *queue; 4068585484eSchristos }; 4078585484eSchristos 4088585484eSchristos static struct timeval timer_start = {0,0}; 4098585484eSchristos static struct timeval timer_end = {0,0}; 4108585484eSchristos static unsigned callback_count = 0; 4118585484eSchristos static THREAD_T load_threads[QUEUE_THREAD_COUNT]; 4128585484eSchristos static struct deferred_test_data deferred_data[QUEUE_THREAD_COUNT]; 4138585484eSchristos 4148585484eSchristos static void 4158585484eSchristos deferred_callback(struct event_callback *cb, void *arg) 4168585484eSchristos { 4178585484eSchristos SLEEP_MS(1); 4188585484eSchristos callback_count += 1; 4198585484eSchristos } 4208585484eSchristos 4218585484eSchristos static THREAD_FN 4228585484eSchristos load_deferred_queue(void *arg) 4238585484eSchristos { 4248585484eSchristos struct deferred_test_data *data = arg; 4258585484eSchristos size_t i; 4268585484eSchristos 4278585484eSchristos for (i = 0; i < CB_COUNT; ++i) { 4288585484eSchristos event_deferred_cb_init_(&data->cbs[i], 0, deferred_callback, 4298585484eSchristos NULL); 4308585484eSchristos event_deferred_cb_schedule_(data->queue, &data->cbs[i]); 4318585484eSchristos SLEEP_MS(1); 4328585484eSchristos } 4338585484eSchristos 4348585484eSchristos THREAD_RETURN(); 4358585484eSchristos } 4368585484eSchristos 4378585484eSchristos static void 4388585484eSchristos timer_callback(evutil_socket_t fd, short what, void *arg) 4398585484eSchristos { 4408585484eSchristos evutil_gettimeofday(&timer_end, NULL); 4418585484eSchristos } 4428585484eSchristos 4438585484eSchristos static void 4448585484eSchristos start_threads_callback(evutil_socket_t fd, short what, void *arg) 4458585484eSchristos { 4468585484eSchristos int i; 4478585484eSchristos 4488585484eSchristos for (i = 0; i < QUEUE_THREAD_COUNT; ++i) { 4498585484eSchristos THREAD_START(load_threads[i], load_deferred_queue, 4508585484eSchristos &deferred_data[i]); 4518585484eSchristos } 4528585484eSchristos } 4538585484eSchristos 4548585484eSchristos static void 4558585484eSchristos thread_deferred_cb_skew(void *arg) 4568585484eSchristos { 4578585484eSchristos struct timeval tv_timer = {1, 0}; 4588585484eSchristos struct event_base *base = NULL; 4598585484eSchristos struct event_config *cfg = NULL; 4608585484eSchristos struct timeval elapsed; 4618585484eSchristos int elapsed_usec; 4628585484eSchristos int i; 4638585484eSchristos 4648585484eSchristos cfg = event_config_new(); 4658585484eSchristos tt_assert(cfg); 4668585484eSchristos event_config_set_max_dispatch_interval(cfg, NULL, 16, 0); 4678585484eSchristos 4688585484eSchristos base = event_base_new_with_config(cfg); 4698585484eSchristos tt_assert(base); 4708585484eSchristos 4718585484eSchristos for (i = 0; i < QUEUE_THREAD_COUNT; ++i) 4728585484eSchristos deferred_data[i].queue = base; 4738585484eSchristos 4748585484eSchristos evutil_gettimeofday(&timer_start, NULL); 4758585484eSchristos event_base_once(base, -1, EV_TIMEOUT, timer_callback, NULL, 4768585484eSchristos &tv_timer); 4778585484eSchristos event_base_once(base, -1, EV_TIMEOUT, start_threads_callback, 4788585484eSchristos NULL, NULL); 4798585484eSchristos event_base_dispatch(base); 4808585484eSchristos 4818585484eSchristos evutil_timersub(&timer_end, &timer_start, &elapsed); 4828585484eSchristos TT_BLATHER(("callback count, %u", callback_count)); 4838585484eSchristos elapsed_usec = 4848585484eSchristos (unsigned)(elapsed.tv_sec*1000000 + elapsed.tv_usec); 4858585484eSchristos TT_BLATHER(("elapsed time, %u usec", elapsed_usec)); 4868585484eSchristos 4878585484eSchristos /* XXX be more intelligent here. just make sure skew is 4888585484eSchristos * within .4 seconds for now. */ 4898585484eSchristos tt_assert(elapsed_usec >= 600000 && elapsed_usec <= 1400000); 4908585484eSchristos 4918585484eSchristos end: 4928585484eSchristos for (i = 0; i < QUEUE_THREAD_COUNT; ++i) 4938585484eSchristos THREAD_JOIN(load_threads[i]); 4948585484eSchristos if (base) 4958585484eSchristos event_base_free(base); 4968585484eSchristos if (cfg) 4978585484eSchristos event_config_free(cfg); 4988585484eSchristos } 4998585484eSchristos 5008585484eSchristos static struct event time_events[5]; 5018585484eSchristos static struct timeval times[5]; 5028585484eSchristos static struct event_base *exit_base = NULL; 5038585484eSchristos static void 5048585484eSchristos note_time_cb(evutil_socket_t fd, short what, void *arg) 5058585484eSchristos { 5068585484eSchristos evutil_gettimeofday(arg, NULL); 5078585484eSchristos if (arg == ×[4]) { 5088585484eSchristos event_base_loopbreak(exit_base); 5098585484eSchristos } 5108585484eSchristos } 5118585484eSchristos static THREAD_FN 5128585484eSchristos register_events_subthread(void *arg) 5138585484eSchristos { 5148585484eSchristos struct timeval tv = {0,0}; 5158585484eSchristos SLEEP_MS(100); 5168585484eSchristos event_active(&time_events[0], EV_TIMEOUT, 1); 5178585484eSchristos SLEEP_MS(100); 5188585484eSchristos event_active(&time_events[1], EV_TIMEOUT, 1); 5198585484eSchristos SLEEP_MS(100); 5208585484eSchristos tv.tv_usec = 100*1000; 5218585484eSchristos event_add(&time_events[2], &tv); 5228585484eSchristos tv.tv_usec = 150*1000; 5238585484eSchristos event_add(&time_events[3], &tv); 5248585484eSchristos SLEEP_MS(200); 5258585484eSchristos event_active(&time_events[4], EV_TIMEOUT, 1); 5268585484eSchristos 5278585484eSchristos THREAD_RETURN(); 5288585484eSchristos } 5298585484eSchristos 5308585484eSchristos static void 5318585484eSchristos thread_no_events(void *arg) 5328585484eSchristos { 5338585484eSchristos THREAD_T thread; 5348585484eSchristos struct basic_test_data *data = arg; 5358585484eSchristos struct timeval starttime, endtime; 5368585484eSchristos int i; 5378585484eSchristos exit_base = data->base; 5388585484eSchristos 5398585484eSchristos memset(times,0,sizeof(times)); 5408585484eSchristos for (i=0;i<5;++i) { 5418585484eSchristos event_assign(&time_events[i], data->base, 5428585484eSchristos -1, 0, note_time_cb, ×[i]); 5438585484eSchristos } 5448585484eSchristos 5458585484eSchristos evutil_gettimeofday(&starttime, NULL); 5468585484eSchristos THREAD_START(thread, register_events_subthread, data->base); 5478585484eSchristos event_base_loop(data->base, EVLOOP_NO_EXIT_ON_EMPTY); 5488585484eSchristos evutil_gettimeofday(&endtime, NULL); 5498585484eSchristos tt_assert(event_base_got_break(data->base)); 5508585484eSchristos THREAD_JOIN(thread); 5518585484eSchristos for (i=0; i<5; ++i) { 5528585484eSchristos struct timeval diff; 5538585484eSchristos double sec; 5548585484eSchristos evutil_timersub(×[i], &starttime, &diff); 5558585484eSchristos sec = diff.tv_sec + diff.tv_usec/1.0e6; 5568585484eSchristos TT_BLATHER(("event %d at %.4f seconds", i, sec)); 5578585484eSchristos } 5588585484eSchristos test_timeval_diff_eq(&starttime, ×[0], 100); 5598585484eSchristos test_timeval_diff_eq(&starttime, ×[1], 200); 5608585484eSchristos test_timeval_diff_eq(&starttime, ×[2], 400); 5618585484eSchristos test_timeval_diff_eq(&starttime, ×[3], 450); 5628585484eSchristos test_timeval_diff_eq(&starttime, ×[4], 500); 5638585484eSchristos test_timeval_diff_eq(&starttime, &endtime, 500); 5648585484eSchristos 5658585484eSchristos end: 5668585484eSchristos ; 5678585484eSchristos } 5688585484eSchristos 569*eabc0478Schristos #define TEST(name, f) \ 570*eabc0478Schristos { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE|(f), \ 5718585484eSchristos &basic_setup, NULL } 5728585484eSchristos 5738585484eSchristos struct testcase_t thread_testcases[] = { 5748585484eSchristos { "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, 5758585484eSchristos &basic_setup, NULL }, 5768585484eSchristos #ifndef _WIN32 5778585484eSchristos { "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, 5788585484eSchristos &basic_setup, (char*)"forking" }, 5798585484eSchristos #endif 580*eabc0478Schristos TEST(conditions_simple, TT_RETRIABLE), 5818585484eSchristos { "deferred_cb_skew", thread_deferred_cb_skew, 5828585484eSchristos TT_FORK|TT_NEED_THREADS|TT_OFF_BY_DEFAULT, 5838585484eSchristos &basic_setup, NULL }, 584*eabc0478Schristos #ifndef _WIN32 585*eabc0478Schristos /****** XXX TODO FIXME windows seems to be having some timing trouble, 586*eabc0478Schristos * looking into it now. / ellzey 587*eabc0478Schristos ******/ 588*eabc0478Schristos TEST(no_events, TT_RETRIABLE), 589*eabc0478Schristos #endif 5908585484eSchristos END_OF_TESTCASES 5918585484eSchristos }; 5928585484eSchristos 593