]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/sel.c
Fix update thread not successfully waking up with LWP
[empserver] / src / lib / lwp / sel.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1994-2008, 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 /*
59  * Threads sleeping until a wakeup time, in lwpSleepUntil() or
60  * lwpSleepFd(), in no particular order
61  */
62 static struct lwpQueue LwpDelayq;
63
64 /* The thread executing lwpSelect() */
65 static struct lwpProc *LwpSelProc;
66
67 void
68 lwpInitSelect(struct lwpProc *proc)
69 {
70     LwpMaxfd = 0;
71     LwpNfds = 0;
72     FD_ZERO(&LwpReadfds);
73     FD_ZERO(&LwpWritefds);
74     LwpFdwait = calloc(FD_SETSIZE, sizeof(struct lwpProc *));
75     LwpDelayq.head = 0;
76     LwpDelayq.tail = 0;
77     LwpSelProc = proc;
78 }
79
80 int
81 lwpSleepFd(int fd, int mask, struct timeval *timeout)
82 {
83     lwpStatus(LwpCurrent, "sleeping on fd %d for %d", fd, mask);
84
85     if (CANT_HAPPEN(fd > FD_SETSIZE)) {
86         errno = EBADF;
87         return -1;
88     }
89     if (LwpFdwait[fd] != 0) {
90         lwpStatus(LwpCurrent,
91                   "multiple sleeps attempted on file descriptor %d", fd);
92         errno = EBADF;
93         return -1;
94     }
95     if (mask & LWP_FD_READ)
96         FD_SET(fd, &LwpReadfds);
97     if (mask & LWP_FD_WRITE)
98         FD_SET(fd, &LwpWritefds);
99     LwpNfds++;
100
101     if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
102         /* select process is sleeping until first waiter arrives */
103         lwpStatus(LwpCurrent, "going to resched fd %d", fd);
104         lwpReady(LwpSelProc);
105     }
106     lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
107
108     if (timeout) {
109         LwpCurrent->runtime = time(NULL) + timeout->tv_sec +
110             (timeout->tv_usec > 0);
111         lwpAddTail(&LwpDelayq, LwpCurrent);
112     } else
113         LwpCurrent->runtime = (time_t)-1;
114
115     if (fd > LwpMaxfd)
116         LwpMaxfd = fd;
117     LwpFdwait[fd] = LwpCurrent;
118     LwpCurrent->fd = fd;
119     LwpCurrent->fd_ready = 0;
120     lwpReschedule();
121     return LwpCurrent->fd_ready != 0;
122 }
123
124 /*
125  * Wake up PROC if it is sleeping in lwpSleepFd().
126  * Must be followed by lwpWakeupSleep() before the next lwpReschedule().
127  */
128 static void
129 lwpWakeupFd(struct lwpProc *proc)
130 {
131     if (CANT_HAPPEN(proc->fd < 0 || proc->fd > LwpMaxfd))
132         return;
133
134     lwpStatus(proc, "awakening; was sleeping on fd %d", proc->fd);
135     if (proc->runtime != (time_t)-1 && proc->runtime != 0) {
136         /* is in LwpDelayq; leave the job to lwpWakeupSleep() */
137         proc->runtime = (time_t)-1;
138         return;
139     }
140     FD_CLR(proc->fd, &LwpReadfds);
141     FD_CLR(proc->fd, &LwpWritefds);
142     LwpNfds--;
143     LwpFdwait[proc->fd] = 0;
144     proc->fd = -1;
145     lwpReady(proc);
146 }
147
148 /*
149  * Wake up threads in LwpDelayq whose time has come.
150  */
151 void
152 lwpWakeupSleep(void)
153 {
154     time_t now;
155     struct lwpQueue save;
156     struct lwpProc *proc;
157
158     if (LwpDelayq.head) {
159         now = time(NULL);
160         save.tail = save.head = 0;
161         while (NULL != (proc = lwpGetFirst(&LwpDelayq))) {
162             if (now >= proc->runtime) {
163                 lwpStatus(proc, "sleep done");
164                 if (proc->runtime != 0)
165                     proc->runtime = (time_t)-1;
166                 if (proc->fd >= 0)
167                     lwpWakeupFd(proc);
168                 else
169                     lwpReady(proc);
170             } else {
171                 lwpAddTail(&save, proc);
172             }
173         }
174         LwpDelayq = save;
175     }
176 }
177
178 void
179 lwpWakeup(struct lwpProc *proc)
180 {
181     if (proc->fd >= 0)
182         lwpWakeupFd(proc);
183     else if (proc->runtime != (time_t)-1)
184         proc->runtime = 0;
185     lwpWakeupSleep();
186 }
187
188 int
189 lwpSleepUntil(time_t until)
190 {
191     int res;
192
193     lwpStatus(LwpCurrent, "sleeping for %ld sec",
194               (long)(until - time(NULL)));
195     LwpCurrent->runtime = until;
196     if (LwpMaxfd == 0 && LwpDelayq.head == 0) {
197         /* select process is sleeping until first waiter arrives */
198         lwpReady(LwpSelProc);
199     }
200     lwpAddTail(&LwpDelayq, LwpCurrent);
201     lwpReschedule();
202     res = LwpCurrent->runtime ? 0 : -1;
203     LwpCurrent->runtime = (time_t)-1;
204     return res;
205 }
206
207 /*ARGSUSED*/
208 void
209 lwpSelect(void *arg)
210 {
211     struct lwpProc *us = LwpCurrent;
212     fd_set readmask;
213     fd_set writemask;
214     int n;
215     int fd;
216     time_t now;
217     time_t delta;
218     struct lwpProc *proc;
219     struct timeval tv;
220
221     lwpStatus(us, "starting select loop");
222     FD_ZERO(&readmask);
223     FD_ZERO(&writemask);
224     while (1) {
225         while (1) {
226             if (LwpNfds)
227                 break;
228             if (LwpDelayq.head)
229                 break;
230             /* wait for someone to lwpSleepFd or lwpSleepUntil */
231             LwpMaxfd = 0;
232             lwpStatus(us, "no fds or sleepers, waiting");
233             lwpReschedule();
234         }
235         tv.tv_sec = 1000000;
236         tv.tv_usec = 0;
237         if (LwpDelayq.head) {
238             time(&now);
239             proc = LwpDelayq.head;
240             for (; proc != 0; proc = proc->next) {
241                 delta = proc->runtime - now;
242                 if (delta < tv.tv_sec)
243                     tv.tv_sec = delta;
244             }
245             if (tv.tv_sec < 0)
246                 tv.tv_sec = 0;
247         }
248         lwpStatus(us, "selecting; sleep %ld secs", tv.tv_sec);
249
250         memcpy(&readmask, &LwpReadfds, sizeof(fd_set));
251         memcpy(&writemask, &LwpWritefds, sizeof(fd_set));
252         n = select(LwpMaxfd + 1, &readmask, &writemask, NULL, &tv);
253         if (n < 0) {
254             if (errno != EINTR) {
255                 logerror("select failed (%s)", strerror(errno));
256                 exit(1);
257             }
258             /* go handle the signal */
259             lwpReady(us);
260             lwpReschedule();
261             continue;
262         }
263
264         if (n > 0) {
265             /* file descriptor activity */
266             for (fd = 0; fd <= LwpMaxfd; fd++) {
267                 if (LwpFdwait[fd] == 0)
268                     continue;
269                 if (FD_ISSET(fd, &readmask)) {
270                     lwpStatus(LwpFdwait[fd], "input ready");
271                     LwpFdwait[fd]->fd_ready = 1;
272                     lwpWakeupFd(LwpFdwait[fd]);
273                     continue;
274                 }
275                 if (FD_ISSET(fd, &writemask)) {
276                     lwpStatus(LwpFdwait[fd], "output ready");
277                     LwpFdwait[fd]->fd_ready = 1;
278                     lwpWakeupFd(LwpFdwait[fd]);
279                     continue;
280                 }
281             }
282         }
283         lwpWakeupSleep();
284         lwpStatus(us, "fd dispatch completed");
285         lwpReady(LwpCurrent);
286         lwpReschedule();
287     }
288     /*NOTREACHED*/
289 }