andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: * This file implements _PR_MD_PR_POLL for Win32. andre@0: */ andre@0: andre@0: /* The default value of FD_SETSIZE is 64. */ andre@0: #define FD_SETSIZE 1024 andre@0: andre@0: #include "primpl.h" andre@0: andre@0: #if !defined(_PR_GLOBAL_THREADS_ONLY) andre@0: andre@0: struct select_data_s { andre@0: PRInt32 status; andre@0: PRInt32 error; andre@0: fd_set *rd, *wt, *ex; andre@0: const struct timeval *tv; andre@0: }; andre@0: andre@0: static void andre@0: _PR_MD_select_thread(void *cdata) andre@0: { andre@0: struct select_data_s *cd = (struct select_data_s *)cdata; andre@0: andre@0: cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv); andre@0: andre@0: if (cd->status == SOCKET_ERROR) { andre@0: cd->error = WSAGetLastError(); andre@0: } andre@0: } andre@0: andre@0: int _PR_NTFiberSafeSelect( andre@0: int nfds, andre@0: fd_set *readfds, andre@0: fd_set *writefds, andre@0: fd_set *exceptfds, andre@0: const struct timeval *timeout) andre@0: { andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: int ready; andre@0: andre@0: if (_PR_IS_NATIVE_THREAD(me)) { andre@0: ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout); andre@0: } andre@0: else andre@0: { andre@0: /* andre@0: ** Creating a new thread on each call!! andre@0: ** I guess web server doesn't use non-block I/O. andre@0: */ andre@0: PRThread *selectThread; andre@0: struct select_data_s data; andre@0: data.status = 0; andre@0: data.error = 0; andre@0: data.rd = readfds; andre@0: data.wt = writefds; andre@0: data.ex = exceptfds; andre@0: data.tv = timeout; andre@0: andre@0: selectThread = PR_CreateThread( andre@0: PR_USER_THREAD, _PR_MD_select_thread, &data, andre@0: PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); andre@0: if (selectThread == NULL) return -1; andre@0: andre@0: PR_JoinThread(selectThread); andre@0: ready = data.status; andre@0: if (ready == SOCKET_ERROR) WSASetLastError(data.error); andre@0: } andre@0: return ready; andre@0: } andre@0: andre@0: #endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */ andre@0: andre@0: PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) andre@0: { andre@0: int ready, err; andre@0: fd_set rd, wt, ex; andre@0: fd_set *rdp, *wtp, *exp; andre@0: int nrd, nwt, nex; andre@0: PRFileDesc *bottom; andre@0: PRPollDesc *pd, *epd; andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: andre@0: struct timeval tv, *tvp = NULL; andre@0: andre@0: if (_PR_PENDING_INTERRUPT(me)) andre@0: { andre@0: me->flags &= ~_PR_INTERRUPT; andre@0: PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: /* andre@0: ** Is it an empty set? If so, just sleep for the timeout and return andre@0: */ andre@0: if (0 == npds) andre@0: { andre@0: PR_Sleep(timeout); andre@0: return 0; andre@0: } andre@0: andre@0: nrd = nwt = nex = 0; andre@0: FD_ZERO(&rd); andre@0: FD_ZERO(&wt); andre@0: FD_ZERO(&ex); andre@0: andre@0: ready = 0; andre@0: for (pd = pds, epd = pd + npds; pd < epd; pd++) andre@0: { andre@0: SOCKET osfd; andre@0: PRInt16 in_flags_read = 0, in_flags_write = 0; andre@0: PRInt16 out_flags_read = 0, out_flags_write = 0; andre@0: andre@0: if ((NULL != pd->fd) && (0 != pd->in_flags)) andre@0: { andre@0: if (pd->in_flags & PR_POLL_READ) andre@0: { andre@0: in_flags_read = (pd->fd->methods->poll)( andre@0: pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE), andre@0: &out_flags_read); andre@0: } andre@0: if (pd->in_flags & PR_POLL_WRITE) andre@0: { andre@0: in_flags_write = (pd->fd->methods->poll)( andre@0: pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ), andre@0: &out_flags_write); andre@0: } andre@0: if ((0 != (in_flags_read & out_flags_read)) andre@0: || (0 != (in_flags_write & out_flags_write))) andre@0: { andre@0: /* this one's ready right now (buffered input) */ andre@0: if (0 == ready) andre@0: { andre@0: /* andre@0: * We will have to return without calling the andre@0: * system poll/select function. So zero the andre@0: * out_flags fields of all the poll descriptors andre@0: * before this one. andre@0: */ andre@0: PRPollDesc *prev; andre@0: for (prev = pds; prev < pd; prev++) andre@0: { andre@0: prev->out_flags = 0; andre@0: } andre@0: } andre@0: ready += 1; andre@0: pd->out_flags = out_flags_read | out_flags_write; andre@0: } andre@0: else andre@0: { andre@0: pd->out_flags = 0; /* pre-condition */ andre@0: /* make sure this is an NSPR supported stack */ andre@0: bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); andre@0: PR_ASSERT(NULL != bottom); /* what to do about that? */ andre@0: if ((NULL != bottom) andre@0: && (_PR_FILEDESC_OPEN == bottom->secret->state)) andre@0: { andre@0: if (0 == ready) andre@0: { andre@0: osfd = (SOCKET) bottom->secret->md.osfd; andre@0: if (in_flags_read & PR_POLL_READ) andre@0: { andre@0: pd->out_flags |= _PR_POLL_READ_SYS_READ; andre@0: FD_SET(osfd, &rd); andre@0: nrd++; andre@0: } andre@0: if (in_flags_read & PR_POLL_WRITE) andre@0: { andre@0: pd->out_flags |= _PR_POLL_READ_SYS_WRITE; andre@0: FD_SET(osfd, &wt); andre@0: nwt++; andre@0: } andre@0: if (in_flags_write & PR_POLL_READ) andre@0: { andre@0: pd->out_flags |= _PR_POLL_WRITE_SYS_READ; andre@0: FD_SET(osfd, &rd); andre@0: nrd++; andre@0: } andre@0: if (in_flags_write & PR_POLL_WRITE) andre@0: { andre@0: pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE; andre@0: FD_SET(osfd, &wt); andre@0: nwt++; andre@0: } andre@0: if (pd->in_flags & PR_POLL_EXCEPT) { andre@0: FD_SET(osfd, &ex); andre@0: nex++; andre@0: } andre@0: } andre@0: } andre@0: else andre@0: { andre@0: if (0 == ready) andre@0: { andre@0: PRPollDesc *prev; andre@0: for (prev = pds; prev < pd; prev++) andre@0: { andre@0: prev->out_flags = 0; andre@0: } andre@0: } andre@0: ready += 1; /* this will cause an abrupt return */ andre@0: pd->out_flags = PR_POLL_NVAL; /* bogii */ andre@0: } andre@0: } andre@0: } andre@0: else andre@0: { andre@0: pd->out_flags = 0; andre@0: } andre@0: } andre@0: andre@0: if (0 != ready) return ready; /* no need to block */ andre@0: andre@0: /* andre@0: * FD_SET does nothing if the fd_set's internal fd_array is full. If andre@0: * nrd, nwt, or nex is greater than FD_SETSIZE, we know FD_SET must andre@0: * have failed to insert an osfd into the corresponding fd_set, and andre@0: * therefore we should fail. andre@0: */ andre@0: if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) { andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: rdp = (0 == nrd) ? NULL : &rd; andre@0: wtp = (0 == nwt) ? NULL : &wt; andre@0: exp = (0 == nex) ? NULL : &ex; andre@0: andre@0: if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) { andre@0: PR_Sleep(timeout); andre@0: return 0; andre@0: } andre@0: andre@0: if (timeout != PR_INTERVAL_NO_TIMEOUT) andre@0: { andre@0: PRInt32 ticksPerSecond = PR_TicksPerSecond(); andre@0: tv.tv_sec = timeout / ticksPerSecond; andre@0: tv.tv_usec = PR_IntervalToMicroseconds( timeout % ticksPerSecond ); andre@0: tvp = &tv; andre@0: } andre@0: andre@0: #if defined(_PR_GLOBAL_THREADS_ONLY) andre@0: ready = _MD_SELECT(0, rdp, wtp, exp, tvp); andre@0: #else andre@0: ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp); andre@0: #endif andre@0: andre@0: /* andre@0: ** Now to unravel the select sets back into the client's poll andre@0: ** descriptor list. Is this possibly an area for pissing away andre@0: ** a few cycles or what? andre@0: */ andre@0: if (ready > 0) andre@0: { andre@0: ready = 0; andre@0: for (pd = pds, epd = pd + npds; pd < epd; pd++) andre@0: { andre@0: PRInt16 out_flags = 0; andre@0: if ((NULL != pd->fd) && (0 != pd->in_flags)) andre@0: { andre@0: SOCKET osfd; andre@0: bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); andre@0: PR_ASSERT(NULL != bottom); andre@0: andre@0: osfd = (SOCKET) bottom->secret->md.osfd; andre@0: andre@0: if (FD_ISSET(osfd, &rd)) andre@0: { andre@0: if (pd->out_flags & _PR_POLL_READ_SYS_READ) andre@0: out_flags |= PR_POLL_READ; andre@0: if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) andre@0: out_flags |= PR_POLL_WRITE; andre@0: } andre@0: if (FD_ISSET(osfd, &wt)) andre@0: { andre@0: if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) andre@0: out_flags |= PR_POLL_READ; andre@0: if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) andre@0: out_flags |= PR_POLL_WRITE; andre@0: } andre@0: if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT; andre@0: } andre@0: pd->out_flags = out_flags; andre@0: if (out_flags) ready++; andre@0: } andre@0: PR_ASSERT(ready > 0); andre@0: } andre@0: else if (ready == SOCKET_ERROR) andre@0: { andre@0: err = WSAGetLastError(); andre@0: if (err == WSAENOTSOCK) andre@0: { andre@0: /* Find the bad fds */ andre@0: int optval; andre@0: int optlen = sizeof(optval); andre@0: ready = 0; andre@0: for (pd = pds, epd = pd + npds; pd < epd; pd++) andre@0: { andre@0: pd->out_flags = 0; andre@0: if ((NULL != pd->fd) && (0 != pd->in_flags)) andre@0: { andre@0: bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); andre@0: if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET, andre@0: SO_TYPE, (char *) &optval, &optlen) == -1) andre@0: { andre@0: PR_ASSERT(WSAGetLastError() == WSAENOTSOCK); andre@0: if (WSAGetLastError() == WSAENOTSOCK) andre@0: { andre@0: pd->out_flags = PR_POLL_NVAL; andre@0: ready++; andre@0: } andre@0: } andre@0: } andre@0: } andre@0: PR_ASSERT(ready > 0); andre@0: } andre@0: else _PR_MD_MAP_SELECT_ERROR(err); andre@0: } andre@0: andre@0: return ready; andre@0: }