]> git.pond.sub.org Git - empserver/blob - src/lib/empthread/io.c
Update known contributors comments
[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     struct iop *iop;
77
78     flags = flags & (IO_READ | IO_WRITE | IO_NBLOCK | IO_NEWSOCK);
79     if ((flags & (IO_READ | IO_WRITE)) == 0)
80         return NULL;
81     iop = malloc(sizeof(struct iop));
82     if (!iop)
83         return NULL;
84     iop->fd = fd;
85     iop->input = 0;
86     iop->output = 0;
87     iop->flags = 0;
88     iop->input_timeout = timeout;
89     iop->bufsize = bufsize;
90     if ((flags & IO_READ) && (flags & IO_NEWSOCK) == 0)
91         iop->input = ioq_create(bufsize);
92     if ((flags & IO_WRITE) && (flags & IO_NEWSOCK) == 0)
93         iop->output = ioq_create(bufsize);
94     if (flags & IO_NBLOCK)
95         io_noblocking(iop, 1);  /* FIXME check success */
96     iop->flags = flags;
97     return iop;
98 }
99
100 void
101 io_close(struct iop *iop)
102 {
103
104     if (iop->input != 0)
105         ioq_destroy(iop->input);
106     if (iop->output != 0)
107         ioq_destroy(iop->output);
108     (void)close(iop->fd);
109     free(iop);
110 }
111
112 /*
113  * Return number of bytes read on success, zero on timeout or EOF, -1
114  * on error, with errno set appropriately.  In particular, return -1
115  * with errno set to EAGAIN or EWOULDBLOCK when no data is available
116  * for non-blocking input (WAITFORINPUT false).  Use io_eof() to
117  * distinguish timeout from EOF.
118  */
119 int
120 io_input(struct iop *iop, int waitforinput)
121 {
122     char buf[IO_BUFSIZE];
123     int cc;
124     int res;
125
126     /* Not a read IOP */
127     if ((iop->flags & IO_READ) == 0) {
128         errno = EBADF;
129         return -1;
130     }
131     /* IOP is markes as in error. */
132     if (iop->flags & IO_ERROR) {
133         errno = EBADF;
134         return -1;
135     }
136     /* Wait for the file to have input. */
137     if (waitforinput) {
138         res = empth_select(iop->fd, EMPTH_FD_READ, &iop->input_timeout);
139         if (res < 0) {
140             iop->flags |= IO_ERROR;
141             return -1;
142         } else if (res == 0)
143             return 0;
144     }
145
146     /* Do the actual read. */
147     cc = read(iop->fd, buf, sizeof(buf));
148     if (cc < 0) {
149         if (errno != EAGAIN && errno != EWOULDBLOCK)
150             /* Some form of file error occurred... */
151             iop->flags |= IO_ERROR;
152         return -1;
153     }
154
155     /* We eof'd */
156     if (cc == 0) {
157         iop->flags |= IO_EOF;
158         return 0;
159     }
160
161     /* Append the input to the IOQ. */
162     ioq_append(iop->input, buf, cc);
163     return cc;
164 }
165
166 int
167 io_inputwaiting(struct iop *iop)
168 {
169     return ioq_qsize(iop->input);
170 }
171
172 int
173 io_outputwaiting(struct iop *iop)
174 {
175     return ioq_qsize(iop->output);
176 }
177
178 int
179 io_output(struct iop *iop, int waitforoutput)
180 {
181     struct iovec iov[16];
182     int cc;
183     int n;
184     int remain;
185
186     /* If there is no output waiting. */
187     if (!io_outputwaiting(iop))
188         return 0;
189
190     /* If the iop is not write enabled. */
191     if ((iop->flags & IO_WRITE) == 0)
192         return -1;
193
194     /* If the io is marked as in error... */
195     if (iop->flags & IO_ERROR)
196         return -1;
197
198     /* make the iov point to the data in the queue. */
199     /* I.E., each of the elements in the queue. */
200     /* returns the number of elements in the iov. */
201     n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
202
203     if (n <= 0) {
204         return 0;
205     }
206
207     /* wait for the file to be output ready. */
208     if (waitforoutput != IO_NOWAIT) {
209         /* This waits for the file to be ready for writing, */
210         /* and lets other threads run. */
211         empth_select(iop->fd, EMPTH_FD_WRITE, NULL);
212     }
213
214     /* Do the actual write. */
215     cc = writev(iop->fd, iov, n);
216
217     /* if it failed.... */
218     if (cc < 0) {
219         /* Hmm, it would block.  file is opened noblock, soooooo.. */
220         if (errno == EAGAIN || errno == EWOULDBLOCK) {
221             /* If there are remaining bytes, set the IO as remaining.. */
222             remain = ioq_qsize(iop->output);
223             return remain;
224         }
225         iop->flags |= IO_ERROR;
226         return -1;
227     }
228
229     /* If no bytes were written, something happened..  Like an EOF. */
230     if (cc == 0) {
231         iop->flags |= IO_EOF;
232         return 0;
233     }
234
235     /* Remove the number of written bytes from the queue. */
236     ioq_dequeue(iop->output, cc);
237
238     return cc;
239 }
240
241 int
242 io_peek(struct iop *iop, char *buf, int nbytes)
243 {
244     if ((iop->flags & IO_READ) == 0)
245         return -1;
246     return ioq_peek(iop->input, buf, nbytes);
247 }
248
249 int
250 io_read(struct iop *iop, char *buf, int nbytes)
251 {
252     int cc;
253
254     if ((iop->flags & IO_READ) == 0)
255         return -1;
256     cc = ioq_peek(iop->input, buf, nbytes);
257     if (cc > 0)
258         ioq_dequeue(iop->input, cc);
259     return cc;
260 }
261
262 int
263 io_write(struct iop *iop, char *buf, int nbytes, int doWait)
264 {
265     int len;
266
267     if ((iop->flags & IO_WRITE) == 0)
268         return -1;
269     ioq_append(iop->output, buf, nbytes);
270     len = ioq_qsize(iop->output);
271     if (len > iop->bufsize) {
272         if (doWait) {
273             io_output_all(iop);
274         } else {
275             /* only try a write every BUFSIZE characters */
276             if (((len - nbytes) % iop->bufsize) < (len % iop->bufsize))
277                 io_output(iop, IO_NOWAIT);
278         }
279     }
280     return nbytes;
281 }
282
283 int
284 io_output_all(struct iop *iop)
285 {
286     int n;
287
288     /*
289      * Mustn't block a player thread while update is pending, or else
290      * a malicous player could delay the update indefinitely
291      */
292     while ((n = io_output(iop, IO_NOWAIT)) > 0 && !play_wrlock_wanted)
293         empth_select(iop->fd, EMPTH_FD_WRITE, NULL);
294
295     return n;
296 }
297
298 int
299 io_gets(struct iop *iop, char *buf, int nbytes)
300 {
301     if ((iop->flags & IO_READ) == 0)
302         return -1;
303     return ioq_gets(iop->input, buf, nbytes);
304 }
305
306 int
307 io_puts(struct iop *iop, char *buf)
308 {
309     if ((iop->flags & IO_WRITE) == 0)
310         return -1;
311     return ioq_puts(iop->output, buf);
312 }
313
314 int
315 io_shutdown(struct iop *iop, int flags)
316 {
317     flags &= (IO_READ | IO_WRITE);
318     if ((iop->flags & flags) != flags)
319         return -1;
320     if (flags & IO_READ) {
321         shutdown(iop->fd, 0);
322         ioq_drain(iop->input);
323     }
324     if (flags & IO_WRITE) {
325         shutdown(iop->fd, 1);
326         ioq_drain(iop->output);
327     }
328     return 0;
329 }
330
331 int
332 io_noblocking(struct iop *iop, int value)
333 {
334     int flags;
335
336     flags = fcntl(iop->fd, F_GETFL, 0);
337     if (flags < 0)
338         return -1;
339     if (value == 0)
340         flags &= ~O_NONBLOCK;
341     else
342         flags |= O_NONBLOCK;
343     if (fcntl(iop->fd, F_SETFL, flags) < 0)
344         return -1;
345     if (value == 0)
346         iop->flags &= ~IO_NBLOCK;
347     else
348         iop->flags |= IO_NBLOCK;
349     return 0;
350 }
351
352 int
353 io_error(struct iop *iop)
354 {
355     return iop->flags & IO_ERROR;
356 }
357
358 int
359 io_eof(struct iop *iop)
360 {
361     return iop->flags & IO_EOF;
362 }
363
364 int
365 io_fileno(struct iop *iop)
366 {
367     return iop->fd;
368 }