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