2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1994-2012, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
5 * Copyright (C) 1991-3 Stephen Crane
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.
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.
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/>.
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.
28 * sel.c: arrange to block on read/write file descriptors using lwp
30 * Known contributors to this file:
32 * Markus Armbruster, 2007-2011
33 * Ron Koenderink, 2009
39 #include <sys/select.h>
43 #include "prototypes.h"
45 /* Largest fd in LwpReadfds, LwpWritefds */
48 /* Number of file descriptors in LwpReadfds, LwpWritefds */
51 /* File descriptors waited for in lwpSleepFd() */
52 static fd_set LwpReadfds, LwpWritefds;
54 /* Map file descriptor to thread sleeping in lwpSleepFd() */
55 static struct lwpProc **LwpFdwait;
58 * Threads sleeping until a wakeup time, in lwpSleepUntil() or
59 * lwpSleepFd(), in no particular order
61 static struct lwpQueue LwpDelayq;
63 /* The thread executing lwpSelect() */
64 static struct lwpProc *LwpSelProc;
67 lwpInitSelect(struct lwpProc *proc)
72 FD_ZERO(&LwpWritefds);
73 LwpFdwait = calloc(FD_SETSIZE, sizeof(struct lwpProc *));
74 LwpDelayq.head = NULL;
75 LwpDelayq.tail = NULL;
80 lwpSleepFd(int fd, int mask, struct timeval *timeout)
82 lwpStatus(LwpCurrent, "sleeping on fd %d for %d", fd, mask);
84 if (CANT_HAPPEN(fd < 0 || fd >= FD_SETSIZE)) {
90 "multiple sleeps attempted on file descriptor %d", fd);
94 if (mask & LWP_FD_READ)
95 FD_SET(fd, &LwpReadfds);
96 if (mask & LWP_FD_WRITE)
97 FD_SET(fd, &LwpWritefds);
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);
105 lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
108 LwpCurrent->runtime = time(NULL) + timeout->tv_sec +
109 (timeout->tv_usec > 0);
110 lwpAddTail(&LwpDelayq, LwpCurrent);
112 LwpCurrent->runtime = (time_t)-1;
116 LwpFdwait[fd] = LwpCurrent;
118 LwpCurrent->fd_ready = 0;
120 return LwpCurrent->fd_ready != 0;
124 * Wake up PROC if it is sleeping in lwpSleepFd().
125 * Must be followed by lwpWakeupSleep() before the next lwpReschedule().
128 lwpWakeupFd(struct lwpProc *proc)
130 if (CANT_HAPPEN(proc->fd < 0 || proc->fd > LwpMaxfd))
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;
138 lwpStatus(proc, "awakening; was sleeping on fd %d", proc->fd);
139 FD_CLR(proc->fd, &LwpReadfds);
140 FD_CLR(proc->fd, &LwpWritefds);
142 LwpFdwait[proc->fd] = NULL;
148 * Wake up threads in LwpDelayq whose time has come.
154 struct lwpQueue save;
155 struct lwpProc *proc;
157 if (LwpDelayq.head) {
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;
170 lwpAddTail(&save, proc);
178 lwpWakeup(struct lwpProc *proc)
182 else if (proc->runtime != (time_t)-1)
188 lwpSleepUntil(time_t until)
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);
199 lwpAddTail(&LwpDelayq, LwpCurrent);
201 res = LwpCurrent->runtime ? 0 : -1;
202 LwpCurrent->runtime = (time_t)-1;
210 struct lwpProc *us = LwpCurrent;
217 struct lwpProc *proc;
220 lwpStatus(us, "starting select loop");
229 /* wait for someone to lwpSleepFd or lwpSleepUntil */
231 lwpStatus(us, "no fds or sleepers, waiting");
236 if (LwpDelayq.head) {
238 for (proc = LwpDelayq.head; proc; proc = proc->next) {
239 delta = proc->runtime - now;
240 if (delta < tv.tv_sec)
246 lwpStatus(us, "selecting; sleep %ld secs", tv.tv_sec);
248 memcpy(&readmask, &LwpReadfds, sizeof(fd_set));
249 memcpy(&writemask, &LwpWritefds, sizeof(fd_set));
250 n = select(LwpMaxfd + 1, &readmask, &writemask, NULL, &tv);
252 if (errno != EINTR) {
253 logerror("select failed (%s)", strerror(errno));
256 /* go handle the signal */
263 /* file descriptor activity */
264 for (fd = 0; fd <= LwpMaxfd; fd++) {
267 if (FD_ISSET(fd, &readmask)) {
268 lwpStatus(LwpFdwait[fd], "input ready");
269 LwpFdwait[fd]->fd_ready = 1;
270 lwpWakeupFd(LwpFdwait[fd]);
273 if (FD_ISSET(fd, &writemask)) {
274 lwpStatus(LwpFdwait[fd], "output ready");
275 LwpFdwait[fd]->fd_ready = 1;
276 lwpWakeupFd(LwpFdwait[fd]);
282 lwpStatus(us, "fd dispatch completed");
283 lwpReady(LwpCurrent);