2 * sel.c: implementation of sel.h.
\r
12 #include <sys/time.h>
\r
13 #include <sys/types.h>
\r
14 #include <sys/select.h>
\r
19 /* ----------------------------------------------------------------------
\r
20 * Chunk of code lifted from PuTTY's misc.c to manage buffers of
\r
21 * data to be written to an fd.
\r
24 #define BUFFER_GRANULE 512
\r
26 typedef struct bufchain_tag {
\r
27 struct bufchain_granule *head, *tail;
\r
28 size_t buffersize; /* current amount of buffered data */
\r
30 struct bufchain_granule {
\r
31 struct bufchain_granule *next;
\r
32 size_t buflen, bufpos;
\r
33 char buf[BUFFER_GRANULE];
\r
36 static void bufchain_init(bufchain *ch)
\r
38 ch->head = ch->tail = NULL;
\r
42 static void bufchain_clear(bufchain *ch)
\r
44 struct bufchain_granule *b;
\r
47 ch->head = ch->head->next;
\r
54 static size_t bufchain_size(bufchain *ch)
\r
56 return ch->buffersize;
\r
59 static void bufchain_add(bufchain *ch, const void *data, size_t len)
\r
61 const char *buf = (const char *)data;
\r
63 if (len == 0) return;
\r
65 ch->buffersize += len;
\r
67 if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {
\r
68 size_t copylen = BUFFER_GRANULE - ch->tail->buflen;
\r
71 memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);
\r
74 ch->tail->buflen += copylen;
\r
77 struct bufchain_granule *newbuf;
\r
78 size_t grainlen = BUFFER_GRANULE;
\r
81 newbuf = snew(struct bufchain_granule);
\r
83 newbuf->buflen = grainlen;
\r
84 memcpy(newbuf->buf, buf, grainlen);
\r
88 ch->tail->next = newbuf;
\r
90 ch->head = ch->tail = newbuf;
\r
91 newbuf->next = NULL;
\r
96 static void bufchain_consume(bufchain *ch, size_t len)
\r
98 struct bufchain_granule *tmp;
\r
100 assert(ch->buffersize >= len);
\r
102 size_t remlen = len;
\r
103 assert(ch->head != NULL);
\r
104 if (remlen >= ch->head->buflen - ch->head->bufpos) {
\r
105 remlen = ch->head->buflen - ch->head->bufpos;
\r
107 ch->head = tmp->next;
\r
112 ch->head->bufpos += remlen;
\r
113 ch->buffersize -= remlen;
\r
118 static void bufchain_prefix(bufchain *ch, void **data, size_t *len)
\r
120 *len = ch->head->buflen - ch->head->bufpos;
\r
121 *data = ch->head->buf + ch->head->bufpos;
\r
124 /* ----------------------------------------------------------------------
\r
125 * The actual implementation of the sel interface.
\r
130 sel_rfd *rhead, *rtail;
\r
131 sel_wfd *whead, *wtail;
\r
136 sel_rfd *prev, *next;
\r
137 sel_readdata_fn_t readdata;
\r
138 sel_readerr_fn_t readerr;
\r
146 sel_wfd *prev, *next;
\r
147 sel_written_fn_t written;
\r
148 sel_writeerr_fn_t writeerr;
\r
154 sel *sel_new(void *ctx)
\r
156 sel *sel = snew(struct sel);
\r
159 sel->rhead = sel->rtail = NULL;
\r
160 sel->whead = sel->wtail = NULL;
\r
165 sel_wfd *sel_wfd_add(sel *sel, int fd,
\r
166 sel_written_fn_t written, sel_writeerr_fn_t writeerr,
\r
169 sel_wfd *wfd = snew(sel_wfd);
\r
171 wfd->written = written;
\r
172 wfd->writeerr = writeerr;
\r
175 bufchain_init(&wfd->buf);
\r
178 wfd->prev = sel->wtail;
\r
180 sel->wtail->next = wfd;
\r
189 sel_rfd *sel_rfd_add(sel *sel, int fd,
\r
190 sel_readdata_fn_t readdata, sel_readerr_fn_t readerr,
\r
193 sel_rfd *rfd = snew(sel_rfd);
\r
195 rfd->readdata = readdata;
\r
196 rfd->readerr = readerr;
\r
202 rfd->prev = sel->rtail;
\r
204 sel->rtail->next = rfd;
\r
213 size_t sel_write(sel_wfd *wfd, const void *data, size_t len)
\r
215 bufchain_add(&wfd->buf, data, len);
\r
216 return bufchain_size(&wfd->buf);
\r
219 void sel_wfd_setfd(sel_wfd *wfd, int fd)
\r
224 void sel_rfd_setfd(sel_rfd *rfd, int fd)
\r
229 void sel_rfd_freeze(sel_rfd *rfd)
\r
234 void sel_rfd_unfreeze(sel_rfd *rfd)
\r
239 int sel_wfd_delete(sel_wfd *wfd)
\r
241 sel *sel = wfd->parent;
\r
245 wfd->prev->next = wfd->next;
\r
247 sel->whead = wfd->next;
\r
249 wfd->next->prev = wfd->prev;
\r
251 sel->wtail = wfd->prev;
\r
253 bufchain_clear(&wfd->buf);
\r
260 int sel_rfd_delete(sel_rfd *rfd)
\r
262 sel *sel = rfd->parent;
\r
266 rfd->prev->next = rfd->next;
\r
268 sel->rhead = rfd->next;
\r
270 rfd->next->prev = rfd->prev;
\r
272 sel->rtail = rfd->prev;
\r
279 void sel_free(sel *sel)
\r
282 sel_wfd_delete(sel->whead);
\r
284 sel_rfd_delete(sel->rhead);
\r
288 void *sel_get_ctx(sel *sel) { return sel->ctx; }
\r
289 void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; }
\r
290 void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; }
\r
291 void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; }
\r
292 void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; }
\r
293 void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; }
\r
295 int sel_iterate(sel *sel, long timeout)
\r
301 struct timeval tv, *ptv;
\r
308 for (rfd = sel->rhead; rfd; rfd = rfd->next) {
\r
309 if (rfd->fd >= 0 && !rfd->frozen) {
\r
310 FD_SET(rfd->fd, &rset);
\r
311 if (maxfd < rfd->fd + 1)
\r
312 maxfd = rfd->fd + 1;
\r
316 for (wfd = sel->whead; wfd; wfd = wfd->next) {
\r
317 if (wfd->fd >= 0 && bufchain_size(&wfd->buf)) {
\r
318 FD_SET(wfd->fd, &wset);
\r
319 if (maxfd < wfd->fd + 1)
\r
320 maxfd = wfd->fd + 1;
\r
328 tv.tv_sec = timeout / 1000;
\r
329 tv.tv_usec = 1000 * (timeout % 1000);
\r
333 ret = select(maxfd, &rset, &wset, NULL, ptv);
\r
334 } while (ret < 0 && (errno == EINTR || errno == EAGAIN));
\r
340 * Just in case one of the callbacks destroys an rfd or wfd we
\r
341 * had yet to get round to, we must loop from the start every
\r
342 * single time. Algorithmically irritating, but necessary
\r
343 * unless we want to store the rfd structures in a heavyweight
\r
344 * tree sorted by fd. And let's face it, if we cared about
\r
345 * good algorithmic complexity it's not at all clear we'd be
\r
346 * using select in the first place.
\r
349 for (wfd = sel->whead; wfd; wfd = wfd->next)
\r
350 if (wfd->fd >= 0 && FD_ISSET(wfd->fd, &wset)) {
\r
354 FD_CLR(wfd->fd, &wset);
\r
355 bufchain_prefix(&wfd->buf, &data, &len);
\r
356 ret = write(wfd->fd, data, len);
\r
360 wfd->writeerr(wfd, errno);
\r
362 bufchain_consume(&wfd->buf, len);
\r
364 wfd->written(wfd, bufchain_size(&wfd->buf));
\r
370 for (rfd = sel->rhead; rfd; rfd = rfd->next)
\r
371 if (rfd->fd >= 0 && !rfd->frozen && FD_ISSET(rfd->fd, &rset)) {
\r
372 FD_CLR(rfd->fd, &rset);
\r
373 ret = read(rfd->fd, buf, sizeof(buf));
\r
376 rfd->readerr(rfd, errno);
\r
379 rfd->readdata(rfd, buf, ret);
\r