]> git.pond.sub.org Git - empserver/blob - src/lib/gen/io.c
Garbage collection to side-step sparse file descriptor problem.
[empserver] / src / lib / gen / io.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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 <errno.h>
43 #include <sys/types.h>
44 #if !defined(_WIN32)
45 #include <sys/uio.h>
46 #include <sys/file.h>
47 #include <unistd.h>             /* close read shutdown select */
48 #include <sys/socket.h>
49 #endif
50 #include <time.h>
51 #include <fcntl.h>
52 #include <stdlib.h>             /* malloc calloc free */
53
54 #if defined(_WIN32)
55 #include <winsock.h>
56 #endif
57
58 #include "misc.h"
59 #include "queue.h"
60 #include "ioqueue.h"
61 #include "io_mask.h"
62 #include "empio.h"
63 #include "gen.h"                /* getfdtablesize */
64
65 #include "empthread.h"
66
67 extern struct player *player;   /* XXX */
68
69 static struct io_mask *iom;
70 static int fdmax;               /* largest file descriptor seen */
71 static fd_set newoutput;
72
73 struct iop {
74     int fd;
75     struct ioqueue *input;
76     struct ioqueue *output;
77     int flags;
78     s_char *assoc;
79     int bufsize;
80     int (*notify) ();
81 };
82
83 void
84 io_init(void)
85 {
86     iom = iom_create(IO_READ | IO_WRITE);
87     fdmax = 0;
88     FD_ZERO(&newoutput);
89 }
90
91 struct iop *
92 io_open(int fd, int flags, int bufsize, int (*notify) (void),
93         s_char *assoc)
94 {
95     struct iop *iop;
96
97     flags = flags & (IO_READ | IO_WRITE | IO_NBLOCK | IO_NEWSOCK);
98     if ((flags & (IO_READ | IO_WRITE)) == 0)
99         return NULL;
100     iop = (struct iop *)malloc(sizeof(struct iop));
101     if (!iop)
102         return NULL;
103     iop->fd = fd;
104     iop->input = 0;
105     iop->output = 0;
106     iop->flags = 0;
107     iop->bufsize = bufsize;
108     if ((flags & IO_READ) && (flags & IO_NEWSOCK) == 0)
109         iop->input = ioq_create(bufsize);
110     if ((flags & IO_WRITE) && (flags & IO_NEWSOCK) == 0)
111         iop->output = ioq_create(bufsize);
112     if (flags & IO_NBLOCK)
113         io_noblocking(iop, 1);
114     iop->flags = flags;
115     iop->assoc = assoc;
116     iop->notify = notify;
117     iom_set(iom, flags, fd);
118     if (fd > fdmax) fdmax = fd;
119     return iop;
120 }
121
122 void
123 io_close(struct iop *iop)
124 {
125
126     if (iop->input != 0)
127         ioq_destroy(iop->input);
128     if (iop->output != 0)
129         ioq_destroy(iop->output);
130     iom_clear(iom, iop->flags, iop->fd);
131     FD_CLR(iop->fd, &newoutput);
132 #if !defined(_WIN32)
133     (void)close(iop->fd);
134 #else
135     closesocket(iop->fd);
136 #endif
137     free((s_char *)iop);
138 }
139
140 int
141 io_input(struct iop *iop, int waitforinput)
142 {
143     s_char buf[IO_BUFSIZE];
144     int cc;
145
146     /* Not a read IOP */
147     if ((iop->flags & IO_READ) == 0)
148         return -1;
149     /* IOP is markes as in error. */
150     if (iop->flags & IO_ERROR)
151         return -1;
152     /* Wait for the file to have input. */
153     if (waitforinput) {
154         empth_select(iop->fd, EMPTH_FD_READ);
155     }
156 #if !defined(_WIN32)
157     /* Do the actual read. */
158     cc = read(iop->fd, buf, sizeof(buf));
159     if (cc < 0) {
160         /* would block, so nothing to read. */
161         if (errno == EWOULDBLOCK)
162             return 0;
163
164         /* Some form of file error occurred... */
165         iop->flags |= IO_ERROR;
166         iom_clear(iom, IO_READ, iop->fd);
167         return -1;
168     }
169 #else
170     cc = recv(iop->fd, buf, sizeof(buf), 0);
171     if (cc == SOCKET_ERROR) {
172         int err = WSAGetLastError();
173         /* Hmm, it would block.  file is opened noblock, soooooo.. */
174         if (err == WSAEWOULDBLOCK)
175             return 0;
176
177         /* Some form of file error occurred... */
178         iop->flags |= IO_ERROR;
179         iom_clear(iom, IO_READ, iop->fd);
180         return -1;
181     }
182 #endif
183
184     /* We eof'd */
185     if (cc == 0) {
186         iop->flags |= IO_EOF;
187         return 0;
188     }
189
190     /* Append the input to the IOQ. */
191     ioq_append(iop->input, buf, cc);
192     return cc;
193 }
194
195 int
196 io_inputwaiting(struct iop *iop)
197 {
198     return ioq_qsize(iop->input);
199 }
200
201 int
202 io_outputwaiting(struct iop *iop)
203 {
204     return ioq_qsize(iop->output);
205 }
206
207 int
208 io_output(struct iop *iop, int waitforoutput)
209 {
210 #if !defined(_WIN32)
211     struct iovec iov[16];
212 #else
213     s_char buf[IO_BUFSIZE];
214 #endif
215     int cc;
216     int n;
217     int remain;
218
219     /* If there is no output waiting. */
220     if (!io_outputwaiting(iop))
221         return 0;
222
223     /* bit clear */
224     FD_CLR(iop->fd, &newoutput);
225
226     /* If the iop is not write enabled. */
227     if ((iop->flags & IO_WRITE) == 0)
228         return -1;
229
230     /* If the io is marked as in error... */
231     if (iop->flags & IO_ERROR)
232         return -1;
233
234     /* This is the same test as io_outputwaiting.... */
235     if (ioq_qsize(iop->output) == 0)
236         return 0;
237
238 #if !defined(_WIN32)
239     /* make the iov point to the data in the queue. */
240     /* I.E., each of the elements in the queue. */
241     /* returns the number of elements in the iov. */
242     n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
243 #else
244     /* Make a buffer containing the output to write. */
245     n = ioq_makebuf(iop->output, buf, sizeof(buf));
246 #endif
247
248     if (n <= 0) {
249         /* If we got no elements, we have no output.... */
250         iom_clear(iom, IO_WRITE, iop->fd);
251         return 0;
252     }
253
254     /* wait for the file to be output ready. */
255     if (waitforoutput != IO_NOWAIT) {
256         /* This waits for the file to be ready for writing, */
257         /* and lets other threads run. */
258         empth_select(iop->fd, EMPTH_FD_WRITE);
259     }
260
261     /* Do the actual write. */
262 #if !defined(_WIN32)
263     cc = writev(iop->fd, iov, n);
264
265     /* if it failed.... */
266     if (cc < 0) {
267         /* Hmm, it would block.  file is opened noblock, soooooo.. */
268         if (errno == EWOULDBLOCK) {
269             /* If there are remaining bytes, set the IO as remaining.. */
270             remain = ioq_qsize(iop->output);
271             if (remain > 0)
272                 iom_set(iom, IO_WRITE, iop->fd);
273             return remain;
274         }
275         iop->flags |= IO_ERROR;
276         iom_clear(iom, IO_WRITE, iop->fd);
277         return -1;
278     }
279 #else
280     cc = send(iop->fd, buf, n, 0);
281
282     /* if it failed.... */
283     if (cc == SOCKET_ERROR) {
284         int err = WSAGetLastError();
285         /* Hmm, it would block.  file is opened noblock, soooooo.. */
286         if (err == WSAEWOULDBLOCK) {
287             /* If there are remaining bytes, set the IO as remaining.. */
288             remain = ioq_qsize(iop->output);
289             if (remain > 0)
290                 iom_set(iom, IO_WRITE, iop->fd);
291             return remain;
292         }
293         iop->flags |= IO_ERROR;
294         iom_clear(iom, IO_WRITE, iop->fd);
295         return -1;
296     }
297 #endif
298
299
300     /* If no bytes were written, something happened..  Like an EOF. */
301 #ifndef hpux
302     if (cc == 0) {
303         iop->flags |= IO_EOF;
304         return 0;
305     }
306 #else
307     if (cc == 0) {
308         remain = ioq_qsize(iop->output);
309         if (remain > 0)
310             iom_set(iom, IO_WRITE, iop->fd);
311         return remain;
312     }
313 #endif /* hpux */
314
315     /* Remove the number of written bytes from the queue. */
316     ioq_dequeue(iop->output, cc);
317
318     /* If the queue has stuff remaining, set it still needing output. */
319     remain = ioq_qsize(iop->output);
320     if (remain == 0) {
321         iom_clear(iom, IO_WRITE, iop->fd);
322     } else {
323         iom_set(iom, IO_WRITE, iop->fd);
324     }
325     return cc;
326 }
327
328 int
329 io_peek(struct iop *iop, s_char *buf, int nbytes)
330 {
331     if ((iop->flags & IO_READ) == 0)
332         return -1;
333     return ioq_peek(iop->input, buf, nbytes);
334 }
335
336 int
337 io_read(struct iop *iop, s_char *buf, int nbytes)
338 {
339     int cc;
340
341     if ((iop->flags & IO_READ) == 0)
342         return -1;
343     cc = ioq_peek(iop->input, buf, nbytes);
344     if (cc > 0)
345         ioq_dequeue(iop->input, cc);
346     return cc;
347 }
348
349 int
350 io_write(struct iop *iop, s_char *buf, int nbytes, int doWait)
351 {
352     int len;
353
354     if ((iop->flags & IO_WRITE) == 0)
355         return -1;
356     ioq_append(iop->output, buf, nbytes);
357     FD_SET(iop->fd, &newoutput);
358     len = ioq_qsize(iop->output);
359     if (len > iop->bufsize) {
360         if (doWait) {
361             io_output_all(iop);
362         } else {
363             /* only try a write every BUFSIZE characters */
364             if (((len - nbytes) % iop->bufsize) < (len % iop->bufsize))
365                 io_output(iop, 0);
366         }
367     }
368     return nbytes;
369 }
370
371 int
372 io_output_all(struct iop *iop)
373 {
374     int n;
375
376     while ((n = io_output(iop, IO_NOWAIT)) > 0) {
377         empth_select(iop->fd, EMPTH_FD_WRITE);
378     }
379     return n;
380 }
381
382 int
383 io_gets(struct iop *iop, s_char *buf, int nbytes)
384 {
385     if ((iop->flags & IO_READ) == 0)
386         return -1;
387     return ioq_gets(iop->input, buf, nbytes);
388 }
389
390 int
391 io_puts(struct iop *iop, s_char *buf)
392 {
393     if ((iop->flags & IO_WRITE) == 0)
394         return -1;
395     FD_SET(iop->fd, &newoutput);
396     return ioq_puts(iop->output, buf);
397 }
398
399 int
400 io_shutdown(struct iop *iop, int flags)
401 {
402     flags &= (IO_READ | IO_WRITE);
403     if ((iop->flags & flags) != flags)
404         return -1;
405     if (flags & IO_READ) {
406         shutdown(iop->fd, 0);
407         ioq_drain(iop->input);
408     }
409     if (flags & IO_WRITE) {
410         shutdown(iop->fd, 1);
411         ioq_drain(iop->output);
412     }
413     return 0;
414 }
415
416 int
417 io_noblocking(struct iop *iop, int value)
418 {
419 #if !defined(_WIN32)
420     int flags;
421
422     flags = fcntl(iop->fd, F_GETFL, 0);
423     if (flags < 0)
424         return -1;
425     if (value == 0)
426         flags &= ~FNDELAY;
427     else
428         flags |= FNDELAY;
429     if (fcntl(iop->fd, F_SETFL, flags) < 0)
430         return -1;
431 #else
432     u_long arg = value;
433     ioctlsocket(iop->fd, FIONBIO, &arg);
434 #endif
435     if (value == 0)
436         iop->flags &= ~IO_NBLOCK;
437     else
438         iop->flags |= IO_NBLOCK;
439     return 0;
440 }
441
442 int
443 io_conn(struct iop *iop)
444 {
445     return (iop->flags & IO_CONN);
446 }
447
448 int
449 io_error(struct iop *iop)
450 {
451     return (iop->flags & IO_ERROR);
452 }
453
454 int
455 io_eof(struct iop *iop)
456 {
457     return (iop->flags & IO_EOF);
458 }
459
460 int
461 io_fileno(struct iop *iop)
462 {
463     return iop->fd;
464 }