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