1 /* 2 * Copyright 1997-2002 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1996-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_streams.c - implement asynch stream file IO for the eventlib 25 * vix 04mar96 [initial] 26 */ 27 28 #if !defined(LINT) && !defined(CODECENTER) 29 static const char rcsid[] = "$Id: ev_streams.c,v 8.22 2001/05/29 05:49:29 marka Exp $"; 30 #endif 31 32 #include "port_before.h" 33 #include "fd_setsize.h" 34 35 #include <sys/types.h> 36 #include <sys/uio.h> 37 38 #include <errno.h> 39 40 #include <isc/eventlib.h> 41 #include <isc/assertions.h> 42 #include "eventlib_p.h" 43 44 #include "port_after.h" 45 46 static int copyvec(evStream *str, const struct iovec *iov, int iocnt); 47 static void consume(evStream *str, size_t bytes); 48 static void done(evContext opaqueCtx, evStream *str); 49 static void writable(evContext opaqueCtx, void *uap, int fd, int evmask); 50 static void readable(evContext opaqueCtx, void *uap, int fd, int evmask); 51 52 struct iovec 53 evConsIovec(void *buf, size_t cnt) { 54 struct iovec ret; 55 56 memset(&ret, 0xf5, sizeof ret); 57 ret.iov_base = buf; 58 ret.iov_len = cnt; 59 return (ret); 60 } 61 62 int 63 evWrite(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt, 64 evStreamFunc func, void *uap, evStreamID *id) 65 { 66 evContext_p *ctx = opaqueCtx.opaque; 67 evStream *new; 68 int save; 69 70 OKNEW(new); 71 new->func = func; 72 new->uap = uap; 73 new->fd = fd; 74 new->flags = 0; 75 if (evSelectFD(opaqueCtx, fd, EV_WRITE, writable, new, &new->file) < 0) 76 goto free; 77 if (copyvec(new, iov, iocnt) < 0) 78 goto free; 79 new->prevDone = NULL; 80 new->nextDone = NULL; 81 if (ctx->streams != NULL) 82 ctx->streams->prev = new; 83 new->prev = NULL; 84 new->next = ctx->streams; 85 ctx->streams = new; 86 if (id != NULL) 87 id->opaque = new; 88 return (0); 89 free: 90 save = errno; 91 FREE(new); 92 errno = save; 93 return (-1); 94 } 95 96 int 97 evRead(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt, 98 evStreamFunc func, void *uap, evStreamID *id) 99 { 100 evContext_p *ctx = opaqueCtx.opaque; 101 evStream *new; 102 int save; 103 104 OKNEW(new); 105 new->func = func; 106 new->uap = uap; 107 new->fd = fd; 108 new->flags = 0; 109 if (evSelectFD(opaqueCtx, fd, EV_READ, readable, new, &new->file) < 0) 110 goto free; 111 if (copyvec(new, iov, iocnt) < 0) 112 goto free; 113 new->prevDone = NULL; 114 new->nextDone = NULL; 115 if (ctx->streams != NULL) 116 ctx->streams->prev = new; 117 new->prev = NULL; 118 new->next = ctx->streams; 119 ctx->streams = new; 120 if (id) 121 id->opaque = new; 122 return (0); 123 free: 124 save = errno; 125 FREE(new); 126 errno = save; 127 return (-1); 128 } 129 130 int 131 evTimeRW(evContext opaqueCtx, evStreamID id, evTimerID timer) /*ARGSUSED*/ { 132 evStream *str = id.opaque; 133 134 UNUSED(opaqueCtx); 135 136 str->timer = timer; 137 str->flags |= EV_STR_TIMEROK; 138 return (0); 139 } 140 141 int 142 evUntimeRW(evContext opaqueCtx, evStreamID id) /*ARGSUSED*/ { 143 evStream *str = id.opaque; 144 145 UNUSED(opaqueCtx); 146 147 str->flags &= ~EV_STR_TIMEROK; 148 return (0); 149 } 150 151 int 152 evCancelRW(evContext opaqueCtx, evStreamID id) { 153 evContext_p *ctx = opaqueCtx.opaque; 154 evStream *old = id.opaque; 155 156 /* 157 * The streams list is doubly threaded. First, there's ctx->streams 158 * that's used by evDestroy() to find and cancel all streams. Second, 159 * there's ctx->strDone (head) and ctx->strLast (tail) which thread 160 * through the potentially smaller number of "IO completed" streams, 161 * used in evGetNext() to avoid scanning the entire list. 162 */ 163 164 /* Unlink from ctx->streams. */ 165 if (old->prev != NULL) 166 old->prev->next = old->next; 167 else 168 ctx->streams = old->next; 169 if (old->next != NULL) 170 old->next->prev = old->prev; 171 172 /* 173 * If 'old' is on the ctx->strDone list, remove it. Update 174 * ctx->strLast if necessary. 175 */ 176 if (old->prevDone == NULL && old->nextDone == NULL) { 177 /* 178 * Either 'old' is the only item on the done list, or it's 179 * not on the done list. If the former, then we unlink it 180 * from the list. If the latter, we leave the list alone. 181 */ 182 if (ctx->strDone == old) { 183 ctx->strDone = NULL; 184 ctx->strLast = NULL; 185 } 186 } else { 187 if (old->prevDone != NULL) 188 old->prevDone->nextDone = old->nextDone; 189 else 190 ctx->strDone = old->nextDone; 191 if (old->nextDone != NULL) 192 old->nextDone->prevDone = old->prevDone; 193 else 194 ctx->strLast = old->prevDone; 195 } 196 197 /* Deallocate the stream. */ 198 if (old->file.opaque) 199 evDeselectFD(opaqueCtx, old->file); 200 memput(old->iovOrig, sizeof (struct iovec) * old->iovOrigCount); 201 FREE(old); 202 return (0); 203 } 204 205 /* Copy a scatter/gather vector and initialize a stream handler's IO. */ 206 static int 207 copyvec(evStream *str, const struct iovec *iov, int iocnt) { 208 int i; 209 210 str->iovOrig = (struct iovec *)memget(sizeof(struct iovec) * iocnt); 211 if (str->iovOrig == NULL) { 212 errno = ENOMEM; 213 return (-1); 214 } 215 str->ioTotal = 0; 216 for (i = 0; i < iocnt; i++) { 217 str->iovOrig[i] = iov[i]; 218 str->ioTotal += iov[i].iov_len; 219 } 220 str->iovOrigCount = iocnt; 221 str->iovCur = str->iovOrig; 222 str->iovCurCount = str->iovOrigCount; 223 str->ioDone = 0; 224 return (0); 225 } 226 227 /* Pull off or truncate lead iovec(s). */ 228 static void 229 consume(evStream *str, size_t bytes) { 230 while (bytes > 0) { 231 if (bytes < (size_t)str->iovCur->iov_len) { 232 str->iovCur->iov_len -= bytes; 233 str->iovCur->iov_base = (void *) 234 ((u_char *)str->iovCur->iov_base + bytes); 235 str->ioDone += bytes; 236 bytes = 0; 237 } else { 238 bytes -= str->iovCur->iov_len; 239 str->ioDone += str->iovCur->iov_len; 240 str->iovCur++; 241 str->iovCurCount--; 242 } 243 } 244 } 245 246 /* Add a stream to Done list and deselect the FD. */ 247 static void 248 done(evContext opaqueCtx, evStream *str) { 249 evContext_p *ctx = opaqueCtx.opaque; 250 251 if (ctx->strLast != NULL) { 252 str->prevDone = ctx->strLast; 253 ctx->strLast->nextDone = str; 254 ctx->strLast = str; 255 } else { 256 INSIST(ctx->strDone == NULL); 257 ctx->strDone = ctx->strLast = str; 258 } 259 evDeselectFD(opaqueCtx, str->file); 260 str->file.opaque = NULL; 261 /* evDrop() will call evCancelRW() on us. */ 262 } 263 264 /* Dribble out some bytes on the stream. (Called by evDispatch().) */ 265 static void 266 writable(evContext opaqueCtx, void *uap, int fd, int evmask) { 267 evStream *str = uap; 268 int bytes; 269 270 UNUSED(evmask); 271 272 bytes = writev(fd, str->iovCur, str->iovCurCount); 273 if (bytes > 0) { 274 if ((str->flags & EV_STR_TIMEROK) != 0) 275 evTouchIdleTimer(opaqueCtx, str->timer); 276 consume(str, bytes); 277 } else { 278 if (bytes < 0 && errno != EINTR) { 279 str->ioDone = -1; 280 str->ioErrno = errno; 281 } 282 } 283 if (str->ioDone == -1 || str->ioDone == str->ioTotal) 284 done(opaqueCtx, str); 285 } 286 287 /* Scoop up some bytes from the stream. (Called by evDispatch().) */ 288 static void 289 readable(evContext opaqueCtx, void *uap, int fd, int evmask) { 290 evStream *str = uap; 291 int bytes; 292 293 UNUSED(evmask); 294 295 bytes = readv(fd, str->iovCur, str->iovCurCount); 296 if (bytes > 0) { 297 if ((str->flags & EV_STR_TIMEROK) != 0) 298 evTouchIdleTimer(opaqueCtx, str->timer); 299 consume(str, bytes); 300 } else { 301 if (bytes == 0) 302 str->ioDone = 0; 303 else { 304 if (errno != EINTR) { 305 str->ioDone = -1; 306 str->ioErrno = errno; 307 } 308 } 309 } 310 if (str->ioDone <= 0 || str->ioDone == str->ioTotal) 311 done(opaqueCtx, str); 312 } 313