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 == &times[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, &times[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(&times[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, &times[0], 100);
557275970Scy	test_timeval_diff_eq(&starttime, &times[1], 200);
558275970Scy	test_timeval_diff_eq(&starttime, &times[2], 400);
559275970Scy	test_timeval_diff_eq(&starttime, &times[3], 450);
560275970Scy	test_timeval_diff_eq(&starttime, &times[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