1275970Scy/* 2275970Scy * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 3275970Scy * 4275970Scy * Redistribution and use in source and binary forms, with or without 5275970Scy * modification, are permitted provided that the following conditions 6275970Scy * are met: 7275970Scy * 1. Redistributions of source code must retain the above copyright 8275970Scy * notice, this list of conditions and the following disclaimer. 9275970Scy * 2. Redistributions in binary form must reproduce the above copyright 10275970Scy * notice, this list of conditions and the following disclaimer in the 11275970Scy * documentation and/or other materials provided with the distribution. 12275970Scy * 3. The name of the author may not be used to endorse or promote products 13275970Scy * derived from this software without specific prior written permission. 14275970Scy * 15275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25275970Scy */ 26275970Scy#include "util-internal.h" 27275970Scy 28275970Scy/* The old tests here need assertions to work. */ 29275970Scy#undef NDEBUG 30275970Scy 31275970Scy#include "event2/event-config.h" 32275970Scy 33275970Scy#include <sys/types.h> 34275970Scy#include <stdio.h> 35275970Scy#include <stdlib.h> 36275970Scy#include <string.h> 37275970Scy#ifdef EVENT__HAVE_UNISTD_H 38275970Scy#include <unistd.h> 39275970Scy#endif 40275970Scy#ifdef EVENT__HAVE_SYS_WAIT_H 41275970Scy#include <sys/wait.h> 42275970Scy#endif 43275970Scy 44275970Scy#ifdef EVENT__HAVE_PTHREADS 45275970Scy#include <pthread.h> 46275970Scy#elif defined(_WIN32) 47275970Scy#include <process.h> 48275970Scy#endif 49275970Scy#include <assert.h> 50275970Scy#ifdef EVENT__HAVE_UNISTD_H 51275970Scy#include <unistd.h> 52275970Scy#endif 53275970Scy#include <time.h> 54275970Scy 55275970Scy#include "sys/queue.h" 56275970Scy 57275970Scy#include "event2/event.h" 58275970Scy#include "event2/event_struct.h" 59275970Scy#include "event2/thread.h" 60275970Scy#include "event2/util.h" 61275970Scy#include "evthread-internal.h" 62275970Scy#include "event-internal.h" 63275970Scy#include "defer-internal.h" 64275970Scy#include "regress.h" 65275970Scy#include "tinytest_macros.h" 66275970Scy#include "time-internal.h" 67275970Scy#include "regress_thread.h" 68275970Scy 69275970Scystruct cond_wait { 70275970Scy void *lock; 71275970Scy void *cond; 72275970Scy}; 73275970Scy 74275970Scystatic void 75275970Scywake_all_timeout(evutil_socket_t fd, short what, void *arg) 76275970Scy{ 77275970Scy struct cond_wait *cw = arg; 78275970Scy EVLOCK_LOCK(cw->lock, 0); 79275970Scy EVTHREAD_COND_BROADCAST(cw->cond); 80275970Scy EVLOCK_UNLOCK(cw->lock, 0); 81275970Scy 82275970Scy} 83275970Scy 84275970Scystatic void 85275970Scywake_one_timeout(evutil_socket_t fd, short what, void *arg) 86275970Scy{ 87275970Scy struct cond_wait *cw = arg; 88275970Scy EVLOCK_LOCK(cw->lock, 0); 89275970Scy EVTHREAD_COND_SIGNAL(cw->cond); 90275970Scy EVLOCK_UNLOCK(cw->lock, 0); 91275970Scy} 92275970Scy 93275970Scy#define NUM_THREADS 100 94275970Scy#define NUM_ITERATIONS 100 95275970Scyvoid *count_lock; 96275970Scystatic int count; 97275970Scy 98275970Scystatic THREAD_FN 99275970Scybasic_thread(void *arg) 100275970Scy{ 101275970Scy struct cond_wait cw; 102275970Scy struct event_base *base = arg; 103275970Scy struct event ev; 104275970Scy int i = 0; 105275970Scy 106275970Scy EVTHREAD_ALLOC_LOCK(cw.lock, 0); 107275970Scy EVTHREAD_ALLOC_COND(cw.cond); 108275970Scy assert(cw.lock); 109275970Scy assert(cw.cond); 110275970Scy 111275970Scy evtimer_assign(&ev, base, wake_all_timeout, &cw); 112275970Scy for (i = 0; i < NUM_ITERATIONS; i++) { 113275970Scy struct timeval tv; 114275970Scy evutil_timerclear(&tv); 115275970Scy tv.tv_sec = 0; 116275970Scy tv.tv_usec = 3000; 117275970Scy 118275970Scy EVLOCK_LOCK(cw.lock, 0); 119275970Scy /* we need to make sure that event does not happen before 120275970Scy * we get to wait on the conditional variable */ 121275970Scy assert(evtimer_add(&ev, &tv) == 0); 122275970Scy 123275970Scy assert(EVTHREAD_COND_WAIT(cw.cond, cw.lock) == 0); 124275970Scy EVLOCK_UNLOCK(cw.lock, 0); 125275970Scy 126275970Scy EVLOCK_LOCK(count_lock, 0); 127275970Scy ++count; 128275970Scy EVLOCK_UNLOCK(count_lock, 0); 129275970Scy } 130275970Scy 131275970Scy /* exit the loop only if all threads fired all timeouts */ 132275970Scy EVLOCK_LOCK(count_lock, 0); 133275970Scy if (count >= NUM_THREADS * NUM_ITERATIONS) 134275970Scy event_base_loopexit(base, NULL); 135275970Scy EVLOCK_UNLOCK(count_lock, 0); 136275970Scy 137275970Scy EVTHREAD_FREE_LOCK(cw.lock, 0); 138275970Scy EVTHREAD_FREE_COND(cw.cond); 139275970Scy 140275970Scy THREAD_RETURN(); 141275970Scy} 142275970Scy 143275970Scystatic int notification_fd_used = 0; 144275970Scy#ifndef _WIN32 145275970Scystatic int got_sigchld = 0; 146275970Scystatic void 147275970Scysigchld_cb(evutil_socket_t fd, short event, void *arg) 148275970Scy{ 149275970Scy struct timeval tv; 150275970Scy struct event_base *base = arg; 151275970Scy 152275970Scy got_sigchld++; 153275970Scy tv.tv_usec = 100000; 154275970Scy tv.tv_sec = 0; 155275970Scy event_base_loopexit(base, &tv); 156275970Scy} 157275970Scy 158275970Scy 159275970Scystatic void 160275970Scynotify_fd_cb(evutil_socket_t fd, short event, void *arg) 161275970Scy{ 162275970Scy ++notification_fd_used; 163275970Scy} 164275970Scy#endif 165275970Scy 166275970Scystatic void 167275970Scythread_basic(void *arg) 168275970Scy{ 169275970Scy THREAD_T threads[NUM_THREADS]; 170275970Scy struct event ev; 171275970Scy struct timeval tv; 172275970Scy int i; 173275970Scy struct basic_test_data *data = arg; 174275970Scy struct event_base *base = data->base; 175275970Scy 176275970Scy struct event *notification_event = NULL; 177275970Scy struct event *sigchld_event = NULL; 178275970Scy 179275970Scy EVTHREAD_ALLOC_LOCK(count_lock, 0); 180275970Scy tt_assert(count_lock); 181275970Scy 182275970Scy tt_assert(base); 183275970Scy if (evthread_make_base_notifiable(base)<0) { 184275970Scy tt_abort_msg("Couldn't make base notifiable!"); 185275970Scy } 186275970Scy 187275970Scy#ifndef _WIN32 188275970Scy if (data->setup_data && !strcmp(data->setup_data, "forking")) { 189275970Scy pid_t pid; 190275970Scy int status; 191275970Scy sigchld_event = evsignal_new(base, SIGCHLD, sigchld_cb, base); 192275970Scy /* This piggybacks on the th_notify_fd weirdly, and looks 193275970Scy * inside libevent internals. Not a good idea in non-testing 194275970Scy * code! */ 195275970Scy notification_event = event_new(base, 196275970Scy base->th_notify_fd[0], EV_READ|EV_PERSIST, notify_fd_cb, 197275970Scy NULL); 198275970Scy event_add(sigchld_event, NULL); 199275970Scy event_add(notification_event, NULL); 200275970Scy 201275970Scy if ((pid = fork()) == 0) { 202275970Scy event_del(notification_event); 203275970Scy if (event_reinit(base) < 0) { 204275970Scy TT_FAIL(("reinit")); 205275970Scy exit(1); 206275970Scy } 207275970Scy event_assign(notification_event, base, 208275970Scy base->th_notify_fd[0], EV_READ|EV_PERSIST, 209275970Scy notify_fd_cb, NULL); 210275970Scy event_add(notification_event, NULL); 211275970Scy goto child; 212275970Scy } 213275970Scy 214275970Scy event_base_dispatch(base); 215275970Scy 216275970Scy if (waitpid(pid, &status, 0) == -1) 217275970Scy tt_abort_perror("waitpid"); 218275970Scy TT_BLATHER(("Waitpid okay\n")); 219275970Scy 220275970Scy tt_assert(got_sigchld); 221275970Scy tt_int_op(notification_fd_used, ==, 0); 222275970Scy 223275970Scy goto end; 224275970Scy } 225275970Scy 226275970Scychild: 227275970Scy#endif 228275970Scy for (i = 0; i < NUM_THREADS; ++i) 229275970Scy THREAD_START(threads[i], basic_thread, base); 230275970Scy 231275970Scy evtimer_assign(&ev, base, NULL, NULL); 232275970Scy evutil_timerclear(&tv); 233275970Scy tv.tv_sec = 1000; 234275970Scy event_add(&ev, &tv); 235275970Scy 236275970Scy event_base_dispatch(base); 237275970Scy 238275970Scy for (i = 0; i < NUM_THREADS; ++i) 239275970Scy THREAD_JOIN(threads[i]); 240275970Scy 241275970Scy event_del(&ev); 242275970Scy 243275970Scy tt_int_op(count, ==, NUM_THREADS * NUM_ITERATIONS); 244275970Scy 245275970Scy EVTHREAD_FREE_LOCK(count_lock, 0); 246275970Scy 247275970Scy TT_BLATHER(("notifiations==%d", notification_fd_used)); 248275970Scy 249275970Scyend: 250275970Scy 251275970Scy if (notification_event) 252275970Scy event_free(notification_event); 253275970Scy if (sigchld_event) 254275970Scy event_free(sigchld_event); 255275970Scy} 256275970Scy 257275970Scy#undef NUM_THREADS 258275970Scy#define NUM_THREADS 10 259275970Scy 260275970Scystruct alerted_record { 261275970Scy struct cond_wait *cond; 262275970Scy struct timeval delay; 263275970Scy struct timeval alerted_at; 264275970Scy int timed_out; 265275970Scy}; 266275970Scy 267275970Scystatic THREAD_FN 268275970Scywait_for_condition(void *arg) 269275970Scy{ 270275970Scy struct alerted_record *rec = arg; 271275970Scy int r; 272275970Scy 273275970Scy EVLOCK_LOCK(rec->cond->lock, 0); 274275970Scy if (rec->delay.tv_sec || rec->delay.tv_usec) { 275275970Scy r = EVTHREAD_COND_WAIT_TIMED(rec->cond->cond, rec->cond->lock, 276275970Scy &rec->delay); 277275970Scy } else { 278275970Scy r = EVTHREAD_COND_WAIT(rec->cond->cond, rec->cond->lock); 279275970Scy } 280275970Scy EVLOCK_UNLOCK(rec->cond->lock, 0); 281275970Scy 282275970Scy evutil_gettimeofday(&rec->alerted_at, NULL); 283275970Scy if (r == 1) 284275970Scy rec->timed_out = 1; 285275970Scy 286275970Scy THREAD_RETURN(); 287275970Scy} 288275970Scy 289275970Scystatic void 290275970Scythread_conditions_simple(void *arg) 291275970Scy{ 292275970Scy struct timeval tv_signal, tv_timeout, tv_broadcast; 293275970Scy struct alerted_record alerted[NUM_THREADS]; 294275970Scy THREAD_T threads[NUM_THREADS]; 295275970Scy struct cond_wait cond; 296275970Scy int i; 297275970Scy struct timeval launched_at; 298275970Scy struct event wake_one; 299275970Scy struct event wake_all; 300275970Scy struct basic_test_data *data = arg; 301275970Scy struct event_base *base = data->base; 302275970Scy int n_timed_out=0, n_signal=0, n_broadcast=0; 303275970Scy 304275970Scy tv_signal.tv_sec = tv_timeout.tv_sec = tv_broadcast.tv_sec = 0; 305275970Scy tv_signal.tv_usec = 30*1000; 306275970Scy tv_timeout.tv_usec = 150*1000; 307275970Scy tv_broadcast.tv_usec = 500*1000; 308275970Scy 309275970Scy EVTHREAD_ALLOC_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE); 310275970Scy EVTHREAD_ALLOC_COND(cond.cond); 311275970Scy tt_assert(cond.lock); 312275970Scy tt_assert(cond.cond); 313275970Scy for (i = 0; i < NUM_THREADS; ++i) { 314275970Scy memset(&alerted[i], 0, sizeof(struct alerted_record)); 315275970Scy alerted[i].cond = &cond; 316275970Scy } 317275970Scy 318275970Scy /* Threads 5 and 6 will be allowed to time out */ 319275970Scy memcpy(&alerted[5].delay, &tv_timeout, sizeof(tv_timeout)); 320275970Scy memcpy(&alerted[6].delay, &tv_timeout, sizeof(tv_timeout)); 321275970Scy 322275970Scy evtimer_assign(&wake_one, base, wake_one_timeout, &cond); 323275970Scy evtimer_assign(&wake_all, base, wake_all_timeout, &cond); 324275970Scy 325275970Scy evutil_gettimeofday(&launched_at, NULL); 326275970Scy 327275970Scy /* Launch the threads... */ 328275970Scy for (i = 0; i < NUM_THREADS; ++i) { 329275970Scy THREAD_START(threads[i], wait_for_condition, &alerted[i]); 330275970Scy } 331275970Scy 332275970Scy /* Start the timers... */ 333275970Scy tt_int_op(event_add(&wake_one, &tv_signal), ==, 0); 334275970Scy tt_int_op(event_add(&wake_all, &tv_broadcast), ==, 0); 335275970Scy 336275970Scy /* And run for a bit... */ 337275970Scy event_base_dispatch(base); 338275970Scy 339275970Scy /* And wait till the threads are done. */ 340275970Scy for (i = 0; i < NUM_THREADS; ++i) 341275970Scy THREAD_JOIN(threads[i]); 342275970Scy 343275970Scy /* Now, let's see what happened. At least one of 5 or 6 should 344275970Scy * have timed out. */ 345275970Scy n_timed_out = alerted[5].timed_out + alerted[6].timed_out; 346275970Scy tt_int_op(n_timed_out, >=, 1); 347275970Scy tt_int_op(n_timed_out, <=, 2); 348275970Scy 349275970Scy for (i = 0; i < NUM_THREADS; ++i) { 350275970Scy const struct timeval *target_delay; 351275970Scy struct timeval target_time, actual_delay; 352275970Scy if (alerted[i].timed_out) { 353275970Scy TT_BLATHER(("%d looks like a timeout\n", i)); 354275970Scy target_delay = &tv_timeout; 355275970Scy tt_assert(i == 5 || i == 6); 356275970Scy } else if (evutil_timerisset(&alerted[i].alerted_at)) { 357275970Scy long diff1,diff2; 358275970Scy evutil_timersub(&alerted[i].alerted_at, 359275970Scy &launched_at, &actual_delay); 360275970Scy diff1 = timeval_msec_diff(&actual_delay, 361275970Scy &tv_signal); 362275970Scy diff2 = timeval_msec_diff(&actual_delay, 363275970Scy &tv_broadcast); 364290000Sglebius if (labs(diff1) < labs(diff2)) { 365275970Scy TT_BLATHER(("%d looks like a signal\n", i)); 366275970Scy target_delay = &tv_signal; 367275970Scy ++n_signal; 368275970Scy } else { 369275970Scy TT_BLATHER(("%d looks like a broadcast\n", i)); 370275970Scy target_delay = &tv_broadcast; 371275970Scy ++n_broadcast; 372275970Scy } 373275970Scy } else { 374275970Scy TT_FAIL(("Thread %d never got woken", i)); 375275970Scy continue; 376275970Scy } 377275970Scy evutil_timeradd(target_delay, &launched_at, &target_time); 378275970Scy test_timeval_diff_leq(&target_time, &alerted[i].alerted_at, 379275970Scy 0, 50); 380275970Scy } 381275970Scy tt_int_op(n_broadcast + n_signal + n_timed_out, ==, NUM_THREADS); 382275970Scy tt_int_op(n_signal, ==, 1); 383275970Scy 384275970Scyend: 385290000Sglebius EVTHREAD_FREE_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE); 386290000Sglebius EVTHREAD_FREE_COND(cond.cond); 387275970Scy} 388275970Scy 389275970Scy#define CB_COUNT 128 390275970Scy#define QUEUE_THREAD_COUNT 8 391275970Scy 392275970Scystatic void 393275970ScySLEEP_MS(int ms) 394275970Scy{ 395275970Scy struct timeval tv; 396275970Scy tv.tv_sec = ms/1000; 397275970Scy tv.tv_usec = (ms%1000)*1000; 398275970Scy evutil_usleep_(&tv); 399275970Scy} 400275970Scy 401275970Scystruct deferred_test_data { 402275970Scy struct event_callback cbs[CB_COUNT]; 403275970Scy struct event_base *queue; 404275970Scy}; 405275970Scy 406275970Scystatic struct timeval timer_start = {0,0}; 407275970Scystatic struct timeval timer_end = {0,0}; 408275970Scystatic unsigned callback_count = 0; 409275970Scystatic THREAD_T load_threads[QUEUE_THREAD_COUNT]; 410275970Scystatic struct deferred_test_data deferred_data[QUEUE_THREAD_COUNT]; 411275970Scy 412275970Scystatic void 413275970Scydeferred_callback(struct event_callback *cb, void *arg) 414275970Scy{ 415275970Scy SLEEP_MS(1); 416275970Scy callback_count += 1; 417275970Scy} 418275970Scy 419275970Scystatic THREAD_FN 420275970Scyload_deferred_queue(void *arg) 421275970Scy{ 422275970Scy struct deferred_test_data *data = arg; 423275970Scy size_t i; 424275970Scy 425275970Scy for (i = 0; i < CB_COUNT; ++i) { 426275970Scy event_deferred_cb_init_(&data->cbs[i], 0, deferred_callback, 427275970Scy NULL); 428275970Scy event_deferred_cb_schedule_(data->queue, &data->cbs[i]); 429275970Scy SLEEP_MS(1); 430275970Scy } 431275970Scy 432275970Scy THREAD_RETURN(); 433275970Scy} 434275970Scy 435275970Scystatic void 436275970Scytimer_callback(evutil_socket_t fd, short what, void *arg) 437275970Scy{ 438275970Scy evutil_gettimeofday(&timer_end, NULL); 439275970Scy} 440275970Scy 441275970Scystatic void 442275970Scystart_threads_callback(evutil_socket_t fd, short what, void *arg) 443275970Scy{ 444275970Scy int i; 445275970Scy 446275970Scy for (i = 0; i < QUEUE_THREAD_COUNT; ++i) { 447275970Scy THREAD_START(load_threads[i], load_deferred_queue, 448275970Scy &deferred_data[i]); 449275970Scy } 450275970Scy} 451275970Scy 452275970Scystatic void 453275970Scythread_deferred_cb_skew(void *arg) 454275970Scy{ 455275970Scy struct timeval tv_timer = {1, 0}; 456275970Scy struct event_base *base = NULL; 457275970Scy struct event_config *cfg = NULL; 458275970Scy struct timeval elapsed; 459275970Scy int elapsed_usec; 460275970Scy int i; 461275970Scy 462275970Scy cfg = event_config_new(); 463275970Scy tt_assert(cfg); 464275970Scy event_config_set_max_dispatch_interval(cfg, NULL, 16, 0); 465275970Scy 466275970Scy base = event_base_new_with_config(cfg); 467275970Scy tt_assert(base); 468275970Scy 469275970Scy for (i = 0; i < QUEUE_THREAD_COUNT; ++i) 470275970Scy deferred_data[i].queue = base; 471275970Scy 472275970Scy evutil_gettimeofday(&timer_start, NULL); 473275970Scy event_base_once(base, -1, EV_TIMEOUT, timer_callback, NULL, 474275970Scy &tv_timer); 475275970Scy event_base_once(base, -1, EV_TIMEOUT, start_threads_callback, 476275970Scy NULL, NULL); 477275970Scy event_base_dispatch(base); 478275970Scy 479275970Scy evutil_timersub(&timer_end, &timer_start, &elapsed); 480275970Scy TT_BLATHER(("callback count, %u", callback_count)); 481275970Scy elapsed_usec = 482275970Scy (unsigned)(elapsed.tv_sec*1000000 + elapsed.tv_usec); 483275970Scy TT_BLATHER(("elapsed time, %u usec", elapsed_usec)); 484275970Scy 485275970Scy /* XXX be more intelligent here. just make sure skew is 486275970Scy * within .4 seconds for now. */ 487275970Scy tt_assert(elapsed_usec >= 600000 && elapsed_usec <= 1400000); 488275970Scy 489275970Scyend: 490275970Scy for (i = 0; i < QUEUE_THREAD_COUNT; ++i) 491275970Scy THREAD_JOIN(load_threads[i]); 492275970Scy if (base) 493275970Scy event_base_free(base); 494275970Scy if (cfg) 495275970Scy event_config_free(cfg); 496275970Scy} 497275970Scy 498275970Scystatic struct event time_events[5]; 499275970Scystatic struct timeval times[5]; 500275970Scystatic struct event_base *exit_base = NULL; 501275970Scystatic void 502275970Scynote_time_cb(evutil_socket_t fd, short what, void *arg) 503275970Scy{ 504275970Scy evutil_gettimeofday(arg, NULL); 505275970Scy if (arg == ×[4]) { 506275970Scy event_base_loopbreak(exit_base); 507275970Scy } 508275970Scy} 509275970Scystatic THREAD_FN 510275970Scyregister_events_subthread(void *arg) 511275970Scy{ 512275970Scy struct timeval tv = {0,0}; 513275970Scy SLEEP_MS(100); 514275970Scy event_active(&time_events[0], EV_TIMEOUT, 1); 515275970Scy SLEEP_MS(100); 516275970Scy event_active(&time_events[1], EV_TIMEOUT, 1); 517275970Scy SLEEP_MS(100); 518275970Scy tv.tv_usec = 100*1000; 519275970Scy event_add(&time_events[2], &tv); 520275970Scy tv.tv_usec = 150*1000; 521275970Scy event_add(&time_events[3], &tv); 522275970Scy SLEEP_MS(200); 523275970Scy event_active(&time_events[4], EV_TIMEOUT, 1); 524275970Scy 525275970Scy THREAD_RETURN(); 526275970Scy} 527275970Scy 528275970Scystatic void 529275970Scythread_no_events(void *arg) 530275970Scy{ 531275970Scy THREAD_T thread; 532275970Scy struct basic_test_data *data = arg; 533275970Scy struct timeval starttime, endtime; 534275970Scy int i; 535275970Scy exit_base = data->base; 536275970Scy 537275970Scy memset(times,0,sizeof(times)); 538275970Scy for (i=0;i<5;++i) { 539275970Scy event_assign(&time_events[i], data->base, 540275970Scy -1, 0, note_time_cb, ×[i]); 541275970Scy } 542275970Scy 543275970Scy evutil_gettimeofday(&starttime, NULL); 544275970Scy THREAD_START(thread, register_events_subthread, data->base); 545275970Scy event_base_loop(data->base, EVLOOP_NO_EXIT_ON_EMPTY); 546275970Scy evutil_gettimeofday(&endtime, NULL); 547275970Scy tt_assert(event_base_got_break(data->base)); 548275970Scy THREAD_JOIN(thread); 549275970Scy for (i=0; i<5; ++i) { 550275970Scy struct timeval diff; 551275970Scy double sec; 552275970Scy evutil_timersub(×[i], &starttime, &diff); 553275970Scy sec = diff.tv_sec + diff.tv_usec/1.0e6; 554275970Scy TT_BLATHER(("event %d at %.4f seconds", i, sec)); 555275970Scy } 556275970Scy test_timeval_diff_eq(&starttime, ×[0], 100); 557275970Scy test_timeval_diff_eq(&starttime, ×[1], 200); 558275970Scy test_timeval_diff_eq(&starttime, ×[2], 400); 559275970Scy test_timeval_diff_eq(&starttime, ×[3], 450); 560275970Scy test_timeval_diff_eq(&starttime, ×[4], 500); 561275970Scy test_timeval_diff_eq(&starttime, &endtime, 500); 562275970Scy 563275970Scyend: 564275970Scy ; 565275970Scy} 566275970Scy 567275970Scy#define TEST(name) \ 568275970Scy { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \ 569275970Scy &basic_setup, NULL } 570275970Scy 571275970Scystruct testcase_t thread_testcases[] = { 572275970Scy { "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, 573275970Scy &basic_setup, NULL }, 574275970Scy#ifndef _WIN32 575275970Scy { "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, 576275970Scy &basic_setup, (char*)"forking" }, 577275970Scy#endif 578275970Scy TEST(conditions_simple), 579275970Scy { "deferred_cb_skew", thread_deferred_cb_skew, 580275970Scy TT_FORK|TT_NEED_THREADS|TT_OFF_BY_DEFAULT, 581275970Scy &basic_setup, NULL }, 582275970Scy TEST(no_events), 583275970Scy END_OF_TESTCASES 584275970Scy}; 585275970Scy 586