call[fs]cgi: Handle serialized child connections asynchronously.
[ashd.git] / lib / mtio-select.c
... / ...
CommitLineData
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <stdlib.h>
20#include <string.h>
21#include <time.h>
22#include <unistd.h>
23#include <errno.h>
24#include <sys/select.h>
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29#include <log.h>
30#include <utils.h>
31#include <mt.h>
32#include <mtio.h>
33
34static struct blocker *blockers;
35static int exitstatus;
36
37struct blocker {
38 struct blocker *n, *p;
39 struct iterator *it;
40 int fd;
41 int ev, rev, id;
42 time_t to;
43 struct muth *th;
44};
45
46struct iterator {
47 struct blocker *bl;
48};
49
50static void addblock(struct blocker *bl)
51{
52 bl->n = blockers;
53 if(blockers)
54 blockers->p = bl;
55 blockers = bl;
56}
57
58static void remblock(struct blocker *bl)
59{
60 if(bl->n)
61 bl->n->p = bl->p;
62 if(bl->p)
63 bl->p->n = bl->n;
64 if(bl == blockers)
65 blockers = bl->n;
66 if(bl->it) {
67 if((bl->it->bl = bl->n) != NULL)
68 bl->it->bl->it = bl->it;
69 bl->it = NULL;
70 }
71}
72
73struct selected mblock(time_t to, int n, struct selected *spec)
74{
75 int i, id;
76 struct blocker bls[n];
77
78 to = (to > 0)?(time(NULL) + to):0;
79 for(i = 0; i < n; i++) {
80 bls[i] = (struct blocker){
81 .fd = spec[i].fd,
82 .ev = spec[i].ev,
83 .id = i,
84 .to = to,
85 .th = current,
86 };
87 addblock(&bls[i]);
88 }
89 id = yield();
90 for(i = 0; i < n; i++)
91 remblock(&bls[i]);
92 if(id < 0)
93 return((struct selected){.fd = -1, .ev = -1});
94 return((struct selected){.fd = bls[id].fd, .ev = bls[id].rev});
95}
96
97int block(int fd, int ev, time_t to)
98{
99 struct blocker bl;
100 int rv;
101
102 if(fd >= FD_SETSIZE) {
103 flog(LOG_ERR, "tried to use more file descriptors than select() can handle: fd %i", fd);
104 errno = EMFILE;
105 return(-1);
106 }
107 bl = (struct blocker) {
108 .fd = fd,
109 .ev = ev,
110 .id = -1,
111 .to = (to > 0)?(time(NULL) + to):0,
112 .th = current,
113 };
114 addblock(&bl);
115 rv = yield();
116 remblock(&bl);
117 return(rv);
118}
119
120int ioloop(void)
121{
122 int ret;
123 fd_set rfds, wfds, efds;
124 struct blocker *bl;
125 struct iterator it;
126 struct timeval toval;
127 time_t now, timeout;
128 int maxfd;
129 int ev;
130
131 exitstatus = 0;
132 while(blockers != NULL) {
133 FD_ZERO(&rfds);
134 FD_ZERO(&wfds);
135 FD_ZERO(&efds);
136 maxfd = 0;
137 now = time(NULL);
138 timeout = 0;
139 for(bl = blockers; bl; bl = bl->n) {
140 if(bl->fd >= 0) {
141 if(bl->ev & EV_READ)
142 FD_SET(bl->fd, &rfds);
143 if(bl->ev & EV_WRITE)
144 FD_SET(bl->fd, &wfds);
145 FD_SET(bl->fd, &efds);
146 if(bl->fd > maxfd)
147 maxfd = bl->fd;
148 }
149 if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
150 timeout = bl->to;
151 }
152 if(exitstatus)
153 return(exitstatus);
154 toval.tv_sec = timeout - now;
155 toval.tv_usec = 0;
156 ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
157 if(ret < 0) {
158 if(errno != EINTR) {
159 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
160 /* To avoid CPU hogging in case it's bad, which it
161 * probably is. */
162 sleep(1);
163 }
164 } else {
165 now = time(NULL);
166 for(bl = it.bl = blockers; bl; bl = it.bl) {
167 if((it.bl = bl->n) != NULL)
168 it.bl->it = &it;
169 ev = 0;
170 if(bl->fd >= 0) {
171 if(FD_ISSET(bl->fd, &rfds))
172 ev |= EV_READ;
173 if(FD_ISSET(bl->fd, &wfds))
174 ev |= EV_WRITE;
175 if(FD_ISSET(bl->fd, &efds))
176 ev = -1;
177 }
178 if((ev < 0) || (ev & bl->ev)) {
179 if(bl->id < 0) {
180 resume(bl->th, ev);
181 } else {
182 bl->rev = ev;
183 resume(bl->th, bl->id);
184 }
185 } else if((bl->to != 0) && (bl->to <= now)) {
186 if(bl->id < 0) {
187 resume(bl->th, 0);
188 } else {
189 bl->rev = 0;
190 resume(bl->th, bl->id);
191 }
192 }
193 if(it.bl)
194 it.bl->it = NULL;
195 }
196 }
197 }
198 return(0);
199}
200
201void exitioloop(int status)
202{
203 exitstatus = status;
204}