/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2005, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ * Copyright (C) 1994-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
+ * Copyright (C) 1991-3 Stephen Crane
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* ---
*
- * See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
- * related information and legal notices. It is expected that any future
- * projects/authors will amend these files as needed.
+ * See files README, COPYING and CREDITS in the root of the source
+ * tree for related information and legal notices. It is expected
+ * that future projects/authors will amend these files as needed.
*
* ---
*
* sel.c: arrange to block on read/write file descriptors using lwp
- *
+ *
* Known contributors to this file:
* Dave Pare, 1994
+ * Markus Armbruster, 2007
*/
-#include <stdlib.h>
+#include <config.h>
+
#include <errno.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/file.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
-
-#ifdef hpux
-#include <stdio.h>
-#endif
-
#include "lwp.h"
#include "lwpint.h"
+#include "prototypes.h"
-#if defined(_EMPTH_LWP)
+/* Largest fd in LwpReadfds, LwpWritefds */
+static int LwpMaxfd;
-struct lwpSelect {
- int maxfd;
- int nfds;
- int nfile;
- fd_set readmask;
- fd_set writemask;
- struct lwpProc **wait;
- struct lwpQueue delayq;
- struct lwpProc *proc;
-};
+/* Number of file descriptors in LwpReadfds, LwpWritefds */
+static int LwpNfds;
-struct lwpSelect LwpSelect;
+/* File descriptors waited for in lwpSleepFd() */
+static fd_set LwpReadfds, LwpWritefds;
+
+/* Map file descriptor to thread sleeping in lwpSleepFd() */
+static struct lwpProc **LwpFdwait;
+
+/*
+ * Threads sleeping until a wakeup time, in lwpSleepUntil() or
+ * lwpSleepFd(), in no particular order
+ */
+static struct lwpQueue LwpDelayq;
+
+/* The thread executing lwpSelect() */
+static struct lwpProc *LwpSelProc;
void
lwpInitSelect(struct lwpProc *proc)
{
- LwpSelect.maxfd = 0;
- LwpSelect.nfds = 0;
-#ifdef hpux
- LwpSelect.nfile = _NFILE;
-#else
- LwpSelect.nfile = getdtablesize();
-#endif
- FD_ZERO(&LwpSelect.readmask);
- FD_ZERO(&LwpSelect.writemask);
- LwpSelect.wait = calloc(LwpSelect.nfile, sizeof(char *));
- LwpSelect.delayq.head = 0;
- LwpSelect.delayq.tail = 0;
- LwpSelect.proc = proc;
+ LwpMaxfd = 0;
+ LwpNfds = 0;
+ FD_ZERO(&LwpReadfds);
+ FD_ZERO(&LwpWritefds);
+ LwpFdwait = calloc(FD_SETSIZE, sizeof(struct lwpProc *));
+ LwpDelayq.head = 0;
+ LwpDelayq.tail = 0;
+ LwpSelProc = proc;
}
-void
-lwpSleepFd(int fd, int mask)
+int
+lwpSleepFd(int fd, int mask, struct timeval *timeout)
{
- lwpStatus(LwpCurrent, "sleeping on fd %d", fd);
+ lwpStatus(LwpCurrent, "sleeping on fd %d for %d", fd, mask);
- if (LwpSelect.wait[fd] != 0) {
+ if (CANT_HAPPEN(fd > FD_SETSIZE)) {
+ errno = EBADF;
+ return -1;
+ }
+ if (LwpFdwait[fd] != 0) {
lwpStatus(LwpCurrent,
"multiple sleeps attempted on file descriptor %d", fd);
- return;
+ errno = EBADF;
+ return -1;
}
if (mask & LWP_FD_READ)
- FD_SET(fd, &LwpSelect.readmask);
+ FD_SET(fd, &LwpReadfds);
if (mask & LWP_FD_WRITE)
- FD_SET(fd, &LwpSelect.writemask);
-
- LwpSelect.nfds++;
+ FD_SET(fd, &LwpWritefds);
+ LwpNfds++;
- if (LwpSelect.maxfd == 0 && LwpSelect.delayq.head == 0) {
+ if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
/* select process is sleeping until first waiter arrives */
lwpStatus(LwpCurrent, "going to resched fd %d", fd);
- lwpReady(LwpSelect.proc);
+ lwpReady(LwpSelProc);
}
lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
- if (fd > LwpSelect.maxfd)
- LwpSelect.maxfd = fd;
- LwpSelect.wait[fd] = LwpCurrent;
+
+ if (timeout) {
+ LwpCurrent->runtime = time(NULL) + timeout->tv_sec +
+ (timeout->tv_usec > 0);
+ lwpAddTail(&LwpDelayq, LwpCurrent);
+ } else
+ LwpCurrent->runtime = (time_t)-1;
+
+ if (fd > LwpMaxfd)
+ LwpMaxfd = fd;
+ LwpFdwait[fd] = LwpCurrent;
LwpCurrent->fd = fd;
+ LwpCurrent->fd_ready = 0;
lwpReschedule();
+ return LwpCurrent->fd_ready != 0;
}
-void
+/*
+ * Wake up PROC if it is sleeping in lwpSleepFd().
+ * Must be followed by lwpWakeupSleep() before the next lwpReschedule().
+ */
+static void
lwpWakeupFd(struct lwpProc *proc)
{
- if (proc->fd < 0)
+ if (CANT_HAPPEN(proc->fd < 0 || proc->fd > LwpMaxfd))
return;
lwpStatus(proc, "awakening; was sleeping on fd %d", proc->fd);
- FD_CLR(proc->fd, &LwpSelect.readmask);
- FD_CLR(proc->fd, &LwpSelect.writemask);
- LwpSelect.nfds--;
- LwpSelect.wait[proc->fd] = 0;
+ if (proc->runtime != (time_t)-1 && proc->runtime != 0) {
+ /* is in LwpDelayq; leave the job to lwpWakeupSleep() */
+ proc->runtime = (time_t)-1;
+ return;
+ }
+ FD_CLR(proc->fd, &LwpReadfds);
+ FD_CLR(proc->fd, &LwpWritefds);
+ LwpNfds--;
+ LwpFdwait[proc->fd] = 0;
proc->fd = -1;
lwpReady(proc);
}
+/*
+ * Wake up threads in LwpDelayq whose time has come.
+ */
void
-lwpSleepUntil(long int until)
+lwpWakeupSleep(void)
{
- lwpStatus(LwpCurrent, "sleeping for %ld sec", until - time(0));
+ time_t now;
+ struct lwpQueue save;
+ struct lwpProc *proc;
+
+ if (LwpDelayq.head) {
+ now = time(NULL);
+ save.tail = save.head = 0;
+ while (NULL != (proc = lwpGetFirst(&LwpDelayq))) {
+ if (now >= proc->runtime) {
+ lwpStatus(proc, "sleep done");
+ if (proc->runtime != 0)
+ proc->runtime = (time_t)-1;
+ if (proc->fd >= 0)
+ lwpWakeupFd(proc);
+ else
+ lwpReady(proc);
+ } else {
+ lwpAddTail(&save, proc);
+ }
+ }
+ LwpDelayq = save;
+ }
+}
+
+void
+lwpWakeup(struct lwpProc *proc)
+{
+ if (proc->fd >= 0)
+ lwpWakeupFd(proc);
+ else if (proc->runtime != (time_t)-1)
+ proc->runtime = 0;
+ lwpWakeupSleep();
+}
+
+int
+lwpSleepUntil(time_t until)
+{
+ int res;
+
+ lwpStatus(LwpCurrent, "sleeping for %ld sec",
+ (long)(until - time(NULL)));
LwpCurrent->runtime = until;
- if (LwpSelect.maxfd == 0 && LwpSelect.delayq.head == 0) {
+ if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
/* select process is sleeping until first waiter arrives */
- lwpReady(LwpSelect.proc);
+ lwpReady(LwpSelProc);
}
- lwpAddTail(&LwpSelect.delayq, LwpCurrent);
+ lwpAddTail(&LwpDelayq, LwpCurrent);
lwpReschedule();
+ res = LwpCurrent->runtime ? 0 : -1;
+ LwpCurrent->runtime = (time_t)-1;
+ return res;
}
/*ARGSUSED*/
time_t delta;
struct lwpProc *proc;
struct timeval tv;
- struct lwpQueue save;
lwpStatus(us, "starting select loop");
FD_ZERO(&readmask);
FD_ZERO(&writemask);
while (1) {
while (1) {
- if (LwpSelect.nfds)
+ if (LwpNfds)
break;
- if (LwpSelect.delayq.head)
+ if (LwpDelayq.head)
break;
/* wait for someone to lwpSleepFd or lwpSleepUntil */
- LwpSelect.maxfd = 0;
+ LwpMaxfd = 0;
lwpStatus(us, "no fds or sleepers, waiting");
lwpReschedule();
}
tv.tv_sec = 1000000;
tv.tv_usec = 0;
- if (LwpSelect.delayq.head) {
+ if (LwpDelayq.head) {
time(&now);
- proc = LwpSelect.delayq.head;
+ proc = LwpDelayq.head;
for (; proc != 0; proc = proc->next) {
delta = proc->runtime - now;
if (delta < tv.tv_sec)
if (tv.tv_sec < 0)
tv.tv_sec = 0;
}
- lwpStatus(us, "selecting; sleep %ld secs", (long)delta);
-
- memcpy(&readmask, &LwpSelect.readmask, sizeof(fd_set));
- memcpy(&writemask, &LwpSelect.writemask, sizeof(fd_set));
- n = select(LwpSelect.maxfd + 1, &readmask, &writemask,
- (fd_set *) 0, &tv);
+ lwpStatus(us, "selecting; sleep %ld secs", tv.tv_sec);
+ memcpy(&readmask, &LwpReadfds, sizeof(fd_set));
+ memcpy(&writemask, &LwpWritefds, sizeof(fd_set));
+ n = select(LwpMaxfd + 1, &readmask, &writemask, NULL, &tv);
if (n < 0) {
- if (errno == EINTR) {
- /* go handle the signal */
- lwpReady(us);
- lwpReschedule();
- continue;
+ if (errno != EINTR) {
+ logerror("select failed (%s)", strerror(errno));
+ exit(1);
}
- lwpStatus(us, "select failed (bad file descriptor?)");
- exit(-1);
+ /* go handle the signal */
+ lwpReady(us);
+ lwpReschedule();
+ continue;
}
- if (LwpSelect.delayq.head) {
- /* sleeping proecss activity */
- time(&now);
- save.tail = save.head = 0;
- while (NULL != (proc = lwpGetFirst(&LwpSelect.delayq))) {
- if (now >= proc->runtime) {
- lwpStatus(proc, "sleep done");
- lwpReady(proc);
- } else {
- lwpAddTail(&save, proc);
- }
- }
- LwpSelect.delayq = save;
- }
if (n > 0) {
/* file descriptor activity */
- for (fd = 0; fd <= LwpSelect.maxfd; fd++) {
- if (LwpSelect.wait[fd] == 0)
+ for (fd = 0; fd <= LwpMaxfd; fd++) {
+ if (LwpFdwait[fd] == 0)
continue;
if (FD_ISSET(fd, &readmask)) {
- lwpStatus(LwpSelect.wait[fd], "input ready");
- lwpWakeupFd(LwpSelect.wait[fd]);
+ lwpStatus(LwpFdwait[fd], "input ready");
+ LwpFdwait[fd]->fd_ready = 1;
+ lwpWakeupFd(LwpFdwait[fd]);
continue;
}
if (FD_ISSET(fd, &writemask)) {
- lwpStatus(LwpSelect.wait[fd], "output ready");
- lwpWakeupFd(LwpSelect.wait[fd]);
+ lwpStatus(LwpFdwait[fd], "output ready");
+ LwpFdwait[fd]->fd_ready = 1;
+ lwpWakeupFd(LwpFdwait[fd]);
continue;
}
}
}
+ lwpWakeupSleep();
lwpStatus(us, "fd dispatch completed");
lwpReady(LwpCurrent);
lwpReschedule();
}
/*NOTREACHED*/
}
-#endif