Used glibc to provide mt-blocking stdio for fds.
[ashd.git] / lib / mtio.c
CommitLineData
83723896
FT
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>
e1cdf02e
FT
20#include <stdio.h>
21#include <fcntl.h>
83723896
FT
22#include <unistd.h>
23#include <string.h>
24#include <time.h>
25#include <errno.h>
26#include <sys/select.h>
e1cdf02e 27#include <sys/socket.h>
83723896
FT
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <log.h>
33#include <utils.h>
34#include <mt.h>
35#include <mtio.h>
36
37struct blocker {
38 struct blocker *n, *p;
39 int fd;
40 int ev;
41 time_t to;
42 struct muth *th;
43};
44
45static struct blocker *blockers;
46
47int block(int fd, int ev, time_t to)
48{
49 struct blocker *bl;
50 int rv;
51
52 omalloc(bl);
53 bl->fd = fd;
54 bl->ev = ev;
55 if(to > 0)
56 bl->to = time(NULL) + to;
57 bl->th = current;
58 bl->n = blockers;
59 if(blockers)
60 blockers->p = bl;
61 blockers = bl;
62 rv = yield();
63 if(bl->n)
64 bl->n->p = bl->p;
65 if(bl->p)
66 bl->p->n = bl->n;
67 if(bl == blockers)
68 blockers = bl->n;
69 return(rv);
70}
71
72void ioloop(void)
73{
74 int ret;
75 fd_set rfds, wfds, efds;
76 struct blocker *bl, *nbl;
77 struct timeval toval;
78 time_t now, timeout;
79 int maxfd;
80 int ev;
81
82 while(blockers != NULL) {
83 FD_ZERO(&rfds);
84 FD_ZERO(&wfds);
85 FD_ZERO(&efds);
86 maxfd = 0;
87 now = time(NULL);
88 timeout = 0;
89 for(bl = blockers; bl; bl = bl->n) {
90 if(bl->ev & EV_READ)
91 FD_SET(bl->fd, &rfds);
92 if(bl->ev & EV_WRITE)
93 FD_SET(bl->fd, &wfds);
94 FD_SET(bl->fd, &efds);
95 if(bl->fd > maxfd)
96 maxfd = bl->fd;
97 if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
98 timeout = bl->to;
99 }
100 toval.tv_sec = timeout - now;
101 toval.tv_usec = 0;
102 ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
103 if(ret < 0) {
104 if(errno != EINTR) {
105 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
106 /* To avoid CPU hogging in case it's bad, which it
107 * probably is. */
108 sleep(1);
109 }
110 }
111 now = time(NULL);
112 for(bl = blockers; bl; bl = nbl) {
113 nbl = bl->n;
114 ev = 0;
115 if(FD_ISSET(bl->fd, &rfds))
116 ev |= EV_READ;
117 if(FD_ISSET(bl->fd, &wfds))
118 ev |= EV_WRITE;
119 if(FD_ISSET(bl->fd, &efds))
120 ev = -1;
3dc69b45 121 if((ev < 0) || (ev & bl->ev))
83723896
FT
122 resume(bl->th, ev);
123 else if((bl->to != 0) && (bl->to <= now))
124 resume(bl->th, 0);
125 }
126 }
127}
e1cdf02e
FT
128
129struct stdiofd {
130 int fd;
131 int sock;
132 int timeout;
133};
134
135static ssize_t mtread(void *cookie, char *buf, size_t len)
136{
137 struct stdiofd *d = cookie;
138 int ev;
139 ssize_t ret;
140
141 while(1) {
142 ret = read(d->fd, buf, len);
143 if((ret < 0) && (errno == EAGAIN)) {
144 ev = block(d->fd, EV_READ, d->timeout);
145 if(ev < 0) {
146 /* If we just go on, we should get the real error. */
147 continue;
148 } else if(ev == 0) {
149 errno = ETIMEDOUT;
150 return(-1);
151 } else {
152 continue;
153 }
154 } else {
155 return(ret);
156 }
157 }
158}
159
160static ssize_t mtwrite(void *cookie, const char *buf, size_t len)
161{
162 struct stdiofd *d = cookie;
163 int ev;
164 ssize_t ret;
165
166 while(1) {
167 if(d->sock)
168 ret = send(d->fd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL);
169 else
170 ret = write(d->fd, buf, len);
171 if((ret < 0) && (errno == EAGAIN)) {
172 ev = block(d->fd, EV_WRITE, d->timeout);
173 if(ev < 0) {
174 /* If we just go on, we should get the real error. */
175 continue;
176 } else if(ev == 0) {
177 errno = ETIMEDOUT;
178 return(-1);
179 } else {
180 continue;
181 }
182 } else {
183 return(ret);
184 }
185 }
186}
187
188static int mtclose(void *cookie)
189{
190 struct stdiofd *d = cookie;
191
192 close(d->fd);
193 free(d);
194 return(0);
195}
196
197static cookie_io_functions_t iofuns = {
198 .read = mtread,
199 .write = mtwrite,
200 .close = mtclose,
201};
202
203FILE *mtstdopen(int fd, int issock, int timeout, char *mode)
204{
205 struct stdiofd *d;
206 FILE *ret;
207
208 omalloc(d);
209 d->fd = fd;
210 d->sock = issock;
211 d->timeout = timeout;
212 ret = fopencookie(d, mode, iofuns);
213 if(!ret)
214 free(d);
215 else
216 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
217 return(ret);
218}