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
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.
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, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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.
29 * sel.c: arrange to block on read/write file descriptors using lwp
31 * Known contributors to this file:
33 * Markus Armbruster, 2007
34 * Ron Koenderink, 2009
45 #include "prototypes.h"
47 /* Largest fd in LwpReadfds, LwpWritefds */
50 /* Number of file descriptors in LwpReadfds, LwpWritefds */
53 /* File descriptors waited for in lwpSleepFd() */
54 static fd_set LwpReadfds, LwpWritefds;
56 /* Map file descriptor to thread sleeping in lwpSleepFd() */
57 static struct lwpProc **LwpFdwait;
60 * Threads sleeping until a wakeup time, in lwpSleepUntil() or
61 * lwpSleepFd(), in no particular order
63 static struct lwpQueue LwpDelayq;
65 /* The thread executing lwpSelect() */
66 static struct lwpProc *LwpSelProc;
69 lwpInitSelect(struct lwpProc *proc)
74 FD_ZERO(&LwpWritefds);
75 LwpFdwait = calloc(FD_SETSIZE, sizeof(struct lwpProc *));
82 lwpSleepFd(int fd, int mask, struct timeval *timeout)
84 lwpStatus(LwpCurrent, "sleeping on fd %d for %d", fd, mask);
86 if (CANT_HAPPEN(fd > FD_SETSIZE)) {
90 if (LwpFdwait[fd] != 0) {
92 "multiple sleeps attempted on file descriptor %d", fd);
96 if (mask & LWP_FD_READ)
97 FD_SET(fd, &LwpReadfds);
98 if (mask & LWP_FD_WRITE)
99 FD_SET(fd, &LwpWritefds);
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);
107 lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
110 LwpCurrent->runtime = time(NULL) + timeout->tv_sec +
111 (timeout->tv_usec > 0);
112 lwpAddTail(&LwpDelayq, LwpCurrent);
114 LwpCurrent->runtime = (time_t)-1;
118 LwpFdwait[fd] = LwpCurrent;
120 LwpCurrent->fd_ready = 0;
122 return LwpCurrent->fd_ready != 0;
126 * Wake up PROC if it is sleeping in lwpSleepFd().
127 * Must be followed by lwpWakeupSleep() before the next lwpReschedule().
130 lwpWakeupFd(struct lwpProc *proc)
132 if (CANT_HAPPEN(proc->fd < 0 || proc->fd > LwpMaxfd))
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;
141 FD_CLR(proc->fd, &LwpReadfds);
142 FD_CLR(proc->fd, &LwpWritefds);
144 LwpFdwait[proc->fd] = 0;
150 * Wake up threads in LwpDelayq whose time has come.
156 struct lwpQueue save;
157 struct lwpProc *proc;
159 if (LwpDelayq.head) {
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;
172 lwpAddTail(&save, proc);
180 lwpWakeup(struct lwpProc *proc)
184 else if (proc->runtime != (time_t)-1)
190 lwpSleepUntil(time_t until)
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);
201 lwpAddTail(&LwpDelayq, LwpCurrent);
203 res = LwpCurrent->runtime ? 0 : -1;
204 LwpCurrent->runtime = (time_t)-1;
212 struct lwpProc *us = LwpCurrent;
219 struct lwpProc *proc;
222 lwpStatus(us, "starting select loop");
231 /* wait for someone to lwpSleepFd or lwpSleepUntil */
233 lwpStatus(us, "no fds or sleepers, waiting");
238 if (LwpDelayq.head) {
240 proc = LwpDelayq.head;
241 for (; proc != 0; proc = proc->next) {
242 delta = proc->runtime - now;
243 if (delta < tv.tv_sec)
249 lwpStatus(us, "selecting; sleep %ld secs", tv.tv_sec);
251 memcpy(&readmask, &LwpReadfds, sizeof(fd_set));
252 memcpy(&writemask, &LwpWritefds, sizeof(fd_set));
253 n = select(LwpMaxfd + 1, &readmask, &writemask, NULL, &tv);
255 if (errno != EINTR) {
256 logerror("select failed (%s)", strerror(errno));
259 /* go handle the signal */
266 /* file descriptor activity */
267 for (fd = 0; fd <= LwpMaxfd; fd++) {
268 if (LwpFdwait[fd] == 0)
270 if (FD_ISSET(fd, &readmask)) {
271 lwpStatus(LwpFdwait[fd], "input ready");
272 LwpFdwait[fd]->fd_ready = 1;
273 lwpWakeupFd(LwpFdwait[fd]);
276 if (FD_ISSET(fd, &writemask)) {
277 lwpStatus(LwpFdwait[fd], "output ready");
278 LwpFdwait[fd]->fd_ready = 1;
279 lwpWakeupFd(LwpFdwait[fd]);
285 lwpStatus(us, "fd dispatch completed");
286 lwpReady(LwpCurrent);