src/lib/lwp/sel.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1994-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *  Copyright (C) 1991-3 Stephen Crane
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  *  ---
00022  *
00023  *  See files README, COPYING and CREDITS in the root of the source
00024  *  tree for related information and legal notices.  It is expected
00025  *  that future projects/authors will amend these files as needed.
00026  *
00027  *  ---
00028  *
00029  *  sel.c: arrange to block on read/write file descriptors using lwp
00030  * 
00031  *  Known contributors to this file:
00032  *     Dave Pare, 1994
00033  *     Markus Armbruster, 2007
00034  */
00035 
00036 #include <config.h>
00037 
00038 #include <errno.h>
00039 #include <sys/time.h>
00040 #include <time.h>
00041 #include <unistd.h>
00042 #include "lwp.h"
00043 #include "lwpint.h"
00044 #include "prototypes.h"
00045 
00046 /* Largest fd in LwpReadfds, LwpWritefds */
00047 static int LwpMaxfd;
00048 
00049 /* Number of file descriptors in LwpReadfds, LwpWritefds */
00050 static int LwpNfds;
00051 
00052 /* File descriptors waited for in lwpSleepFd() */
00053 static fd_set LwpReadfds, LwpWritefds;
00054 
00055 /* Map file descriptor to thread sleeping in lwpSleepFd() */
00056 static struct lwpProc **LwpFdwait;
00057 
00058 /* Threads sleeping in lwpSleepUntil(), in no particular order */
00059 static struct lwpQueue LwpDelayq;
00060 
00061 /* The thread executing lwpSelect() */
00062 static struct lwpProc *LwpSelProc;      
00063 
00064 void
00065 lwpInitSelect(struct lwpProc *proc)
00066 {
00067     LwpMaxfd = 0;
00068     LwpNfds = 0;
00069     FD_ZERO(&LwpReadfds);
00070     FD_ZERO(&LwpWritefds);
00071     LwpFdwait = calloc(FD_SETSIZE, sizeof(struct lwpProc *));
00072     LwpDelayq.head = 0;
00073     LwpDelayq.tail = 0;
00074     LwpSelProc = proc;
00075 }
00076 
00077 void
00078 lwpSleepFd(int fd, int mask)
00079 {
00080     lwpStatus(LwpCurrent, "sleeping on fd %d for %d", fd, mask);
00081 
00082     if (CANT_HAPPEN(fd > FD_SETSIZE))
00083         return;
00084     if (LwpFdwait[fd] != 0) {
00085         lwpStatus(LwpCurrent,
00086                   "multiple sleeps attempted on file descriptor %d", fd);
00087         return;
00088     }
00089     if (mask & LWP_FD_READ)
00090         FD_SET(fd, &LwpReadfds);
00091     if (mask & LWP_FD_WRITE)
00092         FD_SET(fd, &LwpWritefds);
00093 
00094     LwpNfds++;
00095 
00096     if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
00097         /* select process is sleeping until first waiter arrives */
00098         lwpStatus(LwpCurrent, "going to resched fd %d", fd);
00099         lwpReady(LwpSelProc);
00100     }
00101     lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
00102     if (fd > LwpMaxfd)
00103         LwpMaxfd = fd;
00104     LwpFdwait[fd] = LwpCurrent;
00105     LwpCurrent->fd = fd;
00106     lwpReschedule();
00107 }
00108 
00109 static void
00110 lwpWakeupFd(struct lwpProc *proc)
00111 {
00112     if (proc->fd < 0)
00113         return;
00114 
00115     lwpStatus(proc, "awakening; was sleeping on fd %d", proc->fd);
00116     FD_CLR(proc->fd, &LwpReadfds);
00117     FD_CLR(proc->fd, &LwpWritefds);
00118     LwpNfds--;
00119     LwpFdwait[proc->fd] = 0;
00120     proc->fd = -1;
00121     lwpReady(proc);
00122 }
00123 
00124 void
00125 lwpWakeupSleep(void)
00126 {
00127     time_t now;
00128     struct lwpQueue save;
00129     struct lwpProc *proc;
00130 
00131     if (LwpDelayq.head) {
00132         now = time(NULL);
00133         save.tail = save.head = 0;
00134         while (NULL != (proc = lwpGetFirst(&LwpDelayq))) {
00135             if (now >= proc->runtime) {
00136                 lwpStatus(proc, "sleep done");
00137                 lwpReady(proc);
00138             } else {
00139                 lwpAddTail(&save, proc);
00140             }
00141         }
00142         LwpDelayq = save;
00143     }
00144 }
00145 
00146 void
00147 lwpWakeup(struct lwpProc *proc)
00148 {
00149     if (proc->fd >= 0)
00150         lwpWakeupFd(proc);
00151     else if (proc->runtime != (time_t)-1) {
00152         proc->runtime = 0;
00153         lwpWakeupSleep();
00154     }
00155 }
00156 
00157 int
00158 lwpSleepUntil(time_t until)
00159 {
00160     int res;
00161 
00162     lwpStatus(LwpCurrent, "sleeping for %ld sec",
00163               (long)(until - time(NULL)));
00164     LwpCurrent->runtime = until;
00165     if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
00166         /* select process is sleeping until first waiter arrives */
00167         lwpReady(LwpSelProc);
00168     }
00169     lwpAddTail(&LwpDelayq, LwpCurrent);
00170     lwpReschedule();
00171     res = LwpCurrent->runtime ? 0 : -1;
00172     LwpCurrent->runtime = (time_t)-1;
00173     return res;
00174 }
00175 
00176 /*ARGSUSED*/
00177 void
00178 lwpSelect(void *arg)
00179 {
00180     struct lwpProc *us = LwpCurrent;
00181     fd_set readmask;
00182     fd_set writemask;
00183     int n;
00184     int fd;
00185     time_t now;
00186     time_t delta;
00187     struct lwpProc *proc;
00188     struct timeval tv;
00189 
00190     lwpStatus(us, "starting select loop");
00191     FD_ZERO(&readmask);
00192     FD_ZERO(&writemask);
00193     while (1) {
00194         while (1) {
00195             if (LwpNfds)
00196                 break;
00197             if (LwpDelayq.head)
00198                 break;
00199             /* wait for someone to lwpSleepFd or lwpSleepUntil */
00200             LwpMaxfd = 0;
00201             lwpStatus(us, "no fds or sleepers, waiting");
00202             lwpReschedule();
00203         }
00204         tv.tv_sec = 1000000;
00205         tv.tv_usec = 0;
00206         if (LwpDelayq.head) {
00207             time(&now);
00208             proc = LwpDelayq.head;
00209             for (; proc != 0; proc = proc->next) {
00210                 delta = proc->runtime - now;
00211                 if (delta < tv.tv_sec)
00212                     tv.tv_sec = delta;
00213             }
00214             if (tv.tv_sec < 0)
00215                 tv.tv_sec = 0;
00216         }
00217         lwpStatus(us, "selecting; sleep %ld secs", tv.tv_sec);
00218 
00219         memcpy(&readmask, &LwpReadfds, sizeof(fd_set));
00220         memcpy(&writemask, &LwpWritefds, sizeof(fd_set));
00221         n = select(LwpMaxfd + 1, &readmask, &writemask, NULL, &tv);
00222         if (n < 0) {
00223             if (errno != EINTR) {
00224                 logerror("select failed (%s)", strerror(errno));
00225                 exit(1);
00226             }
00227             /* go handle the signal */
00228             lwpReady(us);
00229             lwpReschedule();
00230             continue;
00231         }
00232 
00233         lwpWakeupSleep();
00234         if (n > 0) {
00235             /* file descriptor activity */
00236             for (fd = 0; fd <= LwpMaxfd; fd++) {
00237                 if (LwpFdwait[fd] == 0)
00238                     continue;
00239                 if (FD_ISSET(fd, &readmask)) {
00240                     lwpStatus(LwpFdwait[fd], "input ready");
00241                     lwpWakeupFd(LwpFdwait[fd]);
00242                     continue;
00243                 }
00244                 if (FD_ISSET(fd, &writemask)) {
00245                     lwpStatus(LwpFdwait[fd], "output ready");
00246                     lwpWakeupFd(LwpFdwait[fd]);
00247                     continue;
00248                 }
00249             }
00250         }
00251         lwpStatus(us, "fd dispatch completed");
00252         lwpReady(LwpCurrent);
00253         lwpReschedule();
00254     }
00255     /*NOTREACHED*/
00256 }

Generated on Fri Mar 28 11:01:14 2008 for empserver by  doxygen 1.5.2