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