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