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