xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/test/regress_et.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: regress_et.c,v 1.6 2024/08/18 20:47:23 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*
48585484eSchristos  * Copyright (c) 2009-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 #include "event2/event-config.h"
308585484eSchristos 
318585484eSchristos #ifdef _WIN32
328585484eSchristos #include <winsock2.h>
338585484eSchristos #endif
348585484eSchristos #include <sys/types.h>
358585484eSchristos #include <sys/stat.h>
368585484eSchristos #ifdef EVENT__HAVE_SYS_SOCKET_H
378585484eSchristos #include <sys/socket.h>
388585484eSchristos #endif
398585484eSchristos #include <fcntl.h>
408585484eSchristos #include <stdlib.h>
418585484eSchristos #include <stdio.h>
428585484eSchristos #include <string.h>
438585484eSchristos #ifndef _WIN32
448585484eSchristos #include <sys/time.h>
458585484eSchristos #include <unistd.h>
468585484eSchristos #endif
478585484eSchristos #include <errno.h>
488585484eSchristos 
498585484eSchristos #include "event2/event.h"
508585484eSchristos #include "event2/util.h"
518585484eSchristos 
528585484eSchristos #include "regress.h"
538585484eSchristos 
548585484eSchristos static int was_et = 0;
558585484eSchristos 
56*eabc0478Schristos static int base_supports_et(struct event_base *base)
57*eabc0478Schristos {
58*eabc0478Schristos 	return
59*eabc0478Schristos 		(!strcmp(event_base_get_method(base), "epoll") ||
60*eabc0478Schristos 		!strcmp(event_base_get_method(base), "epoll (with changelist)") ||
61*eabc0478Schristos 		!strcmp(event_base_get_method(base), "kqueue"));
62*eabc0478Schristos }
63*eabc0478Schristos 
648585484eSchristos static void
658585484eSchristos read_cb(evutil_socket_t fd, short event, void *arg)
668585484eSchristos {
678585484eSchristos 	char buf;
688585484eSchristos 	int len;
698585484eSchristos 
708585484eSchristos 	len = recv(fd, &buf, sizeof(buf), 0);
718585484eSchristos 
728585484eSchristos 	called++;
738585484eSchristos 	if (event & EV_ET)
748585484eSchristos 		was_et = 1;
758585484eSchristos 
768585484eSchristos 	if (!len)
778585484eSchristos 		event_del(arg);
788585484eSchristos }
798585484eSchristos 
808585484eSchristos static void
81*eabc0478Schristos test_edgetriggered(void *data_)
828585484eSchristos {
83*eabc0478Schristos 	struct basic_test_data *data = data_;
84*eabc0478Schristos 	struct event_base *base = data->base;
85*eabc0478Schristos 	evutil_socket_t *pair = data->pair;
868585484eSchristos 	struct event *ev = NULL;
878585484eSchristos 	const char *test = "test string";
888585484eSchristos 	int supports_et;
898585484eSchristos 
908585484eSchristos 	/* On Linux 3.2.1 (at least, as patched by Fedora and tested by Nick),
918585484eSchristos 	 * doing a "recv" on an AF_UNIX socket resets the readability of the
928585484eSchristos 	 * socket, even though there is no state change, so we don't actually
938585484eSchristos 	 * get edge-triggered behavior.  Yuck!  Linux 3.1.9 didn't have this
948585484eSchristos 	 * problem.
958585484eSchristos 	 */
968585484eSchristos 
978585484eSchristos 	called = was_et = 0;
988585484eSchristos 
998585484eSchristos 	tt_int_op(send(pair[0], test, (int)strlen(test)+1, 0), >, 0);
100*eabc0478Schristos 	tt_int_op(shutdown(pair[0], EVUTIL_SHUT_WR), ==, 0);
1018585484eSchristos 
102*eabc0478Schristos 	supports_et = base_supports_et(base);
1038585484eSchristos 	TT_BLATHER(("Checking for edge-triggered events with %s, which should %s"
1048585484eSchristos 				"support edge-triggering", event_base_get_method(base),
1058585484eSchristos 				supports_et?"":"not "));
1068585484eSchristos 
107*eabc0478Schristos 	/* Initialize one event */
1088585484eSchristos 	ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, read_cb, &ev);
109*eabc0478Schristos 	tt_assert(ev != NULL);
110*eabc0478Schristos 	tt_int_op(event_add(ev, NULL), ==, 0);
1118585484eSchristos 
1128585484eSchristos 	/* We're going to call the dispatch function twice.  The first invocation
1138585484eSchristos 	 * will read a single byte from pair[1] in either case.  If we're edge
1148585484eSchristos 	 * triggered, we'll only see the event once (since we only see transitions
1158585484eSchristos 	 * from no data to data), so the second invocation of event_base_loop will
1168585484eSchristos 	 * do nothing.  If we're level triggered, the second invocation of
1178585484eSchristos 	 * event_base_loop will also activate the event (because there's still
1188585484eSchristos 	 * data to read). */
119*eabc0478Schristos 	tt_int_op(event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE), ==, 0);
120*eabc0478Schristos 	tt_int_op(event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE), ==, 0);
1218585484eSchristos 
1228585484eSchristos 	if (supports_et) {
1238585484eSchristos 		tt_int_op(called, ==, 1);
1248585484eSchristos 		tt_assert(was_et);
1258585484eSchristos 	} else {
1268585484eSchristos 		tt_int_op(called, ==, 2);
1278585484eSchristos 		tt_assert(!was_et);
1288585484eSchristos 	}
1298585484eSchristos 
1308585484eSchristos end:
1318585484eSchristos 	if (ev) {
1328585484eSchristos 		event_del(ev);
1338585484eSchristos 		event_free(ev);
1348585484eSchristos 	}
1358585484eSchristos }
1368585484eSchristos 
1378585484eSchristos static void
1388585484eSchristos test_edgetriggered_mix_error(void *data_)
1398585484eSchristos {
1408585484eSchristos 	struct basic_test_data *data = data_;
1418585484eSchristos 	struct event_base *base = NULL;
1428585484eSchristos 	struct event *ev_et=NULL, *ev_lt=NULL;
1438585484eSchristos 
1448585484eSchristos #ifdef EVENT__DISABLE_DEBUG_MODE
1458585484eSchristos 	if (1)
1468585484eSchristos 		tt_skip();
1478585484eSchristos #endif
1488585484eSchristos 
149b8ecfcfeSchristos 	if (!libevent_tests_running_in_debug_mode)
1508585484eSchristos 		event_enable_debug_mode();
1518585484eSchristos 
1528585484eSchristos 	base = event_base_new();
1538585484eSchristos 
1548585484eSchristos 	/* try mixing edge-triggered and level-triggered to make sure it fails*/
1558585484eSchristos 	ev_et = event_new(base, data->pair[0], EV_READ|EV_ET, read_cb, ev_et);
1568585484eSchristos 	tt_assert(ev_et);
1578585484eSchristos 	ev_lt = event_new(base, data->pair[0], EV_READ, read_cb, ev_lt);
1588585484eSchristos 	tt_assert(ev_lt);
1598585484eSchristos 
1608585484eSchristos 	/* Add edge-triggered, then level-triggered.  Get an error. */
1618585484eSchristos 	tt_int_op(0, ==, event_add(ev_et, NULL));
1628585484eSchristos 	tt_int_op(-1, ==, event_add(ev_lt, NULL));
1638585484eSchristos 	tt_int_op(EV_READ, ==, event_pending(ev_et, EV_READ, NULL));
1648585484eSchristos 	tt_int_op(0, ==, event_pending(ev_lt, EV_READ, NULL));
1658585484eSchristos 
1668585484eSchristos 	tt_int_op(0, ==, event_del(ev_et));
1678585484eSchristos 	/* Add level-triggered, then edge-triggered.  Get an error. */
1688585484eSchristos 	tt_int_op(0, ==, event_add(ev_lt, NULL));
1698585484eSchristos 	tt_int_op(-1, ==, event_add(ev_et, NULL));
1708585484eSchristos 	tt_int_op(EV_READ, ==, event_pending(ev_lt, EV_READ, NULL));
1718585484eSchristos 	tt_int_op(0, ==, event_pending(ev_et, EV_READ, NULL));
1728585484eSchristos 
1738585484eSchristos end:
1748585484eSchristos 	if (ev_et)
1758585484eSchristos 		event_free(ev_et);
1768585484eSchristos 	if (ev_lt)
1778585484eSchristos 		event_free(ev_lt);
1788585484eSchristos 	if (base)
1798585484eSchristos 		event_base_free(base);
1808585484eSchristos }
1818585484eSchristos 
182*eabc0478Schristos static int read_notification_count;
183*eabc0478Schristos static int last_read_notification_was_et;
184*eabc0478Schristos static void
185*eabc0478Schristos read_notification_cb(evutil_socket_t fd, short event, void *arg)
186*eabc0478Schristos {
187*eabc0478Schristos 	read_notification_count++;
188*eabc0478Schristos 	last_read_notification_was_et = (event & EV_ET);
189*eabc0478Schristos }
190*eabc0478Schristos 
191*eabc0478Schristos static int write_notification_count;
192*eabc0478Schristos static int last_write_notification_was_et;
193*eabc0478Schristos static void
194*eabc0478Schristos write_notification_cb(evutil_socket_t fd, short event, void *arg)
195*eabc0478Schristos {
196*eabc0478Schristos 	write_notification_count++;
197*eabc0478Schristos 	last_write_notification_was_et = (event & EV_ET);
198*eabc0478Schristos }
199*eabc0478Schristos 
200*eabc0478Schristos /* After two or more events have been registered for the same
201*eabc0478Schristos  * file descriptor using EV_ET, if one of the events is
202*eabc0478Schristos  * deleted, then the epoll_ctl() call issued by libevent drops
203*eabc0478Schristos  * the EPOLLET flag resulting in level triggered
204*eabc0478Schristos  * notifications.
205*eabc0478Schristos  */
206*eabc0478Schristos static void
207*eabc0478Schristos test_edge_triggered_multiple_events(void *data_)
208*eabc0478Schristos {
209*eabc0478Schristos 	struct basic_test_data *data = data_;
210*eabc0478Schristos 	struct event *read_ev = NULL;
211*eabc0478Schristos 	struct event *write_ev = NULL;
212*eabc0478Schristos 	const char c = 'A';
213*eabc0478Schristos 	struct event_base *base = data->base;
214*eabc0478Schristos 	evutil_socket_t *pair = data->pair;
215*eabc0478Schristos 
216*eabc0478Schristos 	if (!base_supports_et(base)) {
217*eabc0478Schristos 		tt_skip();
218*eabc0478Schristos 		return;
219*eabc0478Schristos 	}
220*eabc0478Schristos 
221*eabc0478Schristos 	read_notification_count = 0;
222*eabc0478Schristos 	last_read_notification_was_et = 0;
223*eabc0478Schristos 	write_notification_count = 0;
224*eabc0478Schristos 	last_write_notification_was_et = 0;
225*eabc0478Schristos 
226*eabc0478Schristos 	/* Make pair[1] readable */
227*eabc0478Schristos 	tt_int_op(send(pair[0], &c, 1, 0), >, 0);
228*eabc0478Schristos 
229*eabc0478Schristos 	read_ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST,
230*eabc0478Schristos 		read_notification_cb, NULL);
231*eabc0478Schristos 	write_ev = event_new(base, pair[1], EV_WRITE|EV_ET|EV_PERSIST,
232*eabc0478Schristos 		write_notification_cb, NULL);
233*eabc0478Schristos 
234*eabc0478Schristos 	event_add(read_ev, NULL);
235*eabc0478Schristos 	event_add(write_ev, NULL);
236*eabc0478Schristos 	event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE);
237*eabc0478Schristos 	event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE);
238*eabc0478Schristos 
239*eabc0478Schristos 	tt_assert(last_read_notification_was_et);
240*eabc0478Schristos 	tt_int_op(read_notification_count, ==, 1);
241*eabc0478Schristos 	tt_assert(last_write_notification_was_et);
242*eabc0478Schristos 	tt_int_op(write_notification_count, ==, 1);
243*eabc0478Schristos 
244*eabc0478Schristos 	event_del(read_ev);
245*eabc0478Schristos 
246*eabc0478Schristos 	/* trigger acitivity second time for the backend that can have multiple
247*eabc0478Schristos 	 * events for one fd (like kqueue) */
248*eabc0478Schristos 	close(pair[0]);
249*eabc0478Schristos 	pair[0] = -1;
250*eabc0478Schristos 
251*eabc0478Schristos 	/* Verify that we are still edge-triggered for write notifications */
252*eabc0478Schristos 	event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE);
253*eabc0478Schristos 	event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE);
254*eabc0478Schristos 	tt_assert(last_write_notification_was_et);
255*eabc0478Schristos 	tt_int_op(write_notification_count, ==, 2);
256*eabc0478Schristos 
257*eabc0478Schristos end:
258*eabc0478Schristos 	if (read_ev)
259*eabc0478Schristos 		event_free(read_ev);
260*eabc0478Schristos 	if (write_ev)
261*eabc0478Schristos 		event_free(write_ev);
262*eabc0478Schristos }
263*eabc0478Schristos 
2648585484eSchristos struct testcase_t edgetriggered_testcases[] = {
265*eabc0478Schristos 	{ "et", test_edgetriggered,
266*eabc0478Schristos 	  TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, NULL },
2678585484eSchristos 	{ "et_mix_error", test_edgetriggered_mix_error,
2688585484eSchristos 	  TT_FORK|TT_NEED_SOCKETPAIR|TT_NO_LOGS, &basic_setup, NULL },
269*eabc0478Schristos 	{ "et_multiple_events", test_edge_triggered_multiple_events,
270*eabc0478Schristos 	  TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, NULL },
2718585484eSchristos 	END_OF_TESTCASES
2728585484eSchristos };
273