xref: /minix3/external/bsd/libevent/dist/test/regress_thread.c (revision e985b929927b5932e3b68f4b50587d458900107a)
1*e985b929SDavid van Moolenbroek /*	$NetBSD: regress_thread.c,v 1.4 2013/04/12 20:00:21 christos Exp $	*/
2*e985b929SDavid van Moolenbroek /*
3*e985b929SDavid van Moolenbroek  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4*e985b929SDavid van Moolenbroek  *
5*e985b929SDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
6*e985b929SDavid van Moolenbroek  * modification, are permitted provided that the following conditions
7*e985b929SDavid van Moolenbroek  * are met:
8*e985b929SDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
9*e985b929SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer.
10*e985b929SDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
11*e985b929SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
12*e985b929SDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
13*e985b929SDavid van Moolenbroek  * 3. The name of the author may not be used to endorse or promote products
14*e985b929SDavid van Moolenbroek  *    derived from this software without specific prior written permission.
15*e985b929SDavid van Moolenbroek  *
16*e985b929SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17*e985b929SDavid van Moolenbroek  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*e985b929SDavid van Moolenbroek  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19*e985b929SDavid van Moolenbroek  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20*e985b929SDavid van Moolenbroek  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21*e985b929SDavid van Moolenbroek  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22*e985b929SDavid van Moolenbroek  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23*e985b929SDavid van Moolenbroek  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24*e985b929SDavid van Moolenbroek  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25*e985b929SDavid van Moolenbroek  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*e985b929SDavid van Moolenbroek  */
27*e985b929SDavid van Moolenbroek 
28*e985b929SDavid van Moolenbroek /* The old tests here need assertions to work. */
29*e985b929SDavid van Moolenbroek #undef NDEBUG
30*e985b929SDavid van Moolenbroek 
31*e985b929SDavid van Moolenbroek #include "event2/event-config.h"
32*e985b929SDavid van Moolenbroek #include <sys/cdefs.h>
33*e985b929SDavid van Moolenbroek __RCSID("$NetBSD: regress_thread.c,v 1.4 2013/04/12 20:00:21 christos Exp $");
34*e985b929SDavid van Moolenbroek 
35*e985b929SDavid van Moolenbroek #include <sys/types.h>
36*e985b929SDavid van Moolenbroek #include <stdio.h>
37*e985b929SDavid van Moolenbroek #include <stdlib.h>
38*e985b929SDavid van Moolenbroek #include <string.h>
39*e985b929SDavid van Moolenbroek #ifdef _EVENT_HAVE_UNISTD_H
40*e985b929SDavid van Moolenbroek #include <unistd.h>
41*e985b929SDavid van Moolenbroek #endif
42*e985b929SDavid van Moolenbroek #ifdef _EVENT_HAVE_SYS_WAIT_H
43*e985b929SDavid van Moolenbroek #include <sys/wait.h>
44*e985b929SDavid van Moolenbroek #endif
45*e985b929SDavid van Moolenbroek 
46*e985b929SDavid van Moolenbroek #ifdef _EVENT_HAVE_PTHREADS
47*e985b929SDavid van Moolenbroek #include <pthread.h>
48*e985b929SDavid van Moolenbroek #elif defined(WIN32)
49*e985b929SDavid van Moolenbroek #include <process.h>
50*e985b929SDavid van Moolenbroek #endif
51*e985b929SDavid van Moolenbroek #include <assert.h>
52*e985b929SDavid van Moolenbroek #ifdef _EVENT_HAVE_UNISTD_H
53*e985b929SDavid van Moolenbroek #include <unistd.h>
54*e985b929SDavid van Moolenbroek #endif
55*e985b929SDavid van Moolenbroek #include <time.h>
56*e985b929SDavid van Moolenbroek 
57*e985b929SDavid van Moolenbroek #include "sys/queue.h"
58*e985b929SDavid van Moolenbroek 
59*e985b929SDavid van Moolenbroek #include "event2/util.h"
60*e985b929SDavid van Moolenbroek #include "event2/event.h"
61*e985b929SDavid van Moolenbroek #include "event2/event_struct.h"
62*e985b929SDavid van Moolenbroek #include "event2/thread.h"
63*e985b929SDavid van Moolenbroek #include "evthread-internal.h"
64*e985b929SDavid van Moolenbroek #include "event-internal.h"
65*e985b929SDavid van Moolenbroek #include "defer-internal.h"
66*e985b929SDavid van Moolenbroek #include "regress.h"
67*e985b929SDavid van Moolenbroek #include "tinytest_macros.h"
68*e985b929SDavid van Moolenbroek 
69*e985b929SDavid van Moolenbroek #ifdef _EVENT_HAVE_PTHREADS
70*e985b929SDavid van Moolenbroek #define THREAD_T pthread_t
71*e985b929SDavid van Moolenbroek #define THREAD_FN void *
72*e985b929SDavid van Moolenbroek #define THREAD_RETURN() return (NULL)
73*e985b929SDavid van Moolenbroek #define THREAD_START(threadvar, fn, arg) \
74*e985b929SDavid van Moolenbroek 	pthread_create(&(threadvar), NULL, fn, arg)
75*e985b929SDavid van Moolenbroek #define THREAD_JOIN(th) pthread_join(th, NULL)
76*e985b929SDavid van Moolenbroek #else
77*e985b929SDavid van Moolenbroek #define THREAD_T HANDLE
78*e985b929SDavid van Moolenbroek #define THREAD_FN unsigned __stdcall
79*e985b929SDavid van Moolenbroek #define THREAD_RETURN() return (0)
80*e985b929SDavid van Moolenbroek #define THREAD_START(threadvar, fn, arg) do {		\
81*e985b929SDavid van Moolenbroek 	uintptr_t threadhandle = _beginthreadex(NULL,0,fn,(arg),0,NULL); \
82*e985b929SDavid van Moolenbroek 	(threadvar) = (HANDLE) threadhandle; \
83*e985b929SDavid van Moolenbroek 	} while (/*CONSTCOND*/0)
84*e985b929SDavid van Moolenbroek #define THREAD_JOIN(th) WaitForSingleObject(th, INFINITE)
85*e985b929SDavid van Moolenbroek #endif
86*e985b929SDavid van Moolenbroek 
87*e985b929SDavid van Moolenbroek struct cond_wait {
88*e985b929SDavid van Moolenbroek 	void *lock;
89*e985b929SDavid van Moolenbroek 	void *cond;
90*e985b929SDavid van Moolenbroek };
91*e985b929SDavid van Moolenbroek 
92*e985b929SDavid van Moolenbroek static void
wake_all_timeout(evutil_socket_t fd,short what,void * arg)93*e985b929SDavid van Moolenbroek wake_all_timeout(evutil_socket_t fd, short what, void *arg)
94*e985b929SDavid van Moolenbroek {
95*e985b929SDavid van Moolenbroek 	struct cond_wait *cw = arg;
96*e985b929SDavid van Moolenbroek 	EVLOCK_LOCK(cw->lock, 0);
97*e985b929SDavid van Moolenbroek 	EVTHREAD_COND_BROADCAST(cw->cond);
98*e985b929SDavid van Moolenbroek 	EVLOCK_UNLOCK(cw->lock, 0);
99*e985b929SDavid van Moolenbroek 
100*e985b929SDavid van Moolenbroek }
101*e985b929SDavid van Moolenbroek 
102*e985b929SDavid van Moolenbroek #if 0
103*e985b929SDavid van Moolenbroek static void
104*e985b929SDavid van Moolenbroek wake_one_timeout(evutil_socket_t fd, short what, void *arg)
105*e985b929SDavid van Moolenbroek {
106*e985b929SDavid van Moolenbroek 	struct cond_wait *cw = arg;
107*e985b929SDavid van Moolenbroek 	EVLOCK_LOCK(cw->lock, 0);
108*e985b929SDavid van Moolenbroek 	EVTHREAD_COND_SIGNAL(cw->cond);
109*e985b929SDavid van Moolenbroek 	EVLOCK_UNLOCK(cw->lock, 0);
110*e985b929SDavid van Moolenbroek }
111*e985b929SDavid van Moolenbroek #endif
112*e985b929SDavid van Moolenbroek 
113*e985b929SDavid van Moolenbroek #define NUM_THREADS	100
114*e985b929SDavid van Moolenbroek #define NUM_ITERATIONS  100
115*e985b929SDavid van Moolenbroek void *count_lock;
116*e985b929SDavid van Moolenbroek static int count;
117*e985b929SDavid van Moolenbroek 
118*e985b929SDavid van Moolenbroek static THREAD_FN
basic_thread(void * arg)119*e985b929SDavid van Moolenbroek basic_thread(void *arg)
120*e985b929SDavid van Moolenbroek {
121*e985b929SDavid van Moolenbroek 	struct cond_wait cw;
122*e985b929SDavid van Moolenbroek 	struct event_base *base = arg;
123*e985b929SDavid van Moolenbroek 	struct event ev;
124*e985b929SDavid van Moolenbroek 	int i = 0;
125*e985b929SDavid van Moolenbroek 
126*e985b929SDavid van Moolenbroek 	EVTHREAD_ALLOC_LOCK(cw.lock, 0);
127*e985b929SDavid van Moolenbroek 	EVTHREAD_ALLOC_COND(cw.cond);
128*e985b929SDavid van Moolenbroek 	assert(cw.lock);
129*e985b929SDavid van Moolenbroek 	assert(cw.cond);
130*e985b929SDavid van Moolenbroek 
131*e985b929SDavid van Moolenbroek 	evtimer_assign(&ev, base, wake_all_timeout, &cw);
132*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_ITERATIONS; i++) {
133*e985b929SDavid van Moolenbroek 		struct timeval tv;
134*e985b929SDavid van Moolenbroek 		evutil_timerclear(&tv);
135*e985b929SDavid van Moolenbroek 		tv.tv_sec = 0;
136*e985b929SDavid van Moolenbroek 		tv.tv_usec = 3000;
137*e985b929SDavid van Moolenbroek 
138*e985b929SDavid van Moolenbroek 		EVLOCK_LOCK(cw.lock, 0);
139*e985b929SDavid van Moolenbroek 		/* we need to make sure that event does not happen before
140*e985b929SDavid van Moolenbroek 		 * we get to wait on the conditional variable */
141*e985b929SDavid van Moolenbroek 		assert(evtimer_add(&ev, &tv) == 0);
142*e985b929SDavid van Moolenbroek 
143*e985b929SDavid van Moolenbroek 		assert(EVTHREAD_COND_WAIT(cw.cond, cw.lock) == 0);
144*e985b929SDavid van Moolenbroek 		EVLOCK_UNLOCK(cw.lock, 0);
145*e985b929SDavid van Moolenbroek 
146*e985b929SDavid van Moolenbroek 		EVLOCK_LOCK(count_lock, 0);
147*e985b929SDavid van Moolenbroek 		++count;
148*e985b929SDavid van Moolenbroek 		EVLOCK_UNLOCK(count_lock, 0);
149*e985b929SDavid van Moolenbroek 	}
150*e985b929SDavid van Moolenbroek 
151*e985b929SDavid van Moolenbroek 	/* exit the loop only if all threads fired all timeouts */
152*e985b929SDavid van Moolenbroek 	EVLOCK_LOCK(count_lock, 0);
153*e985b929SDavid van Moolenbroek 	if (count >= NUM_THREADS * NUM_ITERATIONS)
154*e985b929SDavid van Moolenbroek 		event_base_loopexit(base, NULL);
155*e985b929SDavid van Moolenbroek 	EVLOCK_UNLOCK(count_lock, 0);
156*e985b929SDavid van Moolenbroek 
157*e985b929SDavid van Moolenbroek 	EVTHREAD_FREE_LOCK(cw.lock, 0);
158*e985b929SDavid van Moolenbroek 	EVTHREAD_FREE_COND(cw.cond);
159*e985b929SDavid van Moolenbroek 
160*e985b929SDavid van Moolenbroek 	THREAD_RETURN();
161*e985b929SDavid van Moolenbroek }
162*e985b929SDavid van Moolenbroek 
163*e985b929SDavid van Moolenbroek static int notification_fd_used = 0;
164*e985b929SDavid van Moolenbroek #ifndef WIN32
165*e985b929SDavid van Moolenbroek static int got_sigchld = 0;
166*e985b929SDavid van Moolenbroek static void
sigchld_cb(evutil_socket_t fd,short event,void * arg)167*e985b929SDavid van Moolenbroek sigchld_cb(evutil_socket_t fd, short event, void *arg)
168*e985b929SDavid van Moolenbroek {
169*e985b929SDavid van Moolenbroek 	struct timeval tv;
170*e985b929SDavid van Moolenbroek 	struct event_base *base = arg;
171*e985b929SDavid van Moolenbroek 
172*e985b929SDavid van Moolenbroek 	got_sigchld++;
173*e985b929SDavid van Moolenbroek 	tv.tv_usec = 100000;
174*e985b929SDavid van Moolenbroek 	tv.tv_sec = 0;
175*e985b929SDavid van Moolenbroek 	event_base_loopexit(base, &tv);
176*e985b929SDavid van Moolenbroek }
177*e985b929SDavid van Moolenbroek 
178*e985b929SDavid van Moolenbroek 
179*e985b929SDavid van Moolenbroek static void
notify_fd_cb(evutil_socket_t fd,short event,void * arg)180*e985b929SDavid van Moolenbroek notify_fd_cb(evutil_socket_t fd, short event, void *arg)
181*e985b929SDavid van Moolenbroek {
182*e985b929SDavid van Moolenbroek 	++notification_fd_used;
183*e985b929SDavid van Moolenbroek }
184*e985b929SDavid van Moolenbroek #endif
185*e985b929SDavid van Moolenbroek 
186*e985b929SDavid van Moolenbroek static void
thread_basic(void * arg)187*e985b929SDavid van Moolenbroek thread_basic(void *arg)
188*e985b929SDavid van Moolenbroek {
189*e985b929SDavid van Moolenbroek 	THREAD_T threads[NUM_THREADS];
190*e985b929SDavid van Moolenbroek 	struct event ev;
191*e985b929SDavid van Moolenbroek 	struct timeval tv;
192*e985b929SDavid van Moolenbroek 	int i;
193*e985b929SDavid van Moolenbroek 	struct basic_test_data *data = arg;
194*e985b929SDavid van Moolenbroek 	struct event_base *base = data->base;
195*e985b929SDavid van Moolenbroek 
196*e985b929SDavid van Moolenbroek 	struct event *notification_event = NULL;
197*e985b929SDavid van Moolenbroek 	struct event *sigchld_event = NULL;
198*e985b929SDavid van Moolenbroek 
199*e985b929SDavid van Moolenbroek 	EVTHREAD_ALLOC_LOCK(count_lock, 0);
200*e985b929SDavid van Moolenbroek 	tt_assert(count_lock);
201*e985b929SDavid van Moolenbroek 
202*e985b929SDavid van Moolenbroek 	tt_assert(base);
203*e985b929SDavid van Moolenbroek 	if (evthread_make_base_notifiable(base)<0) {
204*e985b929SDavid van Moolenbroek 		tt_abort_msg("Couldn't make base notifiable!");
205*e985b929SDavid van Moolenbroek 	}
206*e985b929SDavid van Moolenbroek 
207*e985b929SDavid van Moolenbroek #ifndef WIN32
208*e985b929SDavid van Moolenbroek 	if (data->setup_data && !strcmp(data->setup_data, "forking")) {
209*e985b929SDavid van Moolenbroek 		pid_t pid;
210*e985b929SDavid van Moolenbroek 		int status;
211*e985b929SDavid van Moolenbroek 		sigchld_event = evsignal_new(base, SIGCHLD, sigchld_cb, base);
212*e985b929SDavid van Moolenbroek 		/* This piggybacks on the th_notify_fd weirdly, and looks
213*e985b929SDavid van Moolenbroek 		 * inside libevent internals.  Not a good idea in non-testing
214*e985b929SDavid van Moolenbroek 		 * code! */
215*e985b929SDavid van Moolenbroek 		notification_event = event_new(base,
216*e985b929SDavid van Moolenbroek 		    base->th_notify_fd[0], EV_READ|EV_PERSIST, notify_fd_cb,
217*e985b929SDavid van Moolenbroek 		    NULL);
218*e985b929SDavid van Moolenbroek 		event_add(sigchld_event, NULL);
219*e985b929SDavid van Moolenbroek 		event_add(notification_event, NULL);
220*e985b929SDavid van Moolenbroek 
221*e985b929SDavid van Moolenbroek 		if ((pid = fork()) == 0) {
222*e985b929SDavid van Moolenbroek 			event_del(notification_event);
223*e985b929SDavid van Moolenbroek 			if (event_reinit(base) < 0) {
224*e985b929SDavid van Moolenbroek 				TT_FAIL(("reinit"));
225*e985b929SDavid van Moolenbroek 				exit(1);
226*e985b929SDavid van Moolenbroek 			}
227*e985b929SDavid van Moolenbroek 			event_assign(notification_event, base,
228*e985b929SDavid van Moolenbroek 			    base->th_notify_fd[0], EV_READ|EV_PERSIST,
229*e985b929SDavid van Moolenbroek 			    notify_fd_cb, NULL);
230*e985b929SDavid van Moolenbroek 			event_add(notification_event, NULL);
231*e985b929SDavid van Moolenbroek 	 		goto child;
232*e985b929SDavid van Moolenbroek 		}
233*e985b929SDavid van Moolenbroek 
234*e985b929SDavid van Moolenbroek 		event_base_dispatch(base);
235*e985b929SDavid van Moolenbroek 
236*e985b929SDavid van Moolenbroek 		if (waitpid(pid, &status, 0) == -1)
237*e985b929SDavid van Moolenbroek 			tt_abort_perror("waitpid");
238*e985b929SDavid van Moolenbroek 		TT_BLATHER(("Waitpid okay\n"));
239*e985b929SDavid van Moolenbroek 
240*e985b929SDavid van Moolenbroek 		tt_assert(got_sigchld);
241*e985b929SDavid van Moolenbroek 		tt_int_op(notification_fd_used, ==, 0);
242*e985b929SDavid van Moolenbroek 
243*e985b929SDavid van Moolenbroek 		goto end;
244*e985b929SDavid van Moolenbroek 	}
245*e985b929SDavid van Moolenbroek 
246*e985b929SDavid van Moolenbroek child:
247*e985b929SDavid van Moolenbroek #endif
248*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_THREADS; ++i)
249*e985b929SDavid van Moolenbroek 		THREAD_START(threads[i], basic_thread, base);
250*e985b929SDavid van Moolenbroek 
251*e985b929SDavid van Moolenbroek 	evtimer_assign(&ev, base, NULL, NULL);
252*e985b929SDavid van Moolenbroek 	evutil_timerclear(&tv);
253*e985b929SDavid van Moolenbroek 	tv.tv_sec = 1000;
254*e985b929SDavid van Moolenbroek 	event_add(&ev, &tv);
255*e985b929SDavid van Moolenbroek 
256*e985b929SDavid van Moolenbroek 	event_base_dispatch(base);
257*e985b929SDavid van Moolenbroek 
258*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_THREADS; ++i)
259*e985b929SDavid van Moolenbroek 		THREAD_JOIN(threads[i]);
260*e985b929SDavid van Moolenbroek 
261*e985b929SDavid van Moolenbroek 	event_del(&ev);
262*e985b929SDavid van Moolenbroek 
263*e985b929SDavid van Moolenbroek 	tt_int_op(count, ==, NUM_THREADS * NUM_ITERATIONS);
264*e985b929SDavid van Moolenbroek 
265*e985b929SDavid van Moolenbroek 	EVTHREAD_FREE_LOCK(count_lock, 0);
266*e985b929SDavid van Moolenbroek 
267*e985b929SDavid van Moolenbroek 	TT_BLATHER(("notifiations==%d", notification_fd_used));
268*e985b929SDavid van Moolenbroek 
269*e985b929SDavid van Moolenbroek end:
270*e985b929SDavid van Moolenbroek 
271*e985b929SDavid van Moolenbroek 	if (notification_event)
272*e985b929SDavid van Moolenbroek 		event_free(notification_event);
273*e985b929SDavid van Moolenbroek 	if (sigchld_event)
274*e985b929SDavid van Moolenbroek 		event_free(sigchld_event);
275*e985b929SDavid van Moolenbroek }
276*e985b929SDavid van Moolenbroek 
277*e985b929SDavid van Moolenbroek #undef NUM_THREADS
278*e985b929SDavid van Moolenbroek #define NUM_THREADS 10
279*e985b929SDavid van Moolenbroek 
280*e985b929SDavid van Moolenbroek struct alerted_record {
281*e985b929SDavid van Moolenbroek 	struct cond_wait *cond;
282*e985b929SDavid van Moolenbroek 	struct timeval delay;
283*e985b929SDavid van Moolenbroek 	struct timeval alerted_at;
284*e985b929SDavid van Moolenbroek 	int timed_out;
285*e985b929SDavid van Moolenbroek };
286*e985b929SDavid van Moolenbroek 
287*e985b929SDavid van Moolenbroek #if 0
288*e985b929SDavid van Moolenbroek static THREAD_FN
289*e985b929SDavid van Moolenbroek wait_for_condition(void *arg)
290*e985b929SDavid van Moolenbroek {
291*e985b929SDavid van Moolenbroek 	struct alerted_record *rec = arg;
292*e985b929SDavid van Moolenbroek 	int r;
293*e985b929SDavid van Moolenbroek 
294*e985b929SDavid van Moolenbroek 	EVLOCK_LOCK(rec->cond->lock, 0);
295*e985b929SDavid van Moolenbroek 	if (rec->delay.tv_sec || rec->delay.tv_usec) {
296*e985b929SDavid van Moolenbroek 		r = EVTHREAD_COND_WAIT_TIMED(rec->cond->cond, rec->cond->lock,
297*e985b929SDavid van Moolenbroek 		    &rec->delay);
298*e985b929SDavid van Moolenbroek 	} else {
299*e985b929SDavid van Moolenbroek 		r = EVTHREAD_COND_WAIT(rec->cond->cond, rec->cond->lock);
300*e985b929SDavid van Moolenbroek 	}
301*e985b929SDavid van Moolenbroek 	EVLOCK_UNLOCK(rec->cond->lock, 0);
302*e985b929SDavid van Moolenbroek 
303*e985b929SDavid van Moolenbroek 	evutil_gettimeofday(&rec->alerted_at, NULL);
304*e985b929SDavid van Moolenbroek 	if (r == 1)
305*e985b929SDavid van Moolenbroek 		rec->timed_out = 1;
306*e985b929SDavid van Moolenbroek 
307*e985b929SDavid van Moolenbroek 	THREAD_RETURN();
308*e985b929SDavid van Moolenbroek }
309*e985b929SDavid van Moolenbroek 
310*e985b929SDavid van Moolenbroek static void
311*e985b929SDavid van Moolenbroek thread_conditions_simple(void *arg)
312*e985b929SDavid van Moolenbroek {
313*e985b929SDavid van Moolenbroek 	struct timeval tv_signal, tv_timeout, tv_broadcast;
314*e985b929SDavid van Moolenbroek 	struct alerted_record alerted[NUM_THREADS];
315*e985b929SDavid van Moolenbroek 	THREAD_T threads[NUM_THREADS];
316*e985b929SDavid van Moolenbroek 	struct cond_wait cond;
317*e985b929SDavid van Moolenbroek 	int i;
318*e985b929SDavid van Moolenbroek 	struct timeval launched_at;
319*e985b929SDavid van Moolenbroek 	struct event wake_one;
320*e985b929SDavid van Moolenbroek 	struct event wake_all;
321*e985b929SDavid van Moolenbroek 	struct basic_test_data *data = arg;
322*e985b929SDavid van Moolenbroek 	struct event_base *base = data->base;
323*e985b929SDavid van Moolenbroek 	int n_timed_out=0, n_signal=0, n_broadcast=0;
324*e985b929SDavid van Moolenbroek 
325*e985b929SDavid van Moolenbroek 	tv_signal.tv_sec = tv_timeout.tv_sec = tv_broadcast.tv_sec = 0;
326*e985b929SDavid van Moolenbroek 	tv_signal.tv_usec = 30*1000;
327*e985b929SDavid van Moolenbroek 	tv_timeout.tv_usec = 150*1000;
328*e985b929SDavid van Moolenbroek 	tv_broadcast.tv_usec = 500*1000;
329*e985b929SDavid van Moolenbroek 
330*e985b929SDavid van Moolenbroek 	EVTHREAD_ALLOC_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE);
331*e985b929SDavid van Moolenbroek 	EVTHREAD_ALLOC_COND(cond.cond);
332*e985b929SDavid van Moolenbroek 	tt_assert(cond.lock);
333*e985b929SDavid van Moolenbroek 	tt_assert(cond.cond);
334*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_THREADS; ++i) {
335*e985b929SDavid van Moolenbroek 		memset(&alerted[i], 0, sizeof(struct alerted_record));
336*e985b929SDavid van Moolenbroek 		alerted[i].cond = &cond;
337*e985b929SDavid van Moolenbroek 	}
338*e985b929SDavid van Moolenbroek 
339*e985b929SDavid van Moolenbroek 	/* Threads 5 and 6 will be allowed to time out */
340*e985b929SDavid van Moolenbroek 	memcpy(&alerted[5].delay, &tv_timeout, sizeof(tv_timeout));
341*e985b929SDavid van Moolenbroek 	memcpy(&alerted[6].delay, &tv_timeout, sizeof(tv_timeout));
342*e985b929SDavid van Moolenbroek 
343*e985b929SDavid van Moolenbroek 	evtimer_assign(&wake_one, base, wake_one_timeout, &cond);
344*e985b929SDavid van Moolenbroek 	evtimer_assign(&wake_all, base, wake_all_timeout, &cond);
345*e985b929SDavid van Moolenbroek 
346*e985b929SDavid van Moolenbroek 	evutil_gettimeofday(&launched_at, NULL);
347*e985b929SDavid van Moolenbroek 
348*e985b929SDavid van Moolenbroek 	/* Launch the threads... */
349*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_THREADS; ++i) {
350*e985b929SDavid van Moolenbroek 		THREAD_START(threads[i], wait_for_condition, &alerted[i]);
351*e985b929SDavid van Moolenbroek 	}
352*e985b929SDavid van Moolenbroek 
353*e985b929SDavid van Moolenbroek 	/* Start the timers... */
354*e985b929SDavid van Moolenbroek 	tt_int_op(event_add(&wake_one, &tv_signal), ==, 0);
355*e985b929SDavid van Moolenbroek 	tt_int_op(event_add(&wake_all, &tv_broadcast), ==, 0);
356*e985b929SDavid van Moolenbroek 
357*e985b929SDavid van Moolenbroek 	/* And run for a bit... */
358*e985b929SDavid van Moolenbroek 	event_base_dispatch(base);
359*e985b929SDavid van Moolenbroek 
360*e985b929SDavid van Moolenbroek 	/* And wait till the threads are done. */
361*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_THREADS; ++i)
362*e985b929SDavid van Moolenbroek 		THREAD_JOIN(threads[i]);
363*e985b929SDavid van Moolenbroek 
364*e985b929SDavid van Moolenbroek 	/* Now, let's see what happened. At least one of 5 or 6 should
365*e985b929SDavid van Moolenbroek 	 * have timed out. */
366*e985b929SDavid van Moolenbroek 	n_timed_out = alerted[5].timed_out + alerted[6].timed_out;
367*e985b929SDavid van Moolenbroek 	tt_int_op(n_timed_out, >=, 1);
368*e985b929SDavid van Moolenbroek 	tt_int_op(n_timed_out, <=, 2);
369*e985b929SDavid van Moolenbroek 
370*e985b929SDavid van Moolenbroek 	for (i = 0; i < NUM_THREADS; ++i) {
371*e985b929SDavid van Moolenbroek 		const struct timeval *target_delay;
372*e985b929SDavid van Moolenbroek 		struct timeval target_time, actual_delay;
373*e985b929SDavid van Moolenbroek 		if (alerted[i].timed_out) {
374*e985b929SDavid van Moolenbroek 			TT_BLATHER(("%d looks like a timeout\n", i));
375*e985b929SDavid van Moolenbroek 			target_delay = &tv_timeout;
376*e985b929SDavid van Moolenbroek 			tt_assert(i == 5 || i == 6);
377*e985b929SDavid van Moolenbroek 		} else if (evutil_timerisset(&alerted[i].alerted_at)) {
378*e985b929SDavid van Moolenbroek 			long diff1,diff2;
379*e985b929SDavid van Moolenbroek 			evutil_timersub(&alerted[i].alerted_at,
380*e985b929SDavid van Moolenbroek 			    &launched_at, &actual_delay);
381*e985b929SDavid van Moolenbroek 			diff1 = timeval_msec_diff(&actual_delay,
382*e985b929SDavid van Moolenbroek 			    &tv_signal);
383*e985b929SDavid van Moolenbroek 			diff2 = timeval_msec_diff(&actual_delay,
384*e985b929SDavid van Moolenbroek 			    &tv_broadcast);
385*e985b929SDavid van Moolenbroek 			if (abs(diff1) < abs(diff2)) {
386*e985b929SDavid van Moolenbroek 				TT_BLATHER(("%d looks like a signal\n", i));
387*e985b929SDavid van Moolenbroek 				target_delay = &tv_signal;
388*e985b929SDavid van Moolenbroek 				++n_signal;
389*e985b929SDavid van Moolenbroek 			} else {
390*e985b929SDavid van Moolenbroek 				TT_BLATHER(("%d looks like a broadcast\n", i));
391*e985b929SDavid van Moolenbroek 				target_delay = &tv_broadcast;
392*e985b929SDavid van Moolenbroek 				++n_broadcast;
393*e985b929SDavid van Moolenbroek 			}
394*e985b929SDavid van Moolenbroek 		} else {
395*e985b929SDavid van Moolenbroek 			TT_FAIL(("Thread %d never got woken", i));
396*e985b929SDavid van Moolenbroek 			continue;
397*e985b929SDavid van Moolenbroek 		}
398*e985b929SDavid van Moolenbroek 		evutil_timeradd(target_delay, &launched_at, &target_time);
399*e985b929SDavid van Moolenbroek 		test_timeval_diff_leq(&target_time, &alerted[i].alerted_at,
400*e985b929SDavid van Moolenbroek 		    0, 150);
401*e985b929SDavid van Moolenbroek 	}
402*e985b929SDavid van Moolenbroek 	tt_int_op(n_broadcast + n_signal + n_timed_out, ==, NUM_THREADS);
403*e985b929SDavid van Moolenbroek 	tt_int_op(n_signal, ==, 1);
404*e985b929SDavid van Moolenbroek 
405*e985b929SDavid van Moolenbroek end:
406*e985b929SDavid van Moolenbroek 	;
407*e985b929SDavid van Moolenbroek }
408*e985b929SDavid van Moolenbroek #endif
409*e985b929SDavid van Moolenbroek 
410*e985b929SDavid van Moolenbroek #define CB_COUNT 128
411*e985b929SDavid van Moolenbroek #define QUEUE_THREAD_COUNT 8
412*e985b929SDavid van Moolenbroek 
413*e985b929SDavid van Moolenbroek #ifdef WIN32
414*e985b929SDavid van Moolenbroek #define SLEEP_MS(ms) Sleep(ms)
415*e985b929SDavid van Moolenbroek #else
416*e985b929SDavid van Moolenbroek #define SLEEP_MS(ms) usleep((ms) * 1000)
417*e985b929SDavid van Moolenbroek #endif
418*e985b929SDavid van Moolenbroek 
419*e985b929SDavid van Moolenbroek struct deferred_test_data {
420*e985b929SDavid van Moolenbroek 	struct deferred_cb cbs[CB_COUNT];
421*e985b929SDavid van Moolenbroek 	struct deferred_cb_queue *queue;
422*e985b929SDavid van Moolenbroek };
423*e985b929SDavid van Moolenbroek 
424*e985b929SDavid van Moolenbroek static time_t timer_start = 0;
425*e985b929SDavid van Moolenbroek static time_t timer_end = 0;
426*e985b929SDavid van Moolenbroek static unsigned callback_count = 0;
427*e985b929SDavid van Moolenbroek static THREAD_T load_threads[QUEUE_THREAD_COUNT];
428*e985b929SDavid van Moolenbroek static struct deferred_test_data deferred_data[QUEUE_THREAD_COUNT];
429*e985b929SDavid van Moolenbroek 
430*e985b929SDavid van Moolenbroek static void
deferred_callback(struct deferred_cb * cb,void * arg)431*e985b929SDavid van Moolenbroek deferred_callback(struct deferred_cb *cb, void *arg)
432*e985b929SDavid van Moolenbroek {
433*e985b929SDavid van Moolenbroek 	SLEEP_MS(1);
434*e985b929SDavid van Moolenbroek 	callback_count += 1;
435*e985b929SDavid van Moolenbroek }
436*e985b929SDavid van Moolenbroek 
437*e985b929SDavid van Moolenbroek static THREAD_FN
load_deferred_queue(void * arg)438*e985b929SDavid van Moolenbroek load_deferred_queue(void *arg)
439*e985b929SDavid van Moolenbroek {
440*e985b929SDavid van Moolenbroek 	struct deferred_test_data *data = arg;
441*e985b929SDavid van Moolenbroek 	size_t i;
442*e985b929SDavid van Moolenbroek 
443*e985b929SDavid van Moolenbroek 	for (i = 0; i < CB_COUNT; ++i) {
444*e985b929SDavid van Moolenbroek 		event_deferred_cb_init(&data->cbs[i], deferred_callback, NULL);
445*e985b929SDavid van Moolenbroek 		event_deferred_cb_schedule(data->queue, &data->cbs[i]);
446*e985b929SDavid van Moolenbroek 		SLEEP_MS(1);
447*e985b929SDavid van Moolenbroek 	}
448*e985b929SDavid van Moolenbroek 
449*e985b929SDavid van Moolenbroek 	THREAD_RETURN();
450*e985b929SDavid van Moolenbroek }
451*e985b929SDavid van Moolenbroek 
452*e985b929SDavid van Moolenbroek static void
timer_callback(evutil_socket_t fd,short what,void * arg)453*e985b929SDavid van Moolenbroek timer_callback(evutil_socket_t fd, short what, void *arg)
454*e985b929SDavid van Moolenbroek {
455*e985b929SDavid van Moolenbroek 	timer_end = time(NULL);
456*e985b929SDavid van Moolenbroek }
457*e985b929SDavid van Moolenbroek 
458*e985b929SDavid van Moolenbroek static void
start_threads_callback(evutil_socket_t fd,short what,void * arg)459*e985b929SDavid van Moolenbroek start_threads_callback(evutil_socket_t fd, short what, void *arg)
460*e985b929SDavid van Moolenbroek {
461*e985b929SDavid van Moolenbroek 	int i;
462*e985b929SDavid van Moolenbroek 
463*e985b929SDavid van Moolenbroek 	for (i = 0; i < QUEUE_THREAD_COUNT; ++i) {
464*e985b929SDavid van Moolenbroek 		THREAD_START(load_threads[i], load_deferred_queue,
465*e985b929SDavid van Moolenbroek 				&deferred_data[i]);
466*e985b929SDavid van Moolenbroek 	}
467*e985b929SDavid van Moolenbroek }
468*e985b929SDavid van Moolenbroek 
469*e985b929SDavid van Moolenbroek static void
thread_deferred_cb_skew(void * arg)470*e985b929SDavid van Moolenbroek thread_deferred_cb_skew(void *arg)
471*e985b929SDavid van Moolenbroek {
472*e985b929SDavid van Moolenbroek 	struct basic_test_data *data = arg;
473*e985b929SDavid van Moolenbroek 	struct timeval tv_timer = {4, 0};
474*e985b929SDavid van Moolenbroek 	struct deferred_cb_queue *queue;
475*e985b929SDavid van Moolenbroek 	time_t elapsed;
476*e985b929SDavid van Moolenbroek 	int i;
477*e985b929SDavid van Moolenbroek 
478*e985b929SDavid van Moolenbroek 	queue = event_base_get_deferred_cb_queue(data->base);
479*e985b929SDavid van Moolenbroek 	tt_assert(queue);
480*e985b929SDavid van Moolenbroek 
481*e985b929SDavid van Moolenbroek 	for (i = 0; i < QUEUE_THREAD_COUNT; ++i)
482*e985b929SDavid van Moolenbroek 		deferred_data[i].queue = queue;
483*e985b929SDavid van Moolenbroek 
484*e985b929SDavid van Moolenbroek 	timer_start = time(NULL);
485*e985b929SDavid van Moolenbroek 	event_base_once(data->base, -1, EV_TIMEOUT, timer_callback, NULL,
486*e985b929SDavid van Moolenbroek 			&tv_timer);
487*e985b929SDavid van Moolenbroek 	event_base_once(data->base, -1, EV_TIMEOUT, start_threads_callback,
488*e985b929SDavid van Moolenbroek 			NULL, NULL);
489*e985b929SDavid van Moolenbroek 	event_base_dispatch(data->base);
490*e985b929SDavid van Moolenbroek 
491*e985b929SDavid van Moolenbroek 	elapsed = timer_end - timer_start;
492*e985b929SDavid van Moolenbroek 	TT_BLATHER(("callback count, %u", callback_count));
493*e985b929SDavid van Moolenbroek 	TT_BLATHER(("elapsed time, %u", (unsigned)elapsed));
494*e985b929SDavid van Moolenbroek 	/* XXX be more intelligent here.  just make sure skew is
495*e985b929SDavid van Moolenbroek 	 * within 2 seconds for now. */
496*e985b929SDavid van Moolenbroek 	tt_assert(elapsed >= 4 && elapsed <= 6);
497*e985b929SDavid van Moolenbroek 
498*e985b929SDavid van Moolenbroek end:
499*e985b929SDavid van Moolenbroek 	for (i = 0; i < QUEUE_THREAD_COUNT; ++i)
500*e985b929SDavid van Moolenbroek 		THREAD_JOIN(load_threads[i]);
501*e985b929SDavid van Moolenbroek }
502*e985b929SDavid van Moolenbroek 
503*e985b929SDavid van Moolenbroek #define TEST(name)							\
504*e985b929SDavid van Moolenbroek 	{ #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,	\
505*e985b929SDavid van Moolenbroek 	  &basic_setup, NULL }
506*e985b929SDavid van Moolenbroek 
507*e985b929SDavid van Moolenbroek struct testcase_t thread_testcases[] = {
508*e985b929SDavid van Moolenbroek 	{ "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
509*e985b929SDavid van Moolenbroek 	  &basic_setup, NULL },
510*e985b929SDavid van Moolenbroek #ifndef WIN32
511*e985b929SDavid van Moolenbroek 	{ "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
512*e985b929SDavid van Moolenbroek 	  &basic_setup, __UNCONST("forking") },
513*e985b929SDavid van Moolenbroek #endif
514*e985b929SDavid van Moolenbroek #if 0
515*e985b929SDavid van Moolenbroek 	TEST(conditions_simple),
516*e985b929SDavid van Moolenbroek #endif
517*e985b929SDavid van Moolenbroek 	TEST(deferred_cb_skew),
518*e985b929SDavid van Moolenbroek 	END_OF_TESTCASES
519*e985b929SDavid van Moolenbroek };
520*e985b929SDavid van Moolenbroek 
521