|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* |
|
7 *------------------------------------------------------------------------ |
|
8 * File: uxwrap.c |
|
9 * |
|
10 * Our wrapped versions of the Unix select() and poll() system calls. |
|
11 * |
|
12 *------------------------------------------------------------------------ |
|
13 */ |
|
14 |
|
15 #include "primpl.h" |
|
16 |
|
17 #if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) || defined(QNX) |
|
18 /* Do not wrap select() and poll(). */ |
|
19 #else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ |
|
20 /* The include files for select() */ |
|
21 #ifdef IRIX |
|
22 #include <unistd.h> |
|
23 #include <bstring.h> |
|
24 #endif |
|
25 |
|
26 #include <string.h> |
|
27 #include <sys/types.h> |
|
28 #include <sys/time.h> |
|
29 |
|
30 #define ZAP_SET(_to, _width) \ |
|
31 PR_BEGIN_MACRO \ |
|
32 memset(_to, 0, \ |
|
33 ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \ |
|
34 * sizeof(int) \ |
|
35 ); \ |
|
36 PR_END_MACRO |
|
37 |
|
38 /* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */ |
|
39 static int _pr_xt_hack_fd = -1; |
|
40 |
|
41 int PR_XGetXtHackFD(void) |
|
42 { |
|
43 int fds[2]; |
|
44 |
|
45 if (_pr_xt_hack_fd == -1) { |
|
46 if (!pipe(fds)) { |
|
47 _pr_xt_hack_fd = fds[0]; |
|
48 } |
|
49 } |
|
50 return _pr_xt_hack_fd; |
|
51 } |
|
52 |
|
53 static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0; |
|
54 |
|
55 void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void)) |
|
56 { |
|
57 _pr_xt_hack_okayToReleaseXLock = fn; |
|
58 } |
|
59 |
|
60 |
|
61 /* |
|
62 *----------------------------------------------------------------------- |
|
63 * select() -- |
|
64 * |
|
65 * Wrap up the select system call so that we can deschedule |
|
66 * a thread that tries to wait for i/o. |
|
67 * |
|
68 *----------------------------------------------------------------------- |
|
69 */ |
|
70 |
|
71 #if defined(HPUX9) |
|
72 int select(size_t width, int *rl, int *wl, int *el, const struct timeval *tv) |
|
73 #elif defined(AIX_RENAME_SELECT) |
|
74 int wrap_select(unsigned long width, void *rl, void *wl, void *el, |
|
75 struct timeval *tv) |
|
76 #elif defined(_PR_SELECT_CONST_TIMEVAL) |
|
77 int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, |
|
78 const struct timeval *tv) |
|
79 #else |
|
80 int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *tv) |
|
81 #endif |
|
82 { |
|
83 int osfd; |
|
84 _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; |
|
85 PRInt32 pdcnt; |
|
86 PRIntervalTime timeout; |
|
87 int retVal; |
|
88 #if defined(HPUX9) || defined(AIX_RENAME_SELECT) |
|
89 fd_set *rd = (fd_set*) rl; |
|
90 fd_set *wr = (fd_set*) wl; |
|
91 fd_set *ex = (fd_set*) el; |
|
92 #endif |
|
93 |
|
94 #if 0 |
|
95 /* |
|
96 * Easy special case: zero timeout. Simply call the native |
|
97 * select() with no fear of blocking. |
|
98 */ |
|
99 if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) { |
|
100 #if defined(HPUX9) || defined(AIX_RENAME_SELECT) |
|
101 return _MD_SELECT(width, rl, wl, el, tv); |
|
102 #else |
|
103 return _MD_SELECT(width, rd, wr, ex, tv); |
|
104 #endif |
|
105 } |
|
106 #endif |
|
107 |
|
108 if (!_pr_initialized) { |
|
109 _PR_ImplicitInitialization(); |
|
110 } |
|
111 |
|
112 #ifndef _PR_LOCAL_THREADS_ONLY |
|
113 if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { |
|
114 return _MD_SELECT(width, rd, wr, ex, tv); |
|
115 } |
|
116 #endif |
|
117 |
|
118 if (width < 0 || width > FD_SETSIZE) { |
|
119 errno = EINVAL; |
|
120 return -1; |
|
121 } |
|
122 |
|
123 /* Compute timeout */ |
|
124 if (tv) { |
|
125 /* |
|
126 * These acceptable ranges for t_sec and t_usec are taken |
|
127 * from the select() man pages. |
|
128 */ |
|
129 if (tv->tv_sec < 0 || tv->tv_sec > 100000000 |
|
130 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) { |
|
131 errno = EINVAL; |
|
132 return -1; |
|
133 } |
|
134 |
|
135 /* Convert microseconds to ticks */ |
|
136 timeout = PR_MicrosecondsToInterval(1000000*tv->tv_sec + tv->tv_usec); |
|
137 } else { |
|
138 /* tv being a NULL pointer means blocking indefinitely */ |
|
139 timeout = PR_INTERVAL_NO_TIMEOUT; |
|
140 } |
|
141 |
|
142 /* Check for no descriptors case (just doing a timeout) */ |
|
143 if ((!rd && !wr && !ex) || !width) { |
|
144 PR_Sleep(timeout); |
|
145 return 0; |
|
146 } |
|
147 |
|
148 /* |
|
149 * Set up for PR_Poll(). The PRPollDesc array is allocated |
|
150 * dynamically. If this turns out to have high performance |
|
151 * penalty, one can change to use a large PRPollDesc array |
|
152 * on the stack, and allocate dynamically only when it turns |
|
153 * out to be not large enough. |
|
154 * |
|
155 * I allocate an array of size 'width', which is the maximum |
|
156 * number of fds we may need to poll. |
|
157 */ |
|
158 unixpds = (_PRUnixPollDesc *) PR_CALLOC(width * sizeof(_PRUnixPollDesc)); |
|
159 if (!unixpds) { |
|
160 errno = ENOMEM; |
|
161 return -1; |
|
162 } |
|
163 |
|
164 pdcnt = 0; |
|
165 unixpd = unixpds; |
|
166 for (osfd = 0; osfd < width; osfd++) { |
|
167 int in_flags = 0; |
|
168 if (rd && FD_ISSET(osfd, rd)) { |
|
169 in_flags |= _PR_UNIX_POLL_READ; |
|
170 } |
|
171 if (wr && FD_ISSET(osfd, wr)) { |
|
172 in_flags |= _PR_UNIX_POLL_WRITE; |
|
173 } |
|
174 if (ex && FD_ISSET(osfd, ex)) { |
|
175 in_flags |= _PR_UNIX_POLL_EXCEPT; |
|
176 } |
|
177 if (in_flags) { |
|
178 unixpd->osfd = osfd; |
|
179 unixpd->in_flags = in_flags; |
|
180 unixpd->out_flags = 0; |
|
181 unixpd++; |
|
182 pdcnt++; |
|
183 } |
|
184 } |
|
185 |
|
186 /* |
|
187 * see comments in mozilla/cmd/xfe/mozilla.c (look for |
|
188 * "PR_XGetXtHackFD") |
|
189 */ |
|
190 { |
|
191 int needToLockXAgain; |
|
192 |
|
193 needToLockXAgain = 0; |
|
194 if (rd && (_pr_xt_hack_fd != -1) |
|
195 && FD_ISSET(_pr_xt_hack_fd, rd) && PR_XIsLocked() |
|
196 && (!_pr_xt_hack_okayToReleaseXLock |
|
197 || _pr_xt_hack_okayToReleaseXLock())) { |
|
198 PR_XUnlock(); |
|
199 needToLockXAgain = 1; |
|
200 } |
|
201 |
|
202 /* This is the potentially blocking step */ |
|
203 retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout); |
|
204 |
|
205 if (needToLockXAgain) { |
|
206 PR_XLock(); |
|
207 } |
|
208 } |
|
209 |
|
210 if (retVal > 0) { |
|
211 /* Compute select results */ |
|
212 if (rd) ZAP_SET(rd, width); |
|
213 if (wr) ZAP_SET(wr, width); |
|
214 if (ex) ZAP_SET(ex, width); |
|
215 |
|
216 /* |
|
217 * The return value can be either the number of ready file |
|
218 * descriptors or the number of set bits in the three fd_set's. |
|
219 */ |
|
220 retVal = 0; /* we're going to recompute */ |
|
221 eunixpd = unixpds + pdcnt; |
|
222 for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { |
|
223 if (unixpd->out_flags) { |
|
224 int nbits = 0; /* The number of set bits on for this fd */ |
|
225 |
|
226 if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { |
|
227 errno = EBADF; |
|
228 PR_LOG(_pr_io_lm, PR_LOG_ERROR, |
|
229 ("select returns EBADF for %d", unixpd->osfd)); |
|
230 retVal = -1; |
|
231 break; |
|
232 } |
|
233 /* |
|
234 * If a socket has a pending error, it is considered |
|
235 * both readable and writable. (See W. Richard Stevens, |
|
236 * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3, |
|
237 * pp. 153-154.) We also consider a socket readable if |
|
238 * it has a hangup condition. |
|
239 */ |
|
240 if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ) |
|
241 && (unixpd->out_flags & (_PR_UNIX_POLL_READ |
|
242 | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) { |
|
243 FD_SET(unixpd->osfd, rd); |
|
244 nbits++; |
|
245 } |
|
246 if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) |
|
247 && (unixpd->out_flags & (_PR_UNIX_POLL_WRITE |
|
248 | _PR_UNIX_POLL_ERR))) { |
|
249 FD_SET(unixpd->osfd, wr); |
|
250 nbits++; |
|
251 } |
|
252 if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) |
|
253 && (unixpd->out_flags & PR_POLL_EXCEPT)) { |
|
254 FD_SET(unixpd->osfd, ex); |
|
255 nbits++; |
|
256 } |
|
257 PR_ASSERT(nbits > 0); |
|
258 #if defined(HPUX) || defined(SOLARIS) || defined(OSF1) || defined(AIX) |
|
259 retVal += nbits; |
|
260 #else /* IRIX */ |
|
261 retVal += 1; |
|
262 #endif |
|
263 } |
|
264 } |
|
265 } |
|
266 |
|
267 PR_ASSERT(tv || retVal != 0); |
|
268 PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal)); |
|
269 PR_DELETE(unixpds); |
|
270 |
|
271 return retVal; |
|
272 } |
|
273 |
|
274 /* |
|
275 * Redefine poll, when supported on platforms, for local threads |
|
276 */ |
|
277 |
|
278 /* |
|
279 * I am commenting out the poll() wrapper for Linux for now |
|
280 * because it is difficult to define _MD_POLL that works on all |
|
281 * Linux varieties. People reported that glibc 2.0.7 on Debian |
|
282 * 2.0 Linux machines doesn't have the __syscall_poll symbol |
|
283 * defined. (WTC 30 Nov. 1998) |
|
284 */ |
|
285 #if defined(_PR_POLL_AVAILABLE) && !defined(LINUX) |
|
286 |
|
287 /* |
|
288 *----------------------------------------------------------------------- |
|
289 * poll() -- |
|
290 * |
|
291 * RETURN VALUES: |
|
292 * -1: fails, errno indicates the error. |
|
293 * 0: timed out, the revents bitmasks are not set. |
|
294 * positive value: the number of file descriptors for which poll() |
|
295 * has set the revents bitmask. |
|
296 * |
|
297 *----------------------------------------------------------------------- |
|
298 */ |
|
299 |
|
300 #include <poll.h> |
|
301 |
|
302 #if defined(AIX_RENAME_SELECT) |
|
303 int wrap_poll(void *listptr, unsigned long nfds, long timeout) |
|
304 #elif (defined(AIX) && !defined(AIX_RENAME_SELECT)) |
|
305 int poll(void *listptr, unsigned long nfds, long timeout) |
|
306 #elif defined(OSF1) || (defined(HPUX) && !defined(HPUX9)) |
|
307 int poll(struct pollfd filedes[], unsigned int nfds, int timeout) |
|
308 #elif defined(HPUX9) |
|
309 int poll(struct pollfd filedes[], int nfds, int timeout) |
|
310 #elif defined(NETBSD) |
|
311 int poll(struct pollfd *filedes, nfds_t nfds, int timeout) |
|
312 #elif defined(OPENBSD) |
|
313 int poll(struct pollfd filedes[], nfds_t nfds, int timeout) |
|
314 #elif defined(FREEBSD) |
|
315 int poll(struct pollfd *filedes, unsigned nfds, int timeout) |
|
316 #else |
|
317 int poll(struct pollfd *filedes, unsigned long nfds, int timeout) |
|
318 #endif |
|
319 { |
|
320 #ifdef AIX |
|
321 struct pollfd *filedes = (struct pollfd *) listptr; |
|
322 #endif |
|
323 struct pollfd *pfd, *epfd; |
|
324 _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; |
|
325 PRIntervalTime ticks; |
|
326 PRInt32 pdcnt; |
|
327 int ready; |
|
328 |
|
329 /* |
|
330 * Easy special case: zero timeout. Simply call the native |
|
331 * poll() with no fear of blocking. |
|
332 */ |
|
333 if (timeout == 0) { |
|
334 #if defined(AIX) |
|
335 return _MD_POLL(listptr, nfds, timeout); |
|
336 #else |
|
337 return _MD_POLL(filedes, nfds, timeout); |
|
338 #endif |
|
339 } |
|
340 |
|
341 if (!_pr_initialized) { |
|
342 _PR_ImplicitInitialization(); |
|
343 } |
|
344 |
|
345 #ifndef _PR_LOCAL_THREADS_ONLY |
|
346 if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { |
|
347 return _MD_POLL(filedes, nfds, timeout); |
|
348 } |
|
349 #endif |
|
350 |
|
351 /* We do not support the pollmsg structures on AIX */ |
|
352 #ifdef AIX |
|
353 PR_ASSERT((nfds & 0xff00) == 0); |
|
354 #endif |
|
355 |
|
356 if (timeout < 0 && timeout != -1) { |
|
357 errno = EINVAL; |
|
358 return -1; |
|
359 } |
|
360 |
|
361 /* Convert timeout from miliseconds to ticks */ |
|
362 if (timeout == -1) { |
|
363 ticks = PR_INTERVAL_NO_TIMEOUT; |
|
364 } else { |
|
365 ticks = PR_MillisecondsToInterval(timeout); |
|
366 } |
|
367 |
|
368 /* Check for no descriptor case (just do a timeout) */ |
|
369 if (nfds == 0) { |
|
370 PR_Sleep(ticks); |
|
371 return 0; |
|
372 } |
|
373 |
|
374 unixpds = (_PRUnixPollDesc *) |
|
375 PR_MALLOC(nfds * sizeof(_PRUnixPollDesc)); |
|
376 if (NULL == unixpds) { |
|
377 errno = EAGAIN; |
|
378 return -1; |
|
379 } |
|
380 |
|
381 pdcnt = 0; |
|
382 epfd = filedes + nfds; |
|
383 unixpd = unixpds; |
|
384 for (pfd = filedes; pfd < epfd; pfd++) { |
|
385 /* |
|
386 * poll() ignores negative fd's. |
|
387 */ |
|
388 if (pfd->fd >= 0) { |
|
389 unixpd->osfd = pfd->fd; |
|
390 #ifdef _PR_USE_POLL |
|
391 unixpd->in_flags = pfd->events; |
|
392 #else |
|
393 /* |
|
394 * Map the poll events to one of the three that can be |
|
395 * represented by the select fd_sets: |
|
396 * POLLIN, POLLRDNORM ===> readable |
|
397 * POLLOUT, POLLWRNORM ===> writable |
|
398 * POLLPRI, POLLRDBAND ===> exception |
|
399 * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) |
|
400 * are ignored. |
|
401 * |
|
402 * The output events POLLERR and POLLHUP are never turned on. |
|
403 * POLLNVAL may be turned on. |
|
404 */ |
|
405 unixpd->in_flags = 0; |
|
406 if (pfd->events & (POLLIN |
|
407 #ifdef POLLRDNORM |
|
408 | POLLRDNORM |
|
409 #endif |
|
410 )) { |
|
411 unixpd->in_flags |= _PR_UNIX_POLL_READ; |
|
412 } |
|
413 if (pfd->events & (POLLOUT |
|
414 #ifdef POLLWRNORM |
|
415 | POLLWRNORM |
|
416 #endif |
|
417 )) { |
|
418 unixpd->in_flags |= _PR_UNIX_POLL_WRITE; |
|
419 } |
|
420 if (pfd->events & (POLLPRI |
|
421 #ifdef POLLRDBAND |
|
422 | POLLRDBAND |
|
423 #endif |
|
424 )) { |
|
425 unixpd->in_flags |= PR_POLL_EXCEPT; |
|
426 } |
|
427 #endif /* _PR_USE_POLL */ |
|
428 unixpd->out_flags = 0; |
|
429 unixpd++; |
|
430 pdcnt++; |
|
431 } |
|
432 } |
|
433 |
|
434 ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks); |
|
435 if (-1 == ready) { |
|
436 if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { |
|
437 errno = EINTR; /* XXX we aren't interrupted by a signal, but... */ |
|
438 } else { |
|
439 errno = PR_GetOSError(); |
|
440 } |
|
441 } |
|
442 if (ready <= 0) { |
|
443 goto done; |
|
444 } |
|
445 |
|
446 /* |
|
447 * Copy the out_flags from the _PRUnixPollDesc structures to the |
|
448 * user's pollfd structures and free the allocated memory |
|
449 */ |
|
450 unixpd = unixpds; |
|
451 for (pfd = filedes; pfd < epfd; pfd++) { |
|
452 pfd->revents = 0; |
|
453 if (pfd->fd >= 0) { |
|
454 #ifdef _PR_USE_POLL |
|
455 pfd->revents = unixpd->out_flags; |
|
456 #else |
|
457 if (0 != unixpd->out_flags) { |
|
458 if (unixpd->out_flags & _PR_UNIX_POLL_READ) { |
|
459 if (pfd->events & POLLIN) { |
|
460 pfd->revents |= POLLIN; |
|
461 } |
|
462 #ifdef POLLRDNORM |
|
463 if (pfd->events & POLLRDNORM) { |
|
464 pfd->revents |= POLLRDNORM; |
|
465 } |
|
466 #endif |
|
467 } |
|
468 if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) { |
|
469 if (pfd->events & POLLOUT) { |
|
470 pfd->revents |= POLLOUT; |
|
471 } |
|
472 #ifdef POLLWRNORM |
|
473 if (pfd->events & POLLWRNORM) { |
|
474 pfd->revents |= POLLWRNORM; |
|
475 } |
|
476 #endif |
|
477 } |
|
478 if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) { |
|
479 if (pfd->events & POLLPRI) { |
|
480 pfd->revents |= POLLPRI; |
|
481 } |
|
482 #ifdef POLLRDBAND |
|
483 if (pfd->events & POLLRDBAND) { |
|
484 pfd->revents |= POLLRDBAND; |
|
485 } |
|
486 #endif |
|
487 } |
|
488 if (unixpd->out_flags & _PR_UNIX_POLL_ERR) { |
|
489 pfd->revents |= POLLERR; |
|
490 } |
|
491 if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { |
|
492 pfd->revents |= POLLNVAL; |
|
493 } |
|
494 if (unixpd->out_flags & _PR_UNIX_POLL_HUP) { |
|
495 pfd->revents |= POLLHUP; |
|
496 } |
|
497 } |
|
498 #endif /* _PR_USE_POLL */ |
|
499 unixpd++; |
|
500 } |
|
501 } |
|
502 |
|
503 done: |
|
504 PR_DELETE(unixpds); |
|
505 return ready; |
|
506 } |
|
507 |
|
508 #endif /* !defined(LINUX) */ |
|
509 |
|
510 #endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ |
|
511 |
|
512 /* uxwrap.c */ |
|
513 |