1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1995-1999 by Internet Software Consortium 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 19 * SOFTWARE. 20 */ 21 22 #pragma ident "%Z%%M% %I% %E% SMI" 23 24 /* ev_files.c - implement asynch file IO for the eventlib 25 * vix 11sep95 [initial] 26 */ 27 28 #if !defined(LINT) && !defined(CODECENTER) 29 static const char rcsid[] = "$Id: ev_files.c,v 1.22 2002/07/08 05:50:07 marka Exp $"; 30 #endif 31 32 #include "port_before.h" 33 #include "fd_setsize.h" 34 35 #include <sys/types.h> 36 #include <sys/time.h> 37 #include <sys/ioctl.h> 38 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <unistd.h> 42 43 #include <isc/eventlib.h> 44 #include "eventlib_p.h" 45 46 #include "port_after.h" 47 48 static evFile *FindFD(const evContext_p *ctx, int fd, int eventmask); 49 50 int 51 evSelectFD(evContext opaqueCtx, 52 int fd, 53 int eventmask, 54 evFileFunc func, 55 void *uap, 56 evFileID *opaqueID 57 ) { 58 evContext_p *ctx = opaqueCtx.opaque; 59 evFile *id; 60 int mode; 61 62 evPrintf(ctx, 1, 63 "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n", 64 ctx, fd, eventmask, func, uap); 65 if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0) 66 EV_ERR(EINVAL); 67 #ifdef SUNW_POLL 68 #else 69 if (fd > ctx->highestFD) 70 EV_ERR(EINVAL); 71 #endif /* SUNW_POLL */ 72 OK(mode = fcntl(fd, F_GETFL, NULL)); /* side effect: validate fd. */ 73 74 /* 75 * The first time we touch a file descriptor, we need to check to see 76 * if the application already had it in O_NONBLOCK mode and if so, all 77 * of our deselect()'s have to leave it in O_NONBLOCK. If not, then 78 * all but our last deselect() has to leave it in O_NONBLOCK. 79 */ 80 #ifdef SUNW_POLL 81 /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */ 82 if (fd >= ctx->maxnfds) 83 evPollfdRealloc(ctx, 1, fd); 84 #endif /* SUNW_POLL */ 85 id = FindFD(ctx, fd, EV_MASK_ALL); 86 if (id == NULL) { 87 if (mode & PORT_NONBLOCK) 88 FD_SET(fd, &ctx->nonblockBefore); 89 else { 90 #ifdef USE_FIONBIO_IOCTL 91 int on = 1; 92 OK(ioctl(fd, FIONBIO, (char *)&on)); 93 #else 94 OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK)); 95 #endif 96 FD_CLR(fd, &ctx->nonblockBefore); 97 } 98 } 99 100 /* 101 * If this descriptor is already in use, search for it again to see 102 * if any of the eventmask bits we want to set are already captured. 103 * We cannot usefully capture the same fd event more than once in the 104 * same context. 105 */ 106 if (id != NULL && FindFD(ctx, fd, eventmask) != NULL) 107 EV_ERR(ETOOMANYREFS); 108 109 /* Allocate and fill. */ 110 OKNEW(id); 111 id->func = func; 112 id->uap = uap; 113 id->fd = fd; 114 id->eventmask = eventmask; 115 116 /* 117 * Insert at head. Order could be important for performance if we 118 * believe that evGetNext()'s accesses to the fd_sets will be more 119 * serial and therefore more cache-lucky if the list is ordered by 120 * ``fd.'' We do not believe these things, so we don't do it. 121 * 122 * The interesting sequence is where GetNext() has cached a select() 123 * result and the caller decides to evSelectFD() on some descriptor. 124 * Since GetNext() starts at the head, it can miss new entries we add 125 * at the head. This is not a serious problem since the event being 126 * evSelectFD()'d for has to occur before evSelectFD() is called for 127 * the file event to be considered "missed" -- a real corner case. 128 * Maintaining a "tail" pointer for ctx->files would fix this, but I'm 129 * not sure it would be ``more correct.'' 130 */ 131 if (ctx->files != NULL) 132 ctx->files->prev = id; 133 id->prev = NULL; 134 id->next = ctx->files; 135 ctx->files = id; 136 137 /* Insert into fd table. */ 138 if (ctx->fdTable[fd] != NULL) 139 ctx->fdTable[fd]->fdprev = id; 140 id->fdprev = NULL; 141 id->fdnext = ctx->fdTable[fd]; 142 ctx->fdTable[fd] = id; 143 144 /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */ 145 if (eventmask & EV_READ) 146 FD_SET(fd, &ctx->rdNext); 147 if (eventmask & EV_WRITE) 148 FD_SET(fd, &ctx->wrNext); 149 if (eventmask & EV_EXCEPT) 150 FD_SET(fd, &ctx->exNext); 151 152 /* Update fdMax. */ 153 if (fd > ctx->fdMax) 154 ctx->fdMax = fd; 155 156 /* Remember the ID if the caller provided us a place for it. */ 157 if (opaqueID) 158 opaqueID->opaque = id; 159 160 #ifdef SUNW_POLL 161 #else 162 evPrintf(ctx, 5, 163 "evSelectFD(fd %d, mask 0x%x): new masks: 0x%lx 0x%lx 0x%lx\n", 164 fd, eventmask, 165 (u_long)ctx->rdNext.fds_bits[0], 166 (u_long)ctx->wrNext.fds_bits[0], 167 (u_long)ctx->exNext.fds_bits[0]); 168 #endif /* SUNW_POLL */ 169 170 return (0); 171 } 172 173 int 174 evDeselectFD(evContext opaqueCtx, evFileID opaqueID) { 175 evContext_p *ctx = opaqueCtx.opaque; 176 evFile *del = opaqueID.opaque; 177 evFile *cur; 178 int mode, eventmask; 179 180 if (!del) { 181 evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n"); 182 errno = EINVAL; 183 return (-1); 184 } 185 186 evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n", 187 del->fd, del->eventmask); 188 189 /* Get the mode. Unless the file has been closed, errors are bad. */ 190 mode = fcntl(del->fd, F_GETFL, NULL); 191 if (mode == -1 && errno != EBADF) 192 EV_ERR(errno); 193 194 /* Remove from the list of files. */ 195 if (del->prev != NULL) 196 del->prev->next = del->next; 197 else 198 ctx->files = del->next; 199 if (del->next != NULL) 200 del->next->prev = del->prev; 201 202 /* Remove from the fd table. */ 203 if (del->fdprev != NULL) 204 del->fdprev->fdnext = del->fdnext; 205 else 206 ctx->fdTable[del->fd] = del->fdnext; 207 if (del->fdnext != NULL) 208 del->fdnext->fdprev = del->fdprev; 209 210 /* 211 * If the file descriptor does not appear in any other select() entry, 212 * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode 213 * earlier, then: restore the fd to blocking status. 214 */ 215 if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) && 216 !FD_ISSET(del->fd, &ctx->nonblockBefore) && 217 mode != -1) { 218 /* 219 * Note that we won't return an error status to the caller if 220 * this fcntl() fails since (a) we've already done the work 221 * and (b) the caller didn't ask us anything about O_NONBLOCK. 222 */ 223 #ifdef USE_FIONBIO_IOCTL 224 int off = 1; 225 (void) ioctl(del->fd, FIONBIO, (char *)&off); 226 #else 227 (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK); 228 #endif 229 } 230 231 /* 232 * Now find all other uses of this descriptor and OR together an event 233 * mask so that we don't turn off {rd,wr,ex}Next bits that some other 234 * file event is using. As an optimization, stop if the event mask 235 * fills. 236 */ 237 eventmask = 0; 238 for ((void)NULL; 239 cur != NULL && eventmask != EV_MASK_ALL; 240 cur = cur->next) 241 if (cur->fd == del->fd) 242 eventmask |= cur->eventmask; 243 244 /* OK, now we know which bits we can clear out. */ 245 if (!(eventmask & EV_READ)) { 246 FD_CLR(del->fd, &ctx->rdNext); 247 if (FD_ISSET(del->fd, &ctx->rdLast)) { 248 FD_CLR(del->fd, &ctx->rdLast); 249 ctx->fdCount--; 250 } 251 } 252 if (!(eventmask & EV_WRITE)) { 253 FD_CLR(del->fd, &ctx->wrNext); 254 if (FD_ISSET(del->fd, &ctx->wrLast)) { 255 FD_CLR(del->fd, &ctx->wrLast); 256 ctx->fdCount--; 257 } 258 } 259 if (!(eventmask & EV_EXCEPT)) { 260 FD_CLR(del->fd, &ctx->exNext); 261 if (FD_ISSET(del->fd, &ctx->exLast)) { 262 FD_CLR(del->fd, &ctx->exLast); 263 ctx->fdCount--; 264 } 265 } 266 267 /* If this was the maxFD, find the new one. */ 268 if (del->fd == ctx->fdMax) { 269 ctx->fdMax = -1; 270 for (cur = ctx->files; cur; cur = cur->next) 271 if (cur->fd > ctx->fdMax) 272 ctx->fdMax = cur->fd; 273 } 274 275 /* If this was the fdNext, cycle that to the next entry. */ 276 if (del == ctx->fdNext) 277 ctx->fdNext = del->next; 278 279 #ifdef SUNW_POLL 280 #else 281 evPrintf(ctx, 5, 282 "evDeselectFD(fd %d, mask 0x%x): new masks: 0x%lx 0x%lx 0x%lx\n", 283 del->fd, eventmask, 284 (u_long)ctx->rdNext.fds_bits[0], 285 (u_long)ctx->wrNext.fds_bits[0], 286 (u_long)ctx->exNext.fds_bits[0]); 287 #endif /* SUNW_POLL */ 288 289 /* Couldn't free it before now since we were using fields out of it. */ 290 FREE(del); 291 292 return (0); 293 } 294 295 static evFile * 296 FindFD(const evContext_p *ctx, int fd, int eventmask) { 297 evFile *id; 298 299 for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext) 300 if (id->fd == fd && (id->eventmask & eventmask) != 0) 301 break; 302 return (id); 303 } 304