ev_streams.c revision 1.6 1 1.6 christos /* $NetBSD: ev_streams.c,v 1.6 2009/04/12 17:07:17 christos Exp $ */
2 1.1 christos
3 1.1 christos /*
4 1.1 christos * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 1.1 christos * Copyright (c) 1996-1999 by Internet Software Consortium
6 1.1 christos *
7 1.1 christos * Permission to use, copy, modify, and distribute this software for any
8 1.1 christos * purpose with or without fee is hereby granted, provided that the above
9 1.1 christos * copyright notice and this permission notice appear in all copies.
10 1.1 christos *
11 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 1.1 christos * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 1.1 christos */
19 1.1 christos
20 1.1 christos /* ev_streams.c - implement asynch stream file IO for the eventlib
21 1.1 christos * vix 04mar96 [initial]
22 1.1 christos */
23 1.1 christos
24 1.2 christos #include <sys/cdefs.h>
25 1.2 christos #if !defined(LINT) && !defined(CODECENTER) && !defined(lint)
26 1.2 christos #ifdef notdef
27 1.5 christos static const char rcsid[] = "Id: ev_streams.c,v 1.5 2005/04/27 04:56:36 sra Exp";
28 1.2 christos #else
29 1.6 christos __RCSID("$NetBSD: ev_streams.c,v 1.6 2009/04/12 17:07:17 christos Exp $");
30 1.2 christos #endif
31 1.1 christos #endif
32 1.1 christos
33 1.1 christos #include "port_before.h"
34 1.1 christos #include "fd_setsize.h"
35 1.1 christos
36 1.1 christos #include <sys/types.h>
37 1.1 christos #include <sys/uio.h>
38 1.1 christos
39 1.1 christos #include <errno.h>
40 1.1 christos
41 1.1 christos #include <isc/eventlib.h>
42 1.1 christos #include <isc/assertions.h>
43 1.1 christos #include "eventlib_p.h"
44 1.1 christos
45 1.1 christos #include "port_after.h"
46 1.1 christos
47 1.2 christos #ifndef _LIBC
48 1.1 christos static int copyvec(evStream *str, const struct iovec *iov, int iocnt);
49 1.1 christos static void consume(evStream *str, size_t bytes);
50 1.1 christos static void done(evContext opaqueCtx, evStream *str);
51 1.1 christos static void writable(evContext opaqueCtx, void *uap, int fd, int evmask);
52 1.1 christos static void readable(evContext opaqueCtx, void *uap, int fd, int evmask);
53 1.2 christos #endif
54 1.1 christos
55 1.1 christos struct iovec
56 1.1 christos evConsIovec(void *buf, size_t cnt) {
57 1.1 christos struct iovec ret;
58 1.1 christos
59 1.1 christos memset(&ret, 0xf5, sizeof ret);
60 1.1 christos ret.iov_base = buf;
61 1.1 christos ret.iov_len = cnt;
62 1.1 christos return (ret);
63 1.1 christos }
64 1.1 christos
65 1.2 christos #ifndef _LIBC
66 1.1 christos int
67 1.1 christos evWrite(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt,
68 1.1 christos evStreamFunc func, void *uap, evStreamID *id)
69 1.1 christos {
70 1.1 christos evContext_p *ctx = opaqueCtx.opaque;
71 1.1 christos evStream *new;
72 1.1 christos int save;
73 1.1 christos
74 1.1 christos OKNEW(new);
75 1.1 christos new->func = func;
76 1.1 christos new->uap = uap;
77 1.1 christos new->fd = fd;
78 1.1 christos new->flags = 0;
79 1.1 christos if (evSelectFD(opaqueCtx, fd, EV_WRITE, writable, new, &new->file) < 0)
80 1.1 christos goto free;
81 1.1 christos if (copyvec(new, iov, iocnt) < 0)
82 1.1 christos goto free;
83 1.1 christos new->prevDone = NULL;
84 1.1 christos new->nextDone = NULL;
85 1.1 christos if (ctx->streams != NULL)
86 1.1 christos ctx->streams->prev = new;
87 1.1 christos new->prev = NULL;
88 1.1 christos new->next = ctx->streams;
89 1.1 christos ctx->streams = new;
90 1.1 christos if (id != NULL)
91 1.1 christos id->opaque = new;
92 1.1 christos return (0);
93 1.1 christos free:
94 1.1 christos save = errno;
95 1.1 christos FREE(new);
96 1.1 christos errno = save;
97 1.1 christos return (-1);
98 1.1 christos }
99 1.1 christos
100 1.1 christos int
101 1.1 christos evRead(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt,
102 1.1 christos evStreamFunc func, void *uap, evStreamID *id)
103 1.1 christos {
104 1.1 christos evContext_p *ctx = opaqueCtx.opaque;
105 1.1 christos evStream *new;
106 1.1 christos int save;
107 1.1 christos
108 1.1 christos OKNEW(new);
109 1.1 christos new->func = func;
110 1.1 christos new->uap = uap;
111 1.1 christos new->fd = fd;
112 1.1 christos new->flags = 0;
113 1.1 christos if (evSelectFD(opaqueCtx, fd, EV_READ, readable, new, &new->file) < 0)
114 1.1 christos goto free;
115 1.1 christos if (copyvec(new, iov, iocnt) < 0)
116 1.1 christos goto free;
117 1.1 christos new->prevDone = NULL;
118 1.1 christos new->nextDone = NULL;
119 1.1 christos if (ctx->streams != NULL)
120 1.1 christos ctx->streams->prev = new;
121 1.1 christos new->prev = NULL;
122 1.1 christos new->next = ctx->streams;
123 1.1 christos ctx->streams = new;
124 1.1 christos if (id)
125 1.1 christos id->opaque = new;
126 1.1 christos return (0);
127 1.1 christos free:
128 1.1 christos save = errno;
129 1.1 christos FREE(new);
130 1.1 christos errno = save;
131 1.1 christos return (-1);
132 1.1 christos }
133 1.1 christos
134 1.1 christos int
135 1.1 christos evTimeRW(evContext opaqueCtx, evStreamID id, evTimerID timer) /*ARGSUSED*/ {
136 1.1 christos evStream *str = id.opaque;
137 1.1 christos
138 1.1 christos UNUSED(opaqueCtx);
139 1.1 christos
140 1.1 christos str->timer = timer;
141 1.1 christos str->flags |= EV_STR_TIMEROK;
142 1.1 christos return (0);
143 1.1 christos }
144 1.1 christos
145 1.1 christos int
146 1.1 christos evUntimeRW(evContext opaqueCtx, evStreamID id) /*ARGSUSED*/ {
147 1.1 christos evStream *str = id.opaque;
148 1.1 christos
149 1.1 christos UNUSED(opaqueCtx);
150 1.1 christos
151 1.1 christos str->flags &= ~EV_STR_TIMEROK;
152 1.1 christos return (0);
153 1.1 christos }
154 1.1 christos
155 1.1 christos int
156 1.1 christos evCancelRW(evContext opaqueCtx, evStreamID id) {
157 1.1 christos evContext_p *ctx = opaqueCtx.opaque;
158 1.1 christos evStream *old = id.opaque;
159 1.1 christos
160 1.1 christos /*
161 1.1 christos * The streams list is doubly threaded. First, there's ctx->streams
162 1.1 christos * that's used by evDestroy() to find and cancel all streams. Second,
163 1.1 christos * there's ctx->strDone (head) and ctx->strLast (tail) which thread
164 1.1 christos * through the potentially smaller number of "IO completed" streams,
165 1.1 christos * used in evGetNext() to avoid scanning the entire list.
166 1.1 christos */
167 1.1 christos
168 1.1 christos /* Unlink from ctx->streams. */
169 1.1 christos if (old->prev != NULL)
170 1.1 christos old->prev->next = old->next;
171 1.1 christos else
172 1.1 christos ctx->streams = old->next;
173 1.1 christos if (old->next != NULL)
174 1.1 christos old->next->prev = old->prev;
175 1.1 christos
176 1.1 christos /*
177 1.1 christos * If 'old' is on the ctx->strDone list, remove it. Update
178 1.1 christos * ctx->strLast if necessary.
179 1.1 christos */
180 1.1 christos if (old->prevDone == NULL && old->nextDone == NULL) {
181 1.1 christos /*
182 1.1 christos * Either 'old' is the only item on the done list, or it's
183 1.1 christos * not on the done list. If the former, then we unlink it
184 1.1 christos * from the list. If the latter, we leave the list alone.
185 1.1 christos */
186 1.1 christos if (ctx->strDone == old) {
187 1.1 christos ctx->strDone = NULL;
188 1.1 christos ctx->strLast = NULL;
189 1.1 christos }
190 1.1 christos } else {
191 1.1 christos if (old->prevDone != NULL)
192 1.1 christos old->prevDone->nextDone = old->nextDone;
193 1.1 christos else
194 1.1 christos ctx->strDone = old->nextDone;
195 1.1 christos if (old->nextDone != NULL)
196 1.1 christos old->nextDone->prevDone = old->prevDone;
197 1.1 christos else
198 1.1 christos ctx->strLast = old->prevDone;
199 1.1 christos }
200 1.1 christos
201 1.1 christos /* Deallocate the stream. */
202 1.1 christos if (old->file.opaque)
203 1.1 christos evDeselectFD(opaqueCtx, old->file);
204 1.1 christos memput(old->iovOrig, sizeof (struct iovec) * old->iovOrigCount);
205 1.1 christos FREE(old);
206 1.1 christos return (0);
207 1.1 christos }
208 1.1 christos
209 1.1 christos /* Copy a scatter/gather vector and initialize a stream handler's IO. */
210 1.1 christos static int
211 1.1 christos copyvec(evStream *str, const struct iovec *iov, int iocnt) {
212 1.1 christos int i;
213 1.1 christos
214 1.1 christos str->iovOrig = (struct iovec *)memget(sizeof(struct iovec) * iocnt);
215 1.1 christos if (str->iovOrig == NULL) {
216 1.1 christos errno = ENOMEM;
217 1.1 christos return (-1);
218 1.1 christos }
219 1.1 christos str->ioTotal = 0;
220 1.1 christos for (i = 0; i < iocnt; i++) {
221 1.1 christos str->iovOrig[i] = iov[i];
222 1.1 christos str->ioTotal += iov[i].iov_len;
223 1.1 christos }
224 1.1 christos str->iovOrigCount = iocnt;
225 1.1 christos str->iovCur = str->iovOrig;
226 1.1 christos str->iovCurCount = str->iovOrigCount;
227 1.1 christos str->ioDone = 0;
228 1.1 christos return (0);
229 1.1 christos }
230 1.1 christos
231 1.1 christos /* Pull off or truncate lead iovec(s). */
232 1.1 christos static void
233 1.1 christos consume(evStream *str, size_t bytes) {
234 1.1 christos while (bytes > 0U) {
235 1.1 christos if (bytes < (size_t)str->iovCur->iov_len) {
236 1.1 christos str->iovCur->iov_len -= bytes;
237 1.1 christos str->iovCur->iov_base = (void *)
238 1.1 christos ((u_char *)str->iovCur->iov_base + bytes);
239 1.1 christos str->ioDone += bytes;
240 1.1 christos bytes = 0;
241 1.1 christos } else {
242 1.1 christos bytes -= str->iovCur->iov_len;
243 1.1 christos str->ioDone += str->iovCur->iov_len;
244 1.1 christos str->iovCur++;
245 1.1 christos str->iovCurCount--;
246 1.1 christos }
247 1.1 christos }
248 1.1 christos }
249 1.1 christos
250 1.1 christos /* Add a stream to Done list and deselect the FD. */
251 1.1 christos static void
252 1.1 christos done(evContext opaqueCtx, evStream *str) {
253 1.1 christos evContext_p *ctx = opaqueCtx.opaque;
254 1.1 christos
255 1.1 christos if (ctx->strLast != NULL) {
256 1.1 christos str->prevDone = ctx->strLast;
257 1.1 christos ctx->strLast->nextDone = str;
258 1.1 christos ctx->strLast = str;
259 1.1 christos } else {
260 1.1 christos INSIST(ctx->strDone == NULL);
261 1.1 christos ctx->strDone = ctx->strLast = str;
262 1.1 christos }
263 1.1 christos evDeselectFD(opaqueCtx, str->file);
264 1.1 christos str->file.opaque = NULL;
265 1.1 christos /* evDrop() will call evCancelRW() on us. */
266 1.1 christos }
267 1.1 christos
268 1.1 christos /* Dribble out some bytes on the stream. (Called by evDispatch().) */
269 1.1 christos static void
270 1.1 christos writable(evContext opaqueCtx, void *uap, int fd, int evmask) {
271 1.1 christos evStream *str = uap;
272 1.1 christos int bytes;
273 1.1 christos
274 1.1 christos UNUSED(evmask);
275 1.1 christos
276 1.1 christos bytes = writev(fd, str->iovCur, str->iovCurCount);
277 1.1 christos if (bytes > 0) {
278 1.1 christos if ((str->flags & EV_STR_TIMEROK) != 0)
279 1.1 christos evTouchIdleTimer(opaqueCtx, str->timer);
280 1.1 christos consume(str, bytes);
281 1.1 christos } else {
282 1.1 christos if (bytes < 0 && errno != EINTR) {
283 1.1 christos str->ioDone = -1;
284 1.1 christos str->ioErrno = errno;
285 1.1 christos }
286 1.1 christos }
287 1.1 christos if (str->ioDone == -1 || str->ioDone == str->ioTotal)
288 1.1 christos done(opaqueCtx, str);
289 1.1 christos }
290 1.1 christos
291 1.1 christos /* Scoop up some bytes from the stream. (Called by evDispatch().) */
292 1.1 christos static void
293 1.1 christos readable(evContext opaqueCtx, void *uap, int fd, int evmask) {
294 1.1 christos evStream *str = uap;
295 1.1 christos int bytes;
296 1.1 christos
297 1.1 christos UNUSED(evmask);
298 1.1 christos
299 1.1 christos bytes = readv(fd, str->iovCur, str->iovCurCount);
300 1.1 christos if (bytes > 0) {
301 1.1 christos if ((str->flags & EV_STR_TIMEROK) != 0)
302 1.1 christos evTouchIdleTimer(opaqueCtx, str->timer);
303 1.1 christos consume(str, bytes);
304 1.1 christos } else {
305 1.1 christos if (bytes == 0)
306 1.1 christos str->ioDone = 0;
307 1.1 christos else {
308 1.1 christos if (errno != EINTR) {
309 1.1 christos str->ioDone = -1;
310 1.1 christos str->ioErrno = errno;
311 1.1 christos }
312 1.1 christos }
313 1.1 christos }
314 1.1 christos if (str->ioDone <= 0 || str->ioDone == str->ioTotal)
315 1.1 christos done(opaqueCtx, str);
316 1.1 christos }
317 1.2 christos #endif
318 1.3 christos
319 1.3 christos /*! \file */
320