1 /* $NetBSD: ev_streams.c,v 1.2 2004/05/20 19:52:31 christos Exp $ */
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1996-1999 by Internet Software Consortium
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 /* ev_streams.c - implement asynch stream file IO for the eventlib
21 * vix 04mar96 [initial]
25 * This version of this file is derived from Android 2.3 "Gingerbread",
26 * which contains uncredited changes by Android/Google developers. It has
27 * been modified in 2011 for use in the Android build of Mozilla Firefox by
28 * Mozilla contributors (including Michael Edwards <m.k.edwards@gmail.com>,
29 * and Steve Workman <sjhworkman@gmail.com>).
30 * These changes are offered under the same license as the original NetBSD
31 * file, whose copyright and license are unchanged above.
34 #define ANDROID_CHANGES 1
35 #define MOZILLA_NECKO_EXCLUDE_CODE 1
37 #include <sys/cdefs.h>
38 #if !defined(LINT) && !defined(CODECENTER) && !defined(lint)
40 static const char rcsid
[] = "Id: ev_streams.c,v 1.2.206.2 2004/03/17 00:29:51 marka Exp";
42 __RCSID("$NetBSD: ev_streams.c,v 1.2 2004/05/20 19:52:31 christos Exp $");
46 #include <sys/types.h>
52 #include "eventlib_p.h"
54 #ifndef MOZILLA_NECKO_EXCLUDE_CODE
56 static int copyvec(evStream
*str
, const struct iovec
*iov
, int iocnt
);
57 static void consume(evStream
*str
, size_t bytes
);
58 static void done(evContext opaqueCtx
, evStream
*str
);
59 static void writable(evContext opaqueCtx
, void *uap
, int fd
, int evmask
);
60 static void readable(evContext opaqueCtx
, void *uap
, int fd
, int evmask
);
65 evConsIovec(void *buf
, size_t cnt
) {
68 memset(&ret
, 0xf5, sizeof ret
);
74 #ifndef MOZILLA_NECKO_EXCLUDE_CODE
77 evWrite(evContext opaqueCtx
, int fd
, const struct iovec
*iov
, int iocnt
,
78 evStreamFunc func
, void *uap
, evStreamID
*id
)
80 evContext_p
*ctx
= opaqueCtx
.opaque
;
89 if (evSelectFD(opaqueCtx
, fd
, EV_WRITE
, writable
, new, &new->file
) < 0)
91 if (copyvec(new, iov
, iocnt
) < 0)
95 if (ctx
->streams
!= NULL
)
96 ctx
->streams
->prev
= new;
98 new->next
= ctx
->streams
;
111 evRead(evContext opaqueCtx
, int fd
, const struct iovec
*iov
, int iocnt
,
112 evStreamFunc func
, void *uap
, evStreamID
*id
)
114 evContext_p
*ctx
= opaqueCtx
.opaque
;
123 if (evSelectFD(opaqueCtx
, fd
, EV_READ
, readable
, new, &new->file
) < 0)
125 if (copyvec(new, iov
, iocnt
) < 0)
127 new->prevDone
= NULL
;
128 new->nextDone
= NULL
;
129 if (ctx
->streams
!= NULL
)
130 ctx
->streams
->prev
= new;
132 new->next
= ctx
->streams
;
145 evTimeRW(evContext opaqueCtx
, evStreamID id
, evTimerID timer
) /*ARGSUSED*/ {
146 evStream
*str
= id
.opaque
;
151 str
->flags
|= EV_STR_TIMEROK
;
156 evUntimeRW(evContext opaqueCtx
, evStreamID id
) /*ARGSUSED*/ {
157 evStream
*str
= id
.opaque
;
161 str
->flags
&= ~EV_STR_TIMEROK
;
166 evCancelRW(evContext opaqueCtx
, evStreamID id
) {
167 evContext_p
*ctx
= opaqueCtx
.opaque
;
168 evStream
*old
= id
.opaque
;
171 * The streams list is doubly threaded. First, there's ctx->streams
172 * that's used by evDestroy() to find and cancel all streams. Second,
173 * there's ctx->strDone (head) and ctx->strLast (tail) which thread
174 * through the potentially smaller number of "IO completed" streams,
175 * used in evGetNext() to avoid scanning the entire list.
178 /* Unlink from ctx->streams. */
179 if (old
->prev
!= NULL
)
180 old
->prev
->next
= old
->next
;
182 ctx
->streams
= old
->next
;
183 if (old
->next
!= NULL
)
184 old
->next
->prev
= old
->prev
;
187 * If 'old' is on the ctx->strDone list, remove it. Update
188 * ctx->strLast if necessary.
190 if (old
->prevDone
== NULL
&& old
->nextDone
== NULL
) {
192 * Either 'old' is the only item on the done list, or it's
193 * not on the done list. If the former, then we unlink it
194 * from the list. If the latter, we leave the list alone.
196 if (ctx
->strDone
== old
) {
201 if (old
->prevDone
!= NULL
)
202 old
->prevDone
->nextDone
= old
->nextDone
;
204 ctx
->strDone
= old
->nextDone
;
205 if (old
->nextDone
!= NULL
)
206 old
->nextDone
->prevDone
= old
->prevDone
;
208 ctx
->strLast
= old
->prevDone
;
211 /* Deallocate the stream. */
212 if (old
->file
.opaque
)
213 evDeselectFD(opaqueCtx
, old
->file
);
214 memput(old
->iovOrig
, sizeof (struct iovec
) * old
->iovOrigCount
);
219 /* Copy a scatter/gather vector and initialize a stream handler's IO. */
221 copyvec(evStream
*str
, const struct iovec
*iov
, int iocnt
) {
224 str
->iovOrig
= (struct iovec
*)memget(sizeof(struct iovec
) * iocnt
);
225 if (str
->iovOrig
== NULL
) {
230 for (i
= 0; i
< iocnt
; i
++) {
231 str
->iovOrig
[i
] = iov
[i
];
232 str
->ioTotal
+= iov
[i
].iov_len
;
234 str
->iovOrigCount
= iocnt
;
235 str
->iovCur
= str
->iovOrig
;
236 str
->iovCurCount
= str
->iovOrigCount
;
241 /* Pull off or truncate lead iovec(s). */
243 consume(evStream
*str
, size_t bytes
) {
245 if (bytes
< (size_t)str
->iovCur
->iov_len
) {
246 str
->iovCur
->iov_len
-= bytes
;
247 str
->iovCur
->iov_base
= (void *)
248 ((u_char
*)str
->iovCur
->iov_base
+ bytes
);
249 str
->ioDone
+= bytes
;
252 bytes
-= str
->iovCur
->iov_len
;
253 str
->ioDone
+= str
->iovCur
->iov_len
;
260 /* Add a stream to Done list and deselect the FD. */
262 done(evContext opaqueCtx
, evStream
*str
) {
263 evContext_p
*ctx
= opaqueCtx
.opaque
;
265 if (ctx
->strLast
!= NULL
) {
266 str
->prevDone
= ctx
->strLast
;
267 ctx
->strLast
->nextDone
= str
;
270 INSIST(ctx
->strDone
== NULL
);
271 ctx
->strDone
= ctx
->strLast
= str
;
273 evDeselectFD(opaqueCtx
, str
->file
);
274 str
->file
.opaque
= NULL
;
275 /* evDrop() will call evCancelRW() on us. */
278 /* Dribble out some bytes on the stream. (Called by evDispatch().) */
280 writable(evContext opaqueCtx
, void *uap
, int fd
, int evmask
) {
286 bytes
= writev(fd
, str
->iovCur
, str
->iovCurCount
);
288 if ((str
->flags
& EV_STR_TIMEROK
) != 0)
289 evTouchIdleTimer(opaqueCtx
, str
->timer
);
292 if (bytes
< 0 && errno
!= EINTR
) {
294 str
->ioErrno
= errno
;
297 if (str
->ioDone
== -1 || str
->ioDone
== str
->ioTotal
)
298 done(opaqueCtx
, str
);
301 /* Scoop up some bytes from the stream. (Called by evDispatch().) */
303 readable(evContext opaqueCtx
, void *uap
, int fd
, int evmask
) {
309 bytes
= readv(fd
, str
->iovCur
, str
->iovCurCount
);
311 if ((str
->flags
& EV_STR_TIMEROK
) != 0)
312 evTouchIdleTimer(opaqueCtx
, str
->timer
);
318 if (errno
!= EINTR
) {
320 str
->ioErrno
= errno
;
324 if (str
->ioDone
<= 0 || str
->ioDone
== str
->ioTotal
)
325 done(opaqueCtx
, str
);