1 /* 2 * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 #include "../util-internal.h" 27 #include "event2/event-config.h" 28 29 #ifdef _WIN32 30 #include <winsock2.h> 31 #endif 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #ifdef EVENT__HAVE_SYS_SOCKET_H 35 #include <sys/socket.h> 36 #endif 37 #include <fcntl.h> 38 #include <stdlib.h> 39 #include <stdio.h> 40 #include <string.h> 41 #ifndef _WIN32 42 #include <sys/time.h> 43 #include <unistd.h> 44 #endif 45 #include <errno.h> 46 47 #include "event2/event.h" 48 #include "event2/util.h" 49 50 #include "regress.h" 51 52 static int was_et = 0; 53 54 static int base_supports_et(struct event_base *base) 55 { 56 return 57 (!strcmp(event_base_get_method(base), "epoll") || 58 !strcmp(event_base_get_method(base), "epoll (with changelist)") || 59 !strcmp(event_base_get_method(base), "kqueue")); 60 } 61 62 static void 63 read_cb(evutil_socket_t fd, short event, void *arg) 64 { 65 char buf; 66 int len; 67 68 len = recv(fd, &buf, sizeof(buf), 0); 69 70 called++; 71 if (event & EV_ET) 72 was_et = 1; 73 74 if (!len) 75 event_del(arg); 76 } 77 78 static void 79 test_edgetriggered(void *data_) 80 { 81 struct basic_test_data *data = data_; 82 struct event_base *base = data->base; 83 evutil_socket_t *pair = data->pair; 84 struct event *ev = NULL; 85 const char *test = "test string"; 86 int supports_et; 87 88 /* On Linux 3.2.1 (at least, as patched by Fedora and tested by Nick), 89 * doing a "recv" on an AF_UNIX socket resets the readability of the 90 * socket, even though there is no state change, so we don't actually 91 * get edge-triggered behavior. Yuck! Linux 3.1.9 didn't have this 92 * problem. 93 */ 94 95 called = was_et = 0; 96 97 tt_int_op(send(pair[0], test, (int)strlen(test)+1, 0), >, 0); 98 tt_int_op(shutdown(pair[0], EVUTIL_SHUT_WR), ==, 0); 99 100 supports_et = base_supports_et(base); 101 TT_BLATHER(("Checking for edge-triggered events with %s, which should %s" 102 "support edge-triggering", event_base_get_method(base), 103 supports_et?"":"not ")); 104 105 /* Initialize one event */ 106 ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, read_cb, &ev); 107 tt_assert(ev != NULL); 108 tt_int_op(event_add(ev, NULL), ==, 0); 109 110 /* We're going to call the dispatch function twice. The first invocation 111 * will read a single byte from pair[1] in either case. If we're edge 112 * triggered, we'll only see the event once (since we only see transitions 113 * from no data to data), so the second invocation of event_base_loop will 114 * do nothing. If we're level triggered, the second invocation of 115 * event_base_loop will also activate the event (because there's still 116 * data to read). */ 117 tt_int_op(event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE), ==, 0); 118 tt_int_op(event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE), ==, 0); 119 120 if (supports_et) { 121 tt_int_op(called, ==, 1); 122 tt_assert(was_et); 123 } else { 124 tt_int_op(called, ==, 2); 125 tt_assert(!was_et); 126 } 127 128 end: 129 if (ev) { 130 event_del(ev); 131 event_free(ev); 132 } 133 } 134 135 static void 136 test_edgetriggered_mix_error(void *data_) 137 { 138 struct basic_test_data *data = data_; 139 struct event_base *base = NULL; 140 struct event *ev_et=NULL, *ev_lt=NULL; 141 142 #ifdef EVENT__DISABLE_DEBUG_MODE 143 if (1) 144 tt_skip(); 145 #endif 146 147 if (!libevent_tests_running_in_debug_mode) 148 event_enable_debug_mode(); 149 150 base = event_base_new(); 151 152 /* try mixing edge-triggered and level-triggered to make sure it fails*/ 153 ev_et = event_new(base, data->pair[0], EV_READ|EV_ET, read_cb, ev_et); 154 tt_assert(ev_et); 155 ev_lt = event_new(base, data->pair[0], EV_READ, read_cb, ev_lt); 156 tt_assert(ev_lt); 157 158 /* Add edge-triggered, then level-triggered. Get an error. */ 159 tt_int_op(0, ==, event_add(ev_et, NULL)); 160 tt_int_op(-1, ==, event_add(ev_lt, NULL)); 161 tt_int_op(EV_READ, ==, event_pending(ev_et, EV_READ, NULL)); 162 tt_int_op(0, ==, event_pending(ev_lt, EV_READ, NULL)); 163 164 tt_int_op(0, ==, event_del(ev_et)); 165 /* Add level-triggered, then edge-triggered. Get an error. */ 166 tt_int_op(0, ==, event_add(ev_lt, NULL)); 167 tt_int_op(-1, ==, event_add(ev_et, NULL)); 168 tt_int_op(EV_READ, ==, event_pending(ev_lt, EV_READ, NULL)); 169 tt_int_op(0, ==, event_pending(ev_et, EV_READ, NULL)); 170 171 end: 172 if (ev_et) 173 event_free(ev_et); 174 if (ev_lt) 175 event_free(ev_lt); 176 if (base) 177 event_base_free(base); 178 } 179 180 static int read_notification_count; 181 static int last_read_notification_was_et; 182 static void 183 read_notification_cb(evutil_socket_t fd, short event, void *arg) 184 { 185 read_notification_count++; 186 last_read_notification_was_et = (event & EV_ET); 187 } 188 189 static int write_notification_count; 190 static int last_write_notification_was_et; 191 static void 192 write_notification_cb(evutil_socket_t fd, short event, void *arg) 193 { 194 write_notification_count++; 195 last_write_notification_was_et = (event & EV_ET); 196 } 197 198 /* After two or more events have been registered for the same 199 * file descriptor using EV_ET, if one of the events is 200 * deleted, then the epoll_ctl() call issued by libevent drops 201 * the EPOLLET flag resulting in level triggered 202 * notifications. 203 */ 204 static void 205 test_edge_triggered_multiple_events(void *data_) 206 { 207 struct basic_test_data *data = data_; 208 struct event *read_ev = NULL; 209 struct event *write_ev = NULL; 210 const char c = 'A'; 211 struct event_base *base = data->base; 212 evutil_socket_t *pair = data->pair; 213 214 if (!base_supports_et(base)) { 215 tt_skip(); 216 return; 217 } 218 219 read_notification_count = 0; 220 last_read_notification_was_et = 0; 221 write_notification_count = 0; 222 last_write_notification_was_et = 0; 223 224 /* Make pair[1] readable */ 225 tt_int_op(send(pair[0], &c, 1, 0), >, 0); 226 227 read_ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, 228 read_notification_cb, NULL); 229 write_ev = event_new(base, pair[1], EV_WRITE|EV_ET|EV_PERSIST, 230 write_notification_cb, NULL); 231 232 event_add(read_ev, NULL); 233 event_add(write_ev, NULL); 234 event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); 235 event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); 236 237 tt_assert(last_read_notification_was_et); 238 tt_int_op(read_notification_count, ==, 1); 239 tt_assert(last_write_notification_was_et); 240 tt_int_op(write_notification_count, ==, 1); 241 242 event_del(read_ev); 243 244 /* trigger acitivity second time for the backend that can have multiple 245 * events for one fd (like kqueue) */ 246 close(pair[0]); 247 pair[0] = -1; 248 249 /* Verify that we are still edge-triggered for write notifications */ 250 event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); 251 event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); 252 tt_assert(last_write_notification_was_et); 253 tt_int_op(write_notification_count, ==, 2); 254 255 end: 256 if (read_ev) 257 event_free(read_ev); 258 if (write_ev) 259 event_free(write_ev); 260 } 261 262 struct testcase_t edgetriggered_testcases[] = { 263 { "et", test_edgetriggered, 264 TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, NULL }, 265 { "et_mix_error", test_edgetriggered_mix_error, 266 TT_FORK|TT_NEED_SOCKETPAIR|TT_NO_LOGS, &basic_setup, NULL }, 267 { "et_multiple_events", test_edge_triggered_multiple_events, 268 TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, NULL }, 269 END_OF_TESTCASES 270 }; 271