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