]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/io.c
Make io_output() return a sane value
[empserver] / src / lib / empthread / io.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  io.c: Arrange for input and output on a file descriptor to be queued.
29  *
30  *  Known contributors to this file:
31  *      Doug Hay, 1998
32  *      Steve McClure, 1998
33  *      Ron Koenderink, 2009
34  */
35
36 /*
37  * Arrange for input and output on a file descriptor
38  * to be queued.  Provide main loop -- a mechanism for
39  * blocking across all registered file descriptors, and
40  * reading or writing when appropriate.
41  */
42
43 #include <config.h>
44
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdlib.h>
48 #include <sys/socket.h>
49 #include <sys/types.h>
50 #include <sys/uio.h>
51 #include <unistd.h>
52 #include "empio.h"
53 #include "empthread.h"
54 #include "ioqueue.h"
55 #include "misc.h"
56 #include "queue.h"
57 #include "server.h"
58
59 struct iop {
60     int fd;
61     struct ioqueue *input;
62     struct ioqueue *output;
63     int flags;
64     int bufsize;
65     struct timeval input_timeout;
66 };
67
68 void
69 io_init(void)
70 {
71 }
72
73 struct iop *
74 io_open(int fd, int flags, int bufsize, struct timeval timeout)
75 {
76     int fdfl;
77     struct iop *iop;
78
79     flags = flags & (IO_READ | IO_WRITE);
80     if ((flags & (IO_READ | IO_WRITE)) == 0)
81         return NULL;
82
83     fdfl = fcntl(fd, F_GETFL, 0);
84     if (fdfl < 0)
85         return NULL;
86     fdfl |= O_NONBLOCK;
87     if (fcntl(fd, F_SETFL, fdfl) < 0)
88         return NULL;
89
90     iop = malloc(sizeof(struct iop));
91     if (!iop)
92         return NULL;
93     iop->fd = fd;
94     iop->input = NULL;
95     iop->output = NULL;
96     iop->flags = flags;
97     iop->bufsize = bufsize;
98     iop->input_timeout = timeout;
99     if (flags & IO_READ)
100         iop->input = ioq_create(bufsize);
101     if (flags & IO_WRITE)
102         iop->output = ioq_create(bufsize);
103     return iop;
104 }
105
106 void
107 io_close(struct iop *iop)
108 {
109
110     if (iop->input)
111         ioq_destroy(iop->input);
112     if (iop->output)
113         ioq_destroy(iop->output);
114     (void)close(iop->fd);
115     free(iop);
116 }
117
118 /*
119  * Return number of bytes read on success, zero on timeout, early
120  * wakeup or EOF, -1 on error, with errno set appropriately.  In
121  * particular, return -1 with errno set to EAGAIN or EWOULDBLOCK when
122  * no data is available for non-blocking input (WAITFORINPUT false).
123  * Use io_eof() to distinguish timeout and early wakeup from EOF.
124  */
125 int
126 io_input(struct iop *iop, int waitforinput)
127 {
128     char buf[IO_BUFSIZE];
129     int cc;
130     int res;
131
132     /* Not a read IOP */
133     if ((iop->flags & IO_READ) == 0) {
134         errno = EBADF;
135         return -1;
136     }
137     /* IOP is markes as in error. */
138     if (iop->flags & IO_ERROR) {
139         errno = EBADF;
140         return -1;
141     }
142     /* Wait for the file to have input. */
143     if (waitforinput) {
144         res = empth_select(iop->fd, EMPTH_FD_READ, &iop->input_timeout);
145         if (res < 0) {
146             iop->flags |= IO_ERROR;
147             return -1;
148         } else if (res == 0)
149             return 0;
150     }
151
152     /* Do the actual read. */
153     cc = read(iop->fd, buf, sizeof(buf));
154     if (cc < 0) {
155         if (errno != EAGAIN && errno != EWOULDBLOCK)
156             /* Some form of file error occurred... */
157             iop->flags |= IO_ERROR;
158         return -1;
159     }
160
161     /* We eof'd */
162     if (cc == 0) {
163         iop->flags |= IO_EOF;
164         return 0;
165     }
166
167     /* Append the input to the IOQ. */
168     ioq_append(iop->input, buf, cc);
169     return cc;
170 }
171
172 int
173 io_inputwaiting(struct iop *iop)
174 {
175     return ioq_qsize(iop->input);
176 }
177
178 int
179 io_outputwaiting(struct iop *iop)
180 {
181     return ioq_qsize(iop->output);
182 }
183
184 /*
185  * Write output queued in IOP.
186  * If WAITFOROUTPUT != IO_NOWAIT, writing may put the thread to sleep.
187  * Return number of bytes written on success, -1 on error.
188  * In particular, return zero when nothing was written because the
189  * queue was empty, or because the write slept and got woken up (only
190  * if WAITFOROUTPUT != IO_NOWAIT), or because the write refused to
191  * sleep (only if WAITFOROUTPUT == IO_NOWAIT).
192  */
193 int
194 io_output(struct iop *iop, int waitforoutput)
195 {
196     struct iovec iov[16];
197     int n, res, cc;
198
199     if (!ioq_qsize(iop->output))
200         return 0;
201
202     if ((iop->flags & IO_WRITE) == 0)
203         return -1;
204
205     if (iop->flags & IO_ERROR)
206         return -1;
207
208     n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
209
210     if (waitforoutput != IO_NOWAIT) {
211         res = empth_select(iop->fd, EMPTH_FD_WRITE, NULL);
212         if (res == 0)
213             return 0;
214         if (res < 0) {
215             iop->flags |= IO_ERROR;
216             return -1;
217         }
218     }
219
220     cc = writev(iop->fd, iov, n);
221     if (cc < 0) {
222         if (errno == EAGAIN || errno == EWOULDBLOCK)
223             return 0;
224         iop->flags |= IO_ERROR;
225         return -1;
226     }
227
228     ioq_dequeue(iop->output, cc);
229     return cc;
230 }
231
232 int
233 io_peek(struct iop *iop, char *buf, int nbytes)
234 {
235     if ((iop->flags & IO_READ) == 0)
236         return -1;
237     return ioq_peek(iop->input, buf, nbytes);
238 }
239
240 int
241 io_read(struct iop *iop, char *buf, int nbytes)
242 {
243     int cc;
244
245     if ((iop->flags & IO_READ) == 0)
246         return -1;
247     cc = ioq_peek(iop->input, buf, nbytes);
248     if (cc > 0)
249         ioq_dequeue(iop->input, cc);
250     return cc;
251 }
252
253 int
254 io_write(struct iop *iop, char *buf, int nbytes, int doWait)
255 {
256     int len;
257
258     if ((iop->flags & IO_WRITE) == 0)
259         return -1;
260     ioq_append(iop->output, buf, nbytes);
261     len = ioq_qsize(iop->output);
262     if (len > iop->bufsize) {
263         if (doWait) {
264             io_output_all(iop);
265         } else {
266             /* only try a write every BUFSIZE characters */
267             if (((len - nbytes) % iop->bufsize) < (len % iop->bufsize))
268                 io_output(iop, IO_NOWAIT);
269         }
270     }
271     return nbytes;
272 }
273
274 int
275 io_output_all(struct iop *iop)
276 {
277     int n;
278
279     /*
280      * Mustn't block a player thread while update is pending, or else
281      * a malicous player could delay the update indefinitely
282      */
283     while ((n = io_output(iop, IO_NOWAIT)) > 0 && !play_wrlock_wanted)
284         empth_select(iop->fd, EMPTH_FD_WRITE, NULL);
285
286     return n;
287 }
288
289 int
290 io_gets(struct iop *iop, char *buf, int nbytes)
291 {
292     if ((iop->flags & IO_READ) == 0)
293         return -1;
294     return ioq_gets(iop->input, buf, nbytes);
295 }
296
297 int
298 io_puts(struct iop *iop, char *buf)
299 {
300     if ((iop->flags & IO_WRITE) == 0)
301         return -1;
302     return ioq_puts(iop->output, buf);
303 }
304
305 int
306 io_shutdown(struct iop *iop, int flags)
307 {
308     flags &= (IO_READ | IO_WRITE);
309     if ((iop->flags & flags) != flags)
310         return -1;
311     if (flags & IO_READ) {
312         shutdown(iop->fd, 0);
313         ioq_drain(iop->input);
314     }
315     if (flags & IO_WRITE) {
316         shutdown(iop->fd, 1);
317         ioq_drain(iop->output);
318     }
319     return 0;
320 }
321
322 int
323 io_error(struct iop *iop)
324 {
325     return iop->flags & IO_ERROR;
326 }
327
328 int
329 io_eof(struct iop *iop)
330 {
331     return iop->flags & IO_EOF;
332 }
333
334 int
335 io_fileno(struct iop *iop)
336 {
337     return iop->fd;
338 }