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