1 /* 2 * event.h 3 */ 4 5 /*- 6 * SPDX-License-Identifier: BSD-2-Clause 7 * 8 * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 34 /* 35 * Hack to provide libevent (see devel/libevent port) like API. 36 * Should be removed if FreeBSD ever decides to import libevent into base. 37 */ 38 39 #include <sys/select.h> 40 #include <sys/time.h> 41 #include <sys/queue.h> 42 #include <assert.h> 43 #include <stdarg.h> 44 #include <stdio.h> 45 #include <string.h> 46 #include <syslog.h> 47 48 #include "event.h" 49 #define L2CAP_SOCKET_CHECKED 50 #include "btpand.h" 51 52 #define __event_link(ev) \ 53 do { \ 54 TAILQ_INSERT_TAIL(&pending, ev, next); \ 55 ev->flags |= EV_PENDING; \ 56 } while (0) 57 58 static void tv_add(struct timeval *, struct timeval const *); 59 static void tv_sub(struct timeval *, struct timeval const *); 60 static int tv_cmp(struct timeval const *, struct timeval const *); 61 static int __event_dispatch(void); 62 static void __event_add_current(struct event *); 63 static void __event_del_current(struct event *); 64 65 66 static TAILQ_HEAD(, event) pending; 67 static TAILQ_HEAD(, event) current; 68 69 void 70 event_init(void) 71 { 72 TAILQ_INIT(&pending); 73 } 74 75 int 76 event_dispatch(void) 77 { 78 while (__event_dispatch() == 0) 79 ; 80 81 return (-1); 82 } 83 84 static int 85 __event_dispatch(void) 86 { 87 fd_set r, w; 88 int nfd; 89 struct event *ev; 90 struct timeval now, timeout, t; 91 92 FD_ZERO(&r); 93 FD_ZERO(&w); 94 95 nfd = 0; 96 97 gettimeofday(&now, NULL); 98 99 timeout.tv_sec = 10; /* arbitrary */ 100 timeout.tv_usec = 0; 101 102 TAILQ_INIT(¤t); 103 104 /* 105 * Build fd_set's 106 */ 107 108 event_log_debug("%s: building fd set...", __func__); 109 110 while (!TAILQ_EMPTY(&pending)) { 111 ev = TAILQ_FIRST(&pending); 112 event_del(ev); 113 114 if (ev->flags & EV_HAS_TIMEOUT) { 115 if (tv_cmp(&now, &ev->expire) >= 0) 116 t.tv_sec = t.tv_usec = 0; 117 else { 118 t = ev->expire; 119 tv_sub(&t, &now); 120 } 121 122 if (tv_cmp(&t, &timeout) < 0) 123 timeout = t; 124 } 125 126 if (ev->fd >= 0) { 127 if (ev->flags & EV_READ) { 128 FD_SET(ev->fd, &r); 129 nfd = (nfd > ev->fd) ? nfd : ev->fd; 130 } 131 132 if (ev->flags & EV_WRITE) { 133 FD_SET(ev->fd, &w); 134 nfd = (nfd > ev->fd) ? nfd : ev->fd; 135 } 136 } 137 138 __event_add_current(ev); 139 } 140 141 event_log_debug("%s: waiting for events...", __func__); 142 143 nfd = select(nfd + 1, &r, &w, NULL, &timeout); 144 if (nfd < 0) 145 return (-1); 146 147 /* 148 * Process current pending 149 */ 150 151 event_log_debug("%s: processing events...", __func__); 152 153 gettimeofday(&now, NULL); 154 155 while (!TAILQ_EMPTY(¤t)) { 156 ev = TAILQ_FIRST(¤t); 157 __event_del_current(ev); 158 159 /* check if fd is ready for reading/writing */ 160 if (nfd > 0 && ev->fd >= 0) { 161 if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) { 162 if (ev->flags & EV_PERSIST) { 163 if (ev->flags & EV_HAS_TIMEOUT) 164 event_add(ev, &ev->timeout); 165 else 166 event_add(ev, NULL); 167 } 168 169 nfd --; 170 171 event_log_debug("%s: calling %p(%d, %p), " \ 172 "ev=%p", __func__, ev->cb, ev->fd, 173 ev->cbarg, ev); 174 175 (ev->cb)(ev->fd, 176 (ev->flags & (EV_READ|EV_WRITE)), 177 ev->cbarg); 178 179 continue; 180 } 181 } 182 183 /* if event has no timeout - just requeue */ 184 if ((ev->flags & EV_HAS_TIMEOUT) == 0) { 185 event_add(ev, NULL); 186 continue; 187 } 188 189 /* check if event has expired */ 190 if (tv_cmp(&now, &ev->expire) >= 0) { 191 if (ev->flags & EV_PERSIST) 192 event_add(ev, &ev->timeout); 193 194 event_log_debug("%s: calling %p(%d, %p), ev=%p", 195 __func__, ev->cb, ev->fd, ev->cbarg, ev); 196 197 (ev->cb)(ev->fd, 198 (ev->flags & (EV_READ|EV_WRITE)), 199 ev->cbarg); 200 201 continue; 202 } 203 204 assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); 205 __event_link(ev); 206 } 207 208 return (0); 209 } 210 211 void 212 __event_set(struct event *ev, int fd, short flags, 213 void (*cb)(int, short, void *), void *cbarg) 214 { 215 ev->fd = fd; 216 ev->flags = flags; 217 ev->cb = cb; 218 ev->cbarg = cbarg; 219 } 220 221 int 222 __event_add(struct event *ev, const struct timeval *timeout) 223 { 224 assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); 225 226 if (timeout != NULL) { 227 gettimeofday(&ev->expire, NULL); 228 tv_add(&ev->expire, timeout); 229 ev->timeout = *timeout; 230 ev->flags |= EV_HAS_TIMEOUT; 231 } else 232 ev->flags &= ~EV_HAS_TIMEOUT; 233 234 __event_link(ev); 235 236 return (0); 237 } 238 239 int 240 __event_del(struct event *ev) 241 { 242 assert((ev->flags & EV_CURRENT) == 0); 243 244 if ((ev->flags & EV_PENDING) != 0) { 245 TAILQ_REMOVE(&pending, ev, next); 246 ev->flags &= ~EV_PENDING; 247 } 248 249 return (0); 250 } 251 252 static void 253 __event_add_current(struct event *ev) 254 { 255 assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); 256 257 TAILQ_INSERT_TAIL(¤t, ev, next); 258 ev->flags |= EV_CURRENT; 259 } 260 261 static void 262 __event_del_current(struct event *ev) 263 { 264 assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT); 265 266 TAILQ_REMOVE(¤t, ev, next); 267 ev->flags &= ~EV_CURRENT; 268 } 269 270 static void 271 tv_add(struct timeval *a, struct timeval const *b) 272 { 273 a->tv_sec += b->tv_sec; 274 a->tv_usec += b->tv_usec; 275 276 if(a->tv_usec >= 1000000) { 277 a->tv_usec -= 1000000; 278 a->tv_sec += 1; 279 } 280 } 281 282 static void 283 tv_sub(struct timeval *a, struct timeval const *b) 284 { 285 if (a->tv_usec < b->tv_usec) { 286 a->tv_usec += 1000000; 287 a->tv_sec -= 1; 288 } 289 290 a->tv_usec -= b->tv_usec; 291 a->tv_sec -= b->tv_sec; 292 } 293 294 static int 295 tv_cmp(struct timeval const *a, struct timeval const *b) 296 { 297 if (a->tv_sec > b->tv_sec) 298 return (1); 299 300 if (a->tv_sec < b->tv_sec) 301 return (-1); 302 303 if (a->tv_usec > b->tv_usec) 304 return (1); 305 306 if (a->tv_usec < b->tv_usec) 307 return (-1); 308 309 return (0); 310 } 311 312