blob: 2caaac2aa7aa130dca33c0dfa3ea286719ac93f9 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* This file implements _PR_MD_PR_POLL for Win32.
*/
/* The default value of FD_SETSIZE is 64. */
#define FD_SETSIZE 1024
#include "primpl.h"
#if !defined(_PR_GLOBAL_THREADS_ONLY)
struct select_data_s {
PRInt32 status;
PRInt32 error;
fd_set *rd, *wt, *ex;
const struct timeval *tv;
};
static void
_PR_MD_select_thread(void *cdata)
{
struct select_data_s *cd = (struct select_data_s *)cdata;
cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv);
if (cd->status == SOCKET_ERROR) {
cd->error = WSAGetLastError();
}
}
int _PR_NTFiberSafeSelect(
int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timeval *timeout)
{
PRThread *me = _PR_MD_CURRENT_THREAD();
int ready;
if (_PR_IS_NATIVE_THREAD(me)) {
ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout);
}
else
{
/*
** Creating a new thread on each call!!
** I guess web server doesn't use non-block I/O.
*/
PRThread *selectThread;
struct select_data_s data;
data.status = 0;
data.error = 0;
data.rd = readfds;
data.wt = writefds;
data.ex = exceptfds;
data.tv = timeout;
selectThread = PR_CreateThread(
PR_USER_THREAD, _PR_MD_select_thread, &data,
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
if (selectThread == NULL) return -1;
PR_JoinThread(selectThread);
ready = data.status;
if (ready == SOCKET_ERROR) WSASetLastError(data.error);
}
return ready;
}
#endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */
PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
{
int ready, err;
fd_set rd, wt, ex;
fd_set *rdp, *wtp, *exp;
int nrd, nwt, nex;
PRFileDesc *bottom;
PRPollDesc *pd, *epd;
PRThread *me = _PR_MD_CURRENT_THREAD();
struct timeval tv, *tvp = NULL;
if (_PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
return -1;
}
/*
** Is it an empty set? If so, just sleep for the timeout and return
*/
if (0 == npds)
{
PR_Sleep(timeout);
return 0;
}
nrd = nwt = nex = 0;
FD_ZERO(&rd);
FD_ZERO(&wt);
FD_ZERO(&ex);
ready = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++)
{
SOCKET osfd;
PRInt16 in_flags_read = 0, in_flags_write = 0;
PRInt16 out_flags_read = 0, out_flags_write = 0;
if ((NULL != pd->fd) && (0 != pd->in_flags))
{
if (pd->in_flags & PR_POLL_READ)
{
in_flags_read = (pd->fd->methods->poll)(
pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE),
&out_flags_read);
}
if (pd->in_flags & PR_POLL_WRITE)
{
in_flags_write = (pd->fd->methods->poll)(
pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ),
&out_flags_write);
}
if ((0 != (in_flags_read & out_flags_read))
|| (0 != (in_flags_write & out_flags_write)))
{
/* this one's ready right now (buffered input) */
if (0 == ready)
{
/*
* We will have to return without calling the
* system poll/select function. So zero the
* out_flags fields of all the poll descriptors
* before this one.
*/
PRPollDesc *prev;
for (prev = pds; prev < pd; prev++)
{
prev->out_flags = 0;
}
}
ready += 1;
pd->out_flags = out_flags_read | out_flags_write;
}
else
{
pd->out_flags = 0; /* pre-condition */
/* make sure this is an NSPR supported stack */
bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
PR_ASSERT(NULL != bottom); /* what to do about that? */
if ((NULL != bottom)
&& (_PR_FILEDESC_OPEN == bottom->secret->state))
{
if (0 == ready)
{
osfd = (SOCKET) bottom->secret->md.osfd;
if (in_flags_read & PR_POLL_READ)
{
pd->out_flags |= _PR_POLL_READ_SYS_READ;
FD_SET(osfd, &rd);
nrd++;
}
if (in_flags_read & PR_POLL_WRITE)
{
pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
FD_SET(osfd, &wt);
nwt++;
}
if (in_flags_write & PR_POLL_READ)
{
pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
FD_SET(osfd, &rd);
nrd++;
}
if (in_flags_write & PR_POLL_WRITE)
{
pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
FD_SET(osfd, &wt);
nwt++;
}
if (pd->in_flags & PR_POLL_EXCEPT) {
FD_SET(osfd, &ex);
nex++;
}
}
}
else
{
if (0 == ready)
{
PRPollDesc *prev;
for (prev = pds; prev < pd; prev++)
{
prev->out_flags = 0;
}
}
ready += 1; /* this will cause an abrupt return */
pd->out_flags = PR_POLL_NVAL; /* bogii */
}
}
}
else
{
pd->out_flags = 0;
}
}
if (0 != ready) return ready; /* no need to block */
/*
* FD_SET does nothing if the fd_set's internal fd_array is full. If
* nrd, nwt, or nex is greater than FD_SETSIZE, we know FD_SET must
* have failed to insert an osfd into the corresponding fd_set, and
* therefore we should fail.
*/
if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return -1;
}
rdp = (0 == nrd) ? NULL : &rd;
wtp = (0 == nwt) ? NULL : &wt;
exp = (0 == nex) ? NULL : &ex;
if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) {
PR_Sleep(timeout);
return 0;
}
if (timeout != PR_INTERVAL_NO_TIMEOUT)
{
PRInt32 ticksPerSecond = PR_TicksPerSecond();
tv.tv_sec = timeout / ticksPerSecond;
tv.tv_usec = PR_IntervalToMicroseconds( timeout % ticksPerSecond );
tvp = &tv;
}
#if defined(_PR_GLOBAL_THREADS_ONLY)
ready = _MD_SELECT(0, rdp, wtp, exp, tvp);
#else
ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp);
#endif
/*
** Now to unravel the select sets back into the client's poll
** descriptor list. Is this possibly an area for pissing away
** a few cycles or what?
*/
if (ready > 0)
{
ready = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++)
{
PRInt16 out_flags = 0;
if ((NULL != pd->fd) && (0 != pd->in_flags))
{
SOCKET osfd;
bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
PR_ASSERT(NULL != bottom);
osfd = (SOCKET) bottom->secret->md.osfd;
if (FD_ISSET(osfd, &rd))
{
if (pd->out_flags & _PR_POLL_READ_SYS_READ)
out_flags |= PR_POLL_READ;
if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
out_flags |= PR_POLL_WRITE;
}
if (FD_ISSET(osfd, &wt))
{
if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
out_flags |= PR_POLL_READ;
if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
out_flags |= PR_POLL_WRITE;
}
if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
}
pd->out_flags = out_flags;
if (out_flags) ready++;
}
PR_ASSERT(ready > 0);
}
else if (ready == SOCKET_ERROR)
{
err = WSAGetLastError();
if (err == WSAENOTSOCK)
{
/* Find the bad fds */
int optval;
int optlen = sizeof(optval);
ready = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++)
{
pd->out_flags = 0;
if ((NULL != pd->fd) && (0 != pd->in_flags))
{
bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
SO_TYPE, (char *) &optval, &optlen) == -1)
{
PR_ASSERT(WSAGetLastError() == WSAENOTSOCK);
if (WSAGetLastError() == WSAENOTSOCK)
{
pd->out_flags = PR_POLL_NVAL;
ready++;
}
}
}
}
PR_ASSERT(ready > 0);
}
else _PR_MD_MAP_SELECT_ERROR(err);
}
return ready;
}