Commit | Line | Data |
---|---|---|
d90ca38d | 1 | /* Copyright (C) 1997, 1998 Free Software Foundation, Inc. |
3666451e MD |
2 | * |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License as published by | |
5 | * the Free Software Foundation; either version 2, or (at your option) | |
6 | * any later version. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this software; see the file COPYING. If not, write to | |
15 | * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |
16 | * Boston, MA 02111-1307 USA | |
17 | * | |
18 | * As a special exception, the Free Software Foundation gives permission | |
19 | * for additional uses of the text contained in its release of GUILE. | |
20 | * | |
21 | * The exception is that, if you link the GUILE library with other files | |
22 | * to produce an executable, this does not by itself cause the | |
23 | * resulting executable to be covered by the GNU General Public License. | |
24 | * Your use of that executable is in no way restricted on account of | |
25 | * linking the GUILE library code into it. | |
26 | * | |
27 | * This exception does not however invalidate any other reasons why | |
28 | * the executable file might be covered by the GNU General Public License. | |
29 | * | |
30 | * This exception applies only to the code released by the | |
31 | * Free Software Foundation under the name GUILE. If you copy | |
32 | * code from other Free Software Foundation releases into a copy of | |
33 | * GUILE, as the General Public License permits, the exception does | |
34 | * not apply to the code that you add in this way. To avoid misleading | |
35 | * anyone as to the status of such modified files, you must delete | |
36 | * this exception notice from them. | |
37 | * | |
38 | * If you write modifications of your own for GUILE, it is your choice | |
39 | * whether to permit this exception to apply to your modifications. | |
40 | * If you do not wish that, delete this exception notice. */ | |
41 | \f | |
42 | #include <stdio.h> | |
43 | #include <limits.h> | |
44 | #include <string.h> | |
45 | ||
46 | #include "_scm.h" | |
47 | ||
756fa4f8 TP |
48 | /* |
49 | * iselect.c is linked with Guile only when threads are in use. However, | |
50 | * when threads are *not* in use, the `make depend' mechanism will try | |
51 | * to process this file anyway and get tangled up in iselect.h and | |
7f0f3eaa | 52 | * coop_threads.h. Therefore, we use the USE_THREADS macro (which is |
756fa4f8 TP |
53 | * otherwise redundant for this file) to prevent `make depend' from |
54 | * failing. | |
55 | */ | |
56 | ||
e085f7a6 | 57 | #ifdef USE_THREADS |
3666451e MD |
58 | #include "iselect.h" |
59 | #include "coop-threads.h" | |
e085f7a6 | 60 | #endif |
3666451e | 61 | |
4d3bacdd | 62 | #ifdef MISSING_BZERO_DECL |
b8ff5fe9 MD |
63 | extern void bzero (void *, size_t); |
64 | #endif | |
65 | ||
3666451e MD |
66 | \f |
67 | ||
a48b6916 | 68 | /* COOP queue macros */ |
3666451e MD |
69 | #define QEMPTYP(q) (q.t.next == &q.t) |
70 | #define QFIRST(q) (q.t.next) | |
71 | ||
a48b6916 | 72 | /* These macros count the number of bits in a word. */ |
3666451e | 73 | #define SCM_BITS_PER_LONG (8 * sizeof (unsigned long)) |
95f44da9 MD |
74 | /* Use LONG_MAX instead of ULONG_MAX here since not all systems define |
75 | ULONG_MAX */ | |
76 | #if LONG_MAX >> 16 == 0 | |
3666451e MD |
77 | #define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\ |
78 | + bc[((unsigned char *)(p))[1]]) | |
95f44da9 | 79 | #elif LONG_MAX >> 32 == 0 |
3666451e MD |
80 | #define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\ |
81 | + bc[((unsigned char *)(p))[1]]\ | |
82 | + bc[((unsigned char *)(p))[2]]\ | |
83 | + bc[((unsigned char *)(p))[3]]) | |
95f44da9 | 84 | #elif LONG_MAX >> 64 == 0 |
3666451e MD |
85 | #define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\ |
86 | + bc[((unsigned char *)(p))[1]]\ | |
87 | + bc[((unsigned char *)(p))[2]]\ | |
88 | + bc[((unsigned char *)(p))[3]]\ | |
89 | + bc[((unsigned char *)(p))[4]]\ | |
90 | + bc[((unsigned char *)(p))[5]]\ | |
91 | + bc[((unsigned char *)(p))[6]]\ | |
92 | + bc[((unsigned char *)(p))[7]]) | |
93 | #endif | |
94 | ||
95 | #ifdef HAVE_BZERO | |
96 | #define FD_ZERO_N(pos, n) bzero ((pos), (n)) | |
97 | #else | |
98 | #define FD_ZERO_N(pos, n) memset ((void *) (pos), 0, (n)) | |
99 | #endif | |
100 | ||
101 | typedef unsigned long *ulongptr; | |
102 | ||
103 | static char bc[256]; /* Bit counting array. bc[x] is the number of | |
104 | bits in x. */ | |
105 | ||
c69dfa65 MD |
106 | int scm_I_am_dead; |
107 | ||
a48b6916 MD |
108 | /* This flag indicates that several threads are waiting on the same |
109 | file descriptor. When this is the case, the common fd sets are | |
110 | updated in a more inefficient way. */ | |
111 | int collisionp; | |
112 | ||
113 | /* These are the common fd sets. When new select calls are made, | |
114 | those sets are merged into these. */ | |
3666451e MD |
115 | int gnfds; |
116 | SELECT_TYPE greadfds; | |
117 | SELECT_TYPE gwritefds; | |
118 | SELECT_TYPE gexceptfds; | |
a48b6916 MD |
119 | |
120 | /* These are the result sets. They are used when we call OS select. | |
121 | We couldn't use the common fd sets above, since that would destroy | |
122 | them. */ | |
3666451e MD |
123 | SELECT_TYPE rreadfds; |
124 | SELECT_TYPE rwritefds; | |
125 | SELECT_TYPE rexceptfds; | |
a48b6916 MD |
126 | |
127 | /* Constant timeval struct representing a zero timeout which we use | |
128 | when polling. */ | |
3666451e MD |
129 | static struct timeval timeout0; |
130 | ||
131 | /* Insert thread t into the ordered queue q. | |
132 | q is ordered after wakeup_time. Threads which aren't sleeping but | |
133 | waiting for I/O go last into the queue. */ | |
134 | static void | |
135 | coop_timeout_qinsert (coop_q_t *q, coop_t *t) | |
136 | { | |
137 | coop_t *pred = &q->t; | |
138 | int sec = t->wakeup_time.tv_sec; | |
139 | int usec = t->wakeup_time.tv_usec; | |
140 | while (pred->next != &q->t | |
141 | && pred->next->timeoutp | |
142 | && (pred->next->wakeup_time.tv_sec < sec | |
143 | || (pred->next->wakeup_time.tv_sec == sec | |
144 | && pred->next->wakeup_time.tv_usec < usec))) | |
145 | pred = pred->next; | |
146 | t->next = pred->next; | |
147 | pred->next = t; | |
148 | if (t->next == &q->t) | |
149 | q->tail = t; | |
150 | } | |
151 | ||
3666451e | 152 | /* As select, but doesn't destroy the file descriptor sets passed as |
a48b6916 | 153 | arguments. The results are stored into the result sets. */ |
3666451e MD |
154 | static int |
155 | safe_select (int nfds, | |
156 | SELECT_TYPE *readfds, | |
157 | SELECT_TYPE *writefds, | |
158 | SELECT_TYPE *exceptfds, | |
159 | struct timeval *timeout) | |
160 | { | |
161 | int n = (nfds + 7) / 8; | |
162 | /* Copy file descriptor sets to result area */ | |
163 | if (readfds == NULL) | |
164 | FD_ZERO (&rreadfds); | |
165 | else | |
166 | { | |
167 | memcpy (&rreadfds, readfds, n); | |
168 | FD_ZERO_N ((char *) &rreadfds + n, SELECT_SET_SIZE / 8 - n); | |
169 | } | |
170 | if (writefds == NULL) | |
171 | FD_ZERO (&rwritefds); | |
172 | else | |
173 | { | |
174 | memcpy (&rwritefds, writefds, n); | |
175 | FD_ZERO_N ((char *) &rwritefds + n, SELECT_SET_SIZE / 8 - n); | |
176 | } | |
177 | if (exceptfds == NULL) | |
178 | FD_ZERO (&rexceptfds); | |
179 | else | |
180 | { | |
181 | memcpy (&rexceptfds, exceptfds, n); | |
182 | FD_ZERO_N ((char *) &rexceptfds + n, SELECT_SET_SIZE / 8 - n); | |
183 | } | |
184 | return select (nfds, &rreadfds, &rwritefds, &rexceptfds, timeout); | |
185 | } | |
186 | ||
a48b6916 MD |
187 | /* Merge new file descriptor sets into the common sets. */ |
188 | static void | |
189 | add_fd_sets (coop_t *t) | |
3666451e | 190 | { |
a48b6916 MD |
191 | int n = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG; |
192 | int i; | |
193 | ||
194 | /* Detect if the fd sets of the thread have any bits in common with | |
195 | the rest of the waiting threads. If that is so, set the | |
196 | collision flag. This causes a more time consuming handling of | |
197 | the common fd sets---they need to recalculated every time a | |
198 | thread wakes up. */ | |
199 | if (!collisionp) | |
200 | for (i = 0; i < n; ++i) | |
201 | if ((t->readfds != NULL | |
202 | && (((ulongptr) t->readfds)[i] & ((ulongptr) &greadfds)[i]) != 0) | |
203 | || (t->writefds != NULL | |
204 | && ((((ulongptr) t->writefds)[i] & ((ulongptr) &gwritefds)[i]) | |
205 | != 0)) | |
206 | || (t->exceptfds != NULL | |
207 | && ((((ulongptr) t->exceptfds)[i] & ((ulongptr) &gexceptfds)[i]) | |
208 | != 0))) | |
209 | { | |
210 | collisionp = 1; | |
211 | break; | |
212 | } | |
213 | ||
214 | /* We recalculate nfds below. The cost for this can be paid back | |
215 | with a great bonus since many programs are lazy with the nfds | |
216 | arg. Many even pass 1024 when using one of the lowest fd:s! | |
217 | ||
218 | We approach from above, checking for non-zero bits. As soon as | |
219 | we have determined the value of nfds, we jump down to code below | |
220 | which concludes the updating of the common sets. */ | |
221 | t->nfds = 0; | |
222 | i = n; | |
3666451e MD |
223 | while (i > 0) |
224 | { | |
225 | --i; | |
a48b6916 | 226 | if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0) |
3666451e | 227 | { |
a48b6916 | 228 | ((ulongptr) &greadfds)[i] |= ((ulongptr) t->readfds)[i]; |
3666451e | 229 | n = (i + 1) * SCM_BITS_PER_LONG; |
a48b6916 | 230 | t->nfds = n; |
3666451e MD |
231 | if (n > gnfds) |
232 | gnfds = n; | |
233 | goto cont_read; | |
234 | } | |
a48b6916 | 235 | if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0) |
3666451e | 236 | { |
a48b6916 | 237 | ((ulongptr) &gwritefds)[i] |= ((ulongptr) t->writefds)[i]; |
3666451e | 238 | n = (i + 1) * SCM_BITS_PER_LONG; |
a48b6916 | 239 | t->nfds = n; |
3666451e MD |
240 | if (n > gnfds) |
241 | gnfds = n; | |
242 | goto cont_write; | |
243 | } | |
a48b6916 | 244 | if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0) |
3666451e | 245 | { |
a48b6916 | 246 | ((ulongptr) &gexceptfds)[i] |= ((ulongptr) t->exceptfds)[i]; |
3666451e | 247 | n = (i + 1) * SCM_BITS_PER_LONG; |
a48b6916 | 248 | t->nfds = n; |
3666451e MD |
249 | if (n > gnfds) |
250 | gnfds = n; | |
251 | goto cont_except; | |
252 | } | |
253 | } | |
a48b6916 MD |
254 | return; |
255 | ||
256 | /* nfds is now determined. Just finish updating the common sets. */ | |
3666451e MD |
257 | while (i > 0) |
258 | { | |
259 | --i; | |
a48b6916 MD |
260 | if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0) |
261 | ((ulongptr) &greadfds)[i] |= ((ulongptr) t->readfds)[i]; | |
3666451e | 262 | cont_read: |
a48b6916 MD |
263 | if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0) |
264 | ((ulongptr) &gwritefds)[i] |= ((ulongptr) t->writefds)[i]; | |
3666451e | 265 | cont_write: |
a48b6916 MD |
266 | if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0) |
267 | ((ulongptr) &gexceptfds)[i] |= ((ulongptr) t->exceptfds)[i]; | |
3666451e MD |
268 | cont_except: |
269 | } | |
3666451e MD |
270 | } |
271 | ||
a48b6916 MD |
272 | /* Update the fd sets pointed to by the thread so that they reflect |
273 | the status of the file descriptors which the thread was interested | |
274 | in. Also clear those bits in the common sets. This function is | |
275 | only called when there are no bit collisions. */ | |
3666451e MD |
276 | static void |
277 | finalize_fd_sets (coop_t *t) | |
278 | { | |
279 | int i = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG; | |
280 | int n_ones = 0; | |
281 | register unsigned long s; | |
a48b6916 | 282 | |
3666451e MD |
283 | if (t->nfds == gnfds) |
284 | { | |
a48b6916 MD |
285 | /* This thread is the one responsible for the current high value |
286 | of gnfds. First do our other jobs while at the same time | |
287 | trying to decrease gnfds. */ | |
3666451e MD |
288 | while (i > 0) |
289 | { | |
290 | --i; | |
291 | if (t->readfds != NULL && (s = ((ulongptr) t->readfds)[i]) != 0) | |
292 | { | |
293 | ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i]; | |
294 | ((ulongptr) &greadfds)[i] &= ~s; | |
295 | n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]); | |
296 | } | |
297 | if (((ulongptr) &greadfds)[i] != 0) | |
298 | { | |
299 | gnfds = (i + 1) * SCM_BITS_PER_LONG; | |
300 | goto cont_read; | |
301 | } | |
302 | if (t->writefds != NULL && (s = ((ulongptr) t->writefds)[i]) != 0) | |
303 | { | |
304 | ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i]; | |
305 | ((ulongptr) &gwritefds)[i] &= ~s; | |
306 | n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]); | |
307 | } | |
308 | if (((ulongptr) &gwritefds)[i] != 0) | |
309 | { | |
310 | gnfds = (i + 1) * SCM_BITS_PER_LONG; | |
311 | goto cont_write; | |
312 | } | |
313 | if (t->exceptfds != NULL && (s = ((ulongptr) t->exceptfds)[i]) != 0) | |
314 | { | |
315 | ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i]; | |
316 | ((ulongptr) &gexceptfds)[i] &= ~s; | |
317 | n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]); | |
318 | } | |
319 | if (((ulongptr) &gexceptfds)[i] != 0) | |
320 | { | |
321 | gnfds = (i + 1) * SCM_BITS_PER_LONG; | |
322 | goto cont_except; | |
323 | } | |
324 | } | |
325 | gnfds = 0; | |
326 | t->retval = n_ones; | |
327 | return; | |
328 | } | |
a48b6916 MD |
329 | |
330 | /* Either this thread wasn't responsible for gnfds or gnfds has been | |
331 | determined. */ | |
3666451e MD |
332 | while (i > 0) |
333 | { | |
334 | --i; | |
335 | if (t->readfds != NULL && (s = ((ulongptr) t->readfds)[i]) != 0) | |
336 | { | |
337 | ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i]; | |
338 | ((ulongptr) &greadfds)[i] &= ~s; | |
339 | n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]); | |
340 | } | |
341 | cont_read: | |
342 | if (t->writefds != NULL && (s = ((ulongptr) t->writefds)[i]) != 0) | |
343 | { | |
344 | ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i]; | |
345 | ((ulongptr) &gwritefds)[i] &= ~s; | |
cafc12ff | 346 | n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]); |
3666451e MD |
347 | } |
348 | cont_write: | |
349 | if (t->exceptfds != NULL && (s = ((ulongptr) t->exceptfds)[i]) != 0) | |
350 | { | |
351 | ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i]; | |
352 | ((ulongptr) &gexceptfds)[i] &= ~s; | |
cafc12ff | 353 | n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]); |
3666451e MD |
354 | } |
355 | cont_except: | |
95f44da9 | 356 | ; |
3666451e MD |
357 | } |
358 | t->retval = n_ones; | |
359 | } | |
360 | ||
a48b6916 MD |
361 | /* Just like finalize_fd_sets except that we don't have to update the |
362 | global fd sets. Those will be recalulated elsewhere. */ | |
363 | static void | |
364 | finalize_fd_sets_lazily (coop_t *t) | |
365 | { | |
366 | int i = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG; | |
367 | int n_ones = 0; | |
a48b6916 MD |
368 | while (i > 0) |
369 | { | |
370 | --i; | |
3237b129 | 371 | if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0) |
a48b6916 MD |
372 | { |
373 | ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i]; | |
374 | n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]); | |
375 | } | |
3237b129 | 376 | if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0) |
a48b6916 MD |
377 | { |
378 | ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i]; | |
379 | n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]); | |
380 | } | |
3237b129 | 381 | if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0) |
a48b6916 MD |
382 | { |
383 | ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i]; | |
384 | n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]); | |
385 | } | |
386 | } | |
387 | t->retval = n_ones; | |
388 | } | |
389 | ||
390 | /* Return first fd with a non-zero bit in any of the result sets. */ | |
3666451e MD |
391 | static int |
392 | first_interesting_fd (void) | |
393 | { | |
394 | int i = 0; | |
395 | SELECT_TYPE *s; | |
396 | while (1) | |
397 | { | |
398 | if (((ulongptr) &rreadfds)[i] != 0) | |
399 | { | |
400 | s = &rreadfds; | |
401 | break; | |
402 | } | |
403 | if (((ulongptr) &rwritefds)[i] != 0) | |
404 | { | |
405 | s = &rwritefds; | |
406 | break; | |
407 | } | |
408 | if (((ulongptr) &rexceptfds)[i] != 0) | |
409 | { | |
410 | s = &rexceptfds; | |
411 | break; | |
412 | } | |
413 | ++i; | |
414 | } | |
415 | i *= SCM_BITS_PER_LONG; | |
416 | while (i < gnfds) | |
417 | { | |
418 | if (FD_ISSET (i, s)) | |
419 | return i; | |
420 | ++i; | |
421 | } | |
422 | fprintf (stderr, "first_interesting_fd: internal error\n"); | |
423 | exit (1); | |
424 | } | |
425 | ||
a48b6916 | 426 | /* Revive all threads with an error status. */ |
c69dfa65 MD |
427 | void |
428 | scm_error_revive_threads (void) | |
3666451e MD |
429 | { |
430 | coop_t *t; | |
431 | ||
432 | while ((t = coop_qget (&coop_global_sleepq)) != NULL) | |
433 | { | |
c44bfbc9 | 434 | t->_errno = errno; |
3666451e | 435 | t->retval = -1; |
c69dfa65 MD |
436 | if (t != coop_global_curr) |
437 | coop_qput (&coop_global_runq, t); | |
3666451e | 438 | } |
c69dfa65 | 439 | collisionp = 0; |
3666451e MD |
440 | gnfds = 0; |
441 | FD_ZERO (&greadfds); | |
442 | FD_ZERO (&gwritefds); | |
443 | FD_ZERO (&gexceptfds); | |
444 | } | |
445 | ||
a48b6916 MD |
446 | /* Given the result of a call to safe_select and the current time, |
447 | try to wake up some threads and return the first one. Return NULL | |
448 | if we couldn't find any. */ | |
3666451e | 449 | static coop_t * |
c69dfa65 | 450 | find_thread (int n, struct timeval *now, int sleepingp) |
3666451e MD |
451 | { |
452 | coop_t *t; | |
453 | int fd; | |
454 | ||
c69dfa65 MD |
455 | if (n < 0) |
456 | /* An error or a signal has occured. Wake all threads. Since we | |
457 | don't care to calculate if there is a sinner we report the | |
458 | error to all of them. */ | |
459 | { | |
460 | scm_error_revive_threads (); | |
461 | if (!scm_I_am_dead) | |
462 | return coop_global_curr; | |
463 | } | |
464 | else if (n == 0) | |
3666451e MD |
465 | { |
466 | while (!QEMPTYP (coop_global_sleepq) | |
467 | && (t = QFIRST (coop_global_sleepq))->timeoutp | |
468 | && (t->wakeup_time.tv_sec < now->tv_sec | |
469 | || (t->wakeup_time.tv_sec == now->tv_sec | |
470 | && t->wakeup_time.tv_usec <= now->tv_usec))) | |
471 | { | |
472 | coop_qget (&coop_global_sleepq); | |
a48b6916 MD |
473 | if (collisionp) |
474 | finalize_fd_sets_lazily (t); | |
475 | else | |
476 | finalize_fd_sets (t); | |
3666451e MD |
477 | coop_qput (&coop_global_runq, t); |
478 | } | |
a48b6916 MD |
479 | if (collisionp) |
480 | { | |
481 | while ((t = coop_qget (&coop_global_sleepq)) != NULL) | |
482 | coop_qput (&coop_tmp_queue, t); | |
483 | goto rebuild_global_fd_sets; | |
484 | } | |
3666451e MD |
485 | } |
486 | else if (n > 0) | |
487 | { | |
488 | /* Find the first interesting file descriptor */ | |
489 | fd = first_interesting_fd (); | |
490 | /* Check the sleeping queue for this file descriptor. | |
491 | Other file descriptors will be handled next time | |
492 | coop_next_runnable_thread is called. */ | |
493 | /* This code is inefficient. We'll improve it later. */ | |
494 | while ((t = coop_qget (&coop_global_sleepq)) != NULL) | |
495 | { | |
496 | if ((t->readfds && FD_ISSET (fd, t->readfds)) | |
497 | || (t->writefds && FD_ISSET (fd, t->writefds)) | |
498 | || (t->exceptfds && FD_ISSET (fd, t->exceptfds)) | |
499 | || (t->timeoutp | |
500 | && (t->wakeup_time.tv_sec < now->tv_sec | |
501 | || (t->wakeup_time.tv_sec == now->tv_sec | |
502 | && t->wakeup_time.tv_usec <= now->tv_usec)))) | |
503 | { | |
a48b6916 MD |
504 | if (collisionp) |
505 | finalize_fd_sets_lazily (t); | |
506 | else | |
507 | finalize_fd_sets (t); | |
3666451e MD |
508 | coop_qput (&coop_global_runq, t); |
509 | } | |
510 | else | |
511 | coop_qput(&coop_tmp_queue, t); | |
512 | } | |
a48b6916 MD |
513 | if (collisionp) |
514 | { | |
515 | rebuild_global_fd_sets: | |
516 | collisionp = 0; | |
517 | gnfds = 0; | |
518 | FD_ZERO (&greadfds); | |
519 | FD_ZERO (&gwritefds); | |
520 | FD_ZERO (&gexceptfds); | |
521 | while ((t = coop_qget (&coop_tmp_queue)) != NULL) | |
522 | { | |
523 | add_fd_sets (t); | |
524 | coop_qput (&coop_global_sleepq, t); | |
525 | } | |
526 | } | |
527 | else | |
528 | { | |
529 | while ((t = coop_qget (&coop_tmp_queue)) != NULL) | |
530 | coop_qput (&coop_global_sleepq, t); | |
531 | } | |
3666451e | 532 | } |
3666451e MD |
533 | |
534 | return coop_qget (&coop_global_runq); | |
535 | } | |
536 | ||
537 | /* Return next runnable thread on the run queue. | |
538 | * First update the queue with possible I/O or timeouts. | |
539 | * If no thread is found, return NULL. | |
540 | */ | |
541 | coop_t * | |
542 | coop_next_runnable_thread () | |
543 | { | |
81e81a5c | 544 | coop_t *t; |
3666451e MD |
545 | struct timeval now; |
546 | int n; | |
547 | ||
548 | /* Just return next thread on the runq if the sleepq is empty. */ | |
549 | if (QEMPTYP (coop_global_sleepq)) | |
c69dfa65 MD |
550 | { |
551 | if (QEMPTYP (coop_global_runq)) | |
552 | return coop_global_curr; | |
553 | else | |
554 | return coop_qget (&coop_global_runq); | |
555 | } | |
3666451e MD |
556 | |
557 | if (gnfds > 0) | |
558 | n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, &timeout0); | |
559 | else | |
560 | n = 0; | |
561 | if (QFIRST (coop_global_sleepq)->timeoutp) | |
562 | { | |
563 | gettimeofday (&now, NULL); | |
c69dfa65 | 564 | t = find_thread (n, &now, 0); |
3666451e | 565 | } |
81e81a5c | 566 | else |
c69dfa65 MD |
567 | t = find_thread (n, 0, 0); |
568 | return t == NULL ? coop_global_curr : t; | |
3666451e MD |
569 | } |
570 | ||
571 | coop_t * | |
572 | coop_wait_for_runnable_thread_now (struct timeval *now) | |
573 | { | |
574 | int n; | |
575 | coop_t *t; | |
576 | ||
577 | if (gnfds > 0) | |
578 | n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, &timeout0); | |
579 | else | |
580 | n = 0; | |
581 | /* Is there any other runnable thread? */ | |
c69dfa65 | 582 | t = find_thread (n, now, 1); |
3666451e MD |
583 | while (t == NULL) |
584 | { | |
585 | /* No. Let the process go to sleep. */ | |
586 | if ((t = QFIRST (coop_global_sleepq))->timeoutp) | |
587 | { | |
588 | now->tv_sec = t->wakeup_time.tv_sec - now->tv_sec; | |
589 | if (now->tv_usec > t->wakeup_time.tv_usec) | |
590 | { | |
591 | --now->tv_sec; | |
592 | now->tv_usec = 1000000 + t->wakeup_time.tv_usec - now->tv_usec; | |
593 | } | |
594 | else | |
595 | now->tv_usec = t->wakeup_time.tv_usec - now->tv_usec; | |
596 | n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, now); | |
597 | } | |
598 | else | |
599 | n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, NULL); | |
600 | gettimeofday (now, NULL); | |
c69dfa65 | 601 | t = find_thread (n, now, 1); |
3666451e MD |
602 | } |
603 | ||
604 | return t; | |
605 | } | |
606 | ||
607 | coop_t * | |
608 | coop_wait_for_runnable_thread () | |
609 | { | |
610 | struct timeval now; | |
611 | ||
612 | if (QEMPTYP (coop_global_sleepq)) | |
c69dfa65 MD |
613 | { |
614 | if (QEMPTYP (coop_global_runq)) | |
615 | return coop_global_curr; | |
616 | else | |
617 | return coop_qget (&coop_global_runq); | |
618 | } | |
3666451e MD |
619 | |
620 | if (QFIRST (coop_global_sleepq)->timeoutp) | |
621 | gettimeofday (&now, NULL); | |
622 | ||
623 | return coop_wait_for_runnable_thread_now (&now); | |
624 | } | |
625 | ||
626 | int | |
627 | scm_internal_select (int nfds, | |
628 | SELECT_TYPE *readfds, | |
629 | SELECT_TYPE *writefds, | |
630 | SELECT_TYPE *exceptfds, | |
631 | struct timeval *timeout) | |
632 | { | |
633 | struct timeval now; | |
634 | coop_t *t, *curr = coop_global_curr; | |
c69dfa65 | 635 | |
3666451e MD |
636 | /* If the timeout is 0, we're polling and can handle it quickly. */ |
637 | if (timeout != NULL | |
638 | && timeout->tv_sec == 0 | |
639 | && timeout->tv_usec == 0) | |
640 | return select (nfds, readfds, writefds, exceptfds, timeout); | |
641 | ||
c69dfa65 | 642 | SCM_DEFER_INTS; |
c718cb07 | 643 | |
3666451e | 644 | /* Add our file descriptor flags to the common set. */ |
3237b129 MD |
645 | curr->nfds = nfds; |
646 | curr->readfds = readfds; | |
647 | curr->writefds = writefds; | |
648 | curr->exceptfds = exceptfds; | |
649 | add_fd_sets (curr); | |
3666451e MD |
650 | |
651 | /* Place ourselves on the sleep queue and get a new thread to run. */ | |
652 | if (timeout == NULL) | |
653 | { | |
654 | curr->timeoutp = 0; | |
655 | coop_qput (&coop_global_sleepq, curr); | |
656 | t = coop_wait_for_runnable_thread (); | |
657 | } | |
658 | else | |
659 | { | |
660 | gettimeofday (&now, NULL); | |
661 | curr->timeoutp = 1; | |
662 | curr->wakeup_time.tv_sec = now.tv_sec + timeout->tv_sec; | |
663 | curr->wakeup_time.tv_usec = now.tv_usec + timeout->tv_usec; | |
664 | if (curr->wakeup_time.tv_usec >= 1000000) | |
665 | { | |
666 | ++curr->wakeup_time.tv_sec; | |
667 | curr->wakeup_time.tv_usec -= 1000000; | |
668 | } | |
669 | /* Insert the current thread at the right place in the sleep queue */ | |
670 | coop_timeout_qinsert (&coop_global_sleepq, curr); | |
671 | t = coop_wait_for_runnable_thread_now (&now); | |
672 | } | |
673 | ||
674 | /* If the new thread is the same as the sleeping thread, do nothing */ | |
c69dfa65 | 675 | if (t != coop_global_curr) |
3666451e MD |
676 | { |
677 | /* Do a context switch. */ | |
678 | coop_global_curr = t; | |
679 | QT_BLOCK (coop_sleephelp, curr, NULL, t->sp); | |
680 | } | |
681 | ||
c69dfa65 MD |
682 | if (coop_global_curr->retval == -1) |
683 | errno = coop_global_curr->_errno; | |
684 | SCM_ALLOW_INTS; | |
685 | return coop_global_curr->retval; | |
3666451e MD |
686 | } |
687 | ||
688 | /* Initialize bit counting array */ | |
689 | static void init_bc (int bit, int i, int n) | |
690 | { | |
691 | if (bit == 0) | |
692 | bc[i] = n; | |
693 | else | |
694 | { | |
695 | init_bc (bit >> 1, i, n); | |
696 | init_bc (bit >> 1, i | bit, n + 1); | |
697 | } | |
698 | } | |
699 | ||
700 | void | |
701 | scm_init_iselect () | |
702 | { | |
a48b6916 MD |
703 | #if 0 /* This is just symbolic */ |
704 | collisionp = 0; | |
3666451e MD |
705 | gnfds = 0; |
706 | FD_ZERO (&greadfds); | |
707 | FD_ZERO (&gwritefds); | |
708 | FD_ZERO (&gexceptfds); | |
709 | timeout0.tv_sec = 0; | |
710 | timeout0.tv_usec = 0; | |
a48b6916 | 711 | #endif |
3666451e MD |
712 | init_bc (0x80, 0, 0); |
713 | #include "iselect.x" | |
714 | } |