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