| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |