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