]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/sel.c
Update known contributors comments
[empserver] / src / lib / lwp / sel.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1994-2009, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *  Copyright (C) 1991-3 Stephen Crane
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *  ---
22  *
23  *  See files README, COPYING and CREDITS in the root of the source
24  *  tree for related information and legal notices.  It is expected
25  *  that future projects/authors will amend these files as needed.
26  *
27  *  ---
28  *
29  *  sel.c: arrange to block on read/write file descriptors using lwp
30  *
31  *  Known contributors to this file:
32  *     Dave Pare, 1994
33  *     Markus Armbruster, 2007
34  *     Ron Koenderink, 2009
35  */
36
37 #include <config.h>
38
39 #include <errno.h>
40 #include <sys/time.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include "lwp.h"
44 #include "lwpint.h"
45 #include "prototypes.h"
46
47 /* Largest fd in LwpReadfds, LwpWritefds */
48 static int LwpMaxfd;
49
50 /* Number of file descriptors in LwpReadfds, LwpWritefds */
51 static int LwpNfds;
52
53 /* File descriptors waited for in lwpSleepFd() */
54 static fd_set LwpReadfds, LwpWritefds;
55
56 /* Map file descriptor to thread sleeping in lwpSleepFd() */
57 static struct lwpProc **LwpFdwait;
58
59 /*
60  * Threads sleeping until a wakeup time, in lwpSleepUntil() or
61  * lwpSleepFd(), in no particular order
62  */
63 static struct lwpQueue LwpDelayq;
64
65 /* The thread executing lwpSelect() */
66 static struct lwpProc *LwpSelProc;
67
68 void
69 lwpInitSelect(struct lwpProc *proc)
70 {
71     LwpMaxfd = 0;
72     LwpNfds = 0;
73     FD_ZERO(&LwpReadfds);
74     FD_ZERO(&LwpWritefds);
75     LwpFdwait = calloc(FD_SETSIZE, sizeof(struct lwpProc *));
76     LwpDelayq.head = 0;
77     LwpDelayq.tail = 0;
78     LwpSelProc = proc;
79 }
80
81 int
82 lwpSleepFd(int fd, int mask, struct timeval *timeout)
83 {
84     lwpStatus(LwpCurrent, "sleeping on fd %d for %d", fd, mask);
85
86     if (CANT_HAPPEN(fd > FD_SETSIZE)) {
87         errno = EBADF;
88         return -1;
89     }
90     if (LwpFdwait[fd] != 0) {
91         lwpStatus(LwpCurrent,
92                   "multiple sleeps attempted on file descriptor %d", fd);
93         errno = EBADF;
94         return -1;
95     }
96     if (mask & LWP_FD_READ)
97         FD_SET(fd, &LwpReadfds);
98     if (mask & LWP_FD_WRITE)
99         FD_SET(fd, &LwpWritefds);
100     LwpNfds++;
101
102     if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
103         /* select process is sleeping until first waiter arrives */
104         lwpStatus(LwpCurrent, "going to resched fd %d", fd);
105         lwpReady(LwpSelProc);
106     }
107     lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
108
109     if (timeout) {
110         LwpCurrent->runtime = time(NULL) + timeout->tv_sec +
111             (timeout->tv_usec > 0);
112         lwpAddTail(&LwpDelayq, LwpCurrent);
113     } else
114         LwpCurrent->runtime = (time_t)-1;
115
116     if (fd > LwpMaxfd)
117         LwpMaxfd = fd;
118     LwpFdwait[fd] = LwpCurrent;
119     LwpCurrent->fd = fd;
120     LwpCurrent->fd_ready = 0;
121     lwpReschedule();
122     return LwpCurrent->fd_ready != 0;
123 }
124
125 /*
126  * Wake up PROC if it is sleeping in lwpSleepFd().
127  * Must be followed by lwpWakeupSleep() before the next lwpReschedule().
128  */
129 static void
130 lwpWakeupFd(struct lwpProc *proc)
131 {
132     if (CANT_HAPPEN(proc->fd < 0 || proc->fd > LwpMaxfd))
133         return;
134
135     lwpStatus(proc, "awakening; was sleeping on fd %d", proc->fd);
136     if (proc->runtime != (time_t)-1 && proc->runtime != 0) {
137         /* is in LwpDelayq; leave the job to lwpWakeupSleep() */
138         proc->runtime = (time_t)-1;
139         return;
140     }
141     FD_CLR(proc->fd, &LwpReadfds);
142     FD_CLR(proc->fd, &LwpWritefds);
143     LwpNfds--;
144     LwpFdwait[proc->fd] = 0;
145     proc->fd = -1;
146     lwpReady(proc);
147 }
148
149 /*
150  * Wake up threads in LwpDelayq whose time has come.
151  */
152 void
153 lwpWakeupSleep(void)
154 {
155     time_t now;
156     struct lwpQueue save;
157     struct lwpProc *proc;
158
159     if (LwpDelayq.head) {
160         now = time(NULL);
161         save.tail = save.head = 0;
162         while (NULL != (proc = lwpGetFirst(&LwpDelayq))) {
163             if (now >= proc->runtime) {
164                 lwpStatus(proc, "sleep done");
165                 if (proc->runtime != 0)
166                     proc->runtime = (time_t)-1;
167                 if (proc->fd >= 0)
168                     lwpWakeupFd(proc);
169                 else
170                     lwpReady(proc);
171             } else {
172                 lwpAddTail(&save, proc);
173             }
174         }
175         LwpDelayq = save;
176     }
177 }
178
179 void
180 lwpWakeup(struct lwpProc *proc)
181 {
182     if (proc->fd >= 0)
183         lwpWakeupFd(proc);
184     else if (proc->runtime != (time_t)-1)
185         proc->runtime = 0;
186     lwpWakeupSleep();
187 }
188
189 int
190 lwpSleepUntil(time_t until)
191 {
192     int res;
193
194     lwpStatus(LwpCurrent, "sleeping for %ld sec",
195               (long)(until - time(NULL)));
196     LwpCurrent->runtime = until;
197     if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
198         /* select process is sleeping until first waiter arrives */
199         lwpReady(LwpSelProc);
200     }
201     lwpAddTail(&LwpDelayq, LwpCurrent);
202     lwpReschedule();
203     res = LwpCurrent->runtime ? 0 : -1;
204     LwpCurrent->runtime = (time_t)-1;
205     return res;
206 }
207
208 /*ARGSUSED*/
209 void
210 lwpSelect(void *arg)
211 {
212     struct lwpProc *us = LwpCurrent;
213     fd_set readmask;
214     fd_set writemask;
215     int n;
216     int fd;
217     time_t now;
218     time_t delta;
219     struct lwpProc *proc;
220     struct timeval tv;
221
222     lwpStatus(us, "starting select loop");
223     FD_ZERO(&readmask);
224     FD_ZERO(&writemask);
225     while (1) {
226         while (1) {
227             if (LwpNfds)
228                 break;
229             if (LwpDelayq.head)
230                 break;
231             /* wait for someone to lwpSleepFd or lwpSleepUntil */
232             LwpMaxfd = 0;
233             lwpStatus(us, "no fds or sleepers, waiting");
234             lwpReschedule();
235         }
236         tv.tv_sec = 1000000;
237         tv.tv_usec = 0;
238         if (LwpDelayq.head) {
239             time(&now);
240             proc = LwpDelayq.head;
241             for (; proc != 0; proc = proc->next) {
242                 delta = proc->runtime - now;
243                 if (delta < tv.tv_sec)
244                     tv.tv_sec = delta;
245             }
246             if (tv.tv_sec < 0)
247                 tv.tv_sec = 0;
248         }
249         lwpStatus(us, "selecting; sleep %ld secs", tv.tv_sec);
250
251         memcpy(&readmask, &LwpReadfds, sizeof(fd_set));
252         memcpy(&writemask, &LwpWritefds, sizeof(fd_set));
253         n = select(LwpMaxfd + 1, &readmask, &writemask, NULL, &tv);
254         if (n < 0) {
255             if (errno != EINTR) {
256                 logerror("select failed (%s)", strerror(errno));
257                 exit(1);
258             }
259             /* go handle the signal */
260             lwpReady(us);
261             lwpReschedule();
262             continue;
263         }
264
265         if (n > 0) {
266             /* file descriptor activity */
267             for (fd = 0; fd <= LwpMaxfd; fd++) {
268                 if (LwpFdwait[fd] == 0)
269                     continue;
270                 if (FD_ISSET(fd, &readmask)) {
271                     lwpStatus(LwpFdwait[fd], "input ready");
272                     LwpFdwait[fd]->fd_ready = 1;
273                     lwpWakeupFd(LwpFdwait[fd]);
274                     continue;
275                 }
276                 if (FD_ISSET(fd, &writemask)) {
277                     lwpStatus(LwpFdwait[fd], "output ready");
278                     LwpFdwait[fd]->fd_ready = 1;
279                     lwpWakeupFd(LwpFdwait[fd]);
280                     continue;
281                 }
282             }
283         }
284         lwpWakeupSleep();
285         lwpStatus(us, "fd dispatch completed");
286         lwpReady(LwpCurrent);
287         lwpReschedule();
288     }
289     /*NOTREACHED*/
290 }