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