/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "primpl.h"
static int winsockNotPresent = 0;
void
_PR_MD_INIT_IO()
{
int rv;
WORD WSAVersion = 0x0101;
WSADATA WSAData;
rv = WSAStartup( WSAVersion, &WSAData );
if ( rv != 0 )
{
_PR_MD_MAP_WSASTARTUP_ERROR(WSAGetLastError());
winsockNotPresent = 1;
}
return;
}
void
_PR_MD_CLEANUP_BEFORE_EXIT(void)
{
int rv;
int err;
rv = WSACleanup();
if ( rv == SOCKET_ERROR )
{
err = WSAGetLastError();
PR_ASSERT(0);
}
return;
} /* end _PR_MD_CLEANUP_BEFORE_EXIT() */
/* --- SOCKET IO --------------------------------------------------------- */
PRStatus
_MD_WindowsGetHostName(char *name, PRUint32 namelen)
{
PRIntn rv;
PRInt32 syserror;
rv = gethostname(name, (PRInt32) namelen);
if (0 == rv) {
return PR_SUCCESS;
}
syserror = WSAGetLastError();
PR_ASSERT(WSANOTINITIALISED != syserror);
_PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
return PR_FAILURE;
}
PRInt32
_PR_MD_SOCKET(int af, int type, int flags)
{
SOCKET sock;
PRUint32 one = 1;
PRInt32 rv;
PRInt32 err;
if ( winsockNotPresent )
return( (PRInt32)INVALID_SOCKET );
sock = socket(af, type, flags);
if (sock == INVALID_SOCKET )
{
int rv = GetLastError();
closesocket(sock);
_PR_MD_MAP_SOCKET_ERROR(rv);
return (PRInt32)INVALID_SOCKET;
}
/*
** Make the socket Non-Blocking
*/
rv = ioctlsocket( sock, FIONBIO, &one);
if ( rv != 0 )
{
err = WSAGetLastError();
return -1;
}
return (PRInt32)sock;
}
PRInt32
_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
{
PRUint32 result;
if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
return -1;
}
return result;
}
/*
** _MD_CloseSocket() -- Close a socket
**
*/
PRInt32
_PR_MD_CLOSE_SOCKET(PRInt32 osfd)
{
PRInt32 rv;
rv = closesocket((SOCKET) osfd );
if (rv < 0)
_PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
return rv;
}
PRInt32 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
{
int rv, err;
rv = listen(fd->secret->md.osfd, backlog);
if ( rv == SOCKET_ERROR ) {
_PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
return(-1);
}
return(rv);
}
PRInt32
_PR_MD_ACCEPT(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen,
PRIntervalTime timeout )
{
PRInt32 osfd = fd->secret->md.osfd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 err;
PRIntn rv;
MD_ASSERTINT( *addrlen );
while ((rv = (SOCKET)accept(osfd, (struct sockaddr *) addr,
(int *)addrlen)) == INVALID_SOCKET ) {
err = WSAGetLastError();
if ( err == WSAEWOULDBLOCK ) {
if (fd->secret->nonblocking) {
break;
}
if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
if ( _PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
} else
{
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
}
rv = -1;
goto done;
} else if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
rv = -1;
goto done;
}
} else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
continue;
} else {
break;
}
}
if (rv < 0) {
_PR_MD_MAP_ACCEPT_ERROR(err);
}
done:
if ( rv == INVALID_SOCKET )
return(-1 );
else
return(rv);
} /* end _MD_Accept() */
PRInt32
_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
PRIntervalTime timeout)
{
PRInt32 osfd = fd->secret->md.osfd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 rv, err;
while ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) {
err = WSAGetLastError();
if (err == WSAEISCONN) {
rv = 0;
break;
}
/* for winsock1.1, it reports EALREADY as EINVAL */
if ((err == WSAEWOULDBLOCK)
||(err == WSAEALREADY)
|| (err = WSAEINVAL)) {
if (fd->secret->nonblocking) {
break;
}
if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
if ( _PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
} else
{
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
}
rv = -1;
goto done;
} else if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
rv = -1;
goto done;
}
} else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
continue;
} else {
break;
}
}
if (rv < 0) {
_PR_MD_MAP_CONNECT_ERROR(err);
}
done:
return rv;
}
PRInt32
_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
{
PRInt32 rv;
int one = 1;
rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
if (rv == SOCKET_ERROR) {
_PR_MD_MAP_BIND_ERROR(WSAGetLastError());
return -1;
}
return 0;
}
PRInt32
_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
PRIntervalTime timeout)
{
PRInt32 osfd = fd->secret->md.osfd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 rv, err;
while ((rv = recv(osfd,buf,amount,flags)) == -1) {
err = WSAGetLastError();
if ( err == WSAEWOULDBLOCK ) {
if (fd->secret->nonblocking) {
break;
}
if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
if ( _PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
} else
{
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
}
rv = -1;
goto done;
} else if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
rv = -1;
goto done;
}
} else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
continue;
} else {
break;
}
}
if (rv < 0) {
_PR_MD_MAP_RECV_ERROR(err);
}
done:
return(rv);
}
PRInt32
_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
PRIntervalTime timeout)
{
PRInt32 osfd = fd->secret->md.osfd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 rv, err;
while ((rv = send(osfd,buf,amount,flags)) == -1) {
err = WSAGetLastError();
if ( err == WSAEWOULDBLOCK ) {
if (fd->secret->nonblocking) {
break;
}
if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
if ( _PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
} else
{
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
}
rv = -1;
goto done;
} else if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
rv = -1;
goto done;
}
} else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
continue;
} else {
break;
}
}
if (rv < 0) {
_PR_MD_MAP_SEND_ERROR(err);
}
done:
return rv;
}
PRInt32
_PR_MD_SENDTO(PRFileDesc*fd, const void *buf, PRInt32 amount, PRIntn flags,
const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
{
PRInt32 osfd = fd->secret->md.osfd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 rv, err;
while ((rv = sendto(osfd, buf, amount, flags,
(struct sockaddr *) addr, addrlen)) == -1) {
err = WSAGetLastError();
if ( err == WSAEWOULDBLOCK ) {
if (fd->secret->nonblocking) {
break;
}
if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
if ( _PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
} else
{
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
}
rv = -1;
goto done;
} else if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
rv = -1;
goto done;
}
} else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
continue;
} else {
break;
}
}
if (rv < 0) {
_PR_MD_MAP_SENDTO_ERROR(err);
}
done:
return rv;
}
PRInt32
_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
{
PRInt32 osfd = fd->secret->md.osfd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRInt32 rv, err;
while ((*addrlen = PR_NETADDR_SIZE(addr)),
((rv = recvfrom(osfd, buf, amount, flags,
(struct sockaddr FAR *) addr,(int FAR *)addrlen)) == -1)) {
err = WSAGetLastError();
if ( err == WSAEWOULDBLOCK ) {
if (fd->secret->nonblocking) {
break;
}
if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
if ( _PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
} else
{
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
}
rv = -1;
goto done;
} else if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
rv = -1;
goto done;
}
} else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
continue;
} else {
break;
}
}
if (rv < 0) {
_PR_MD_MAP_RECVFROM_ERROR(err);
}
done:
return(rv);
}
PRInt32
_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
{
int index;
int sent = 0;
int rv;
for (index=0; index < iov_size; index++)
{
/*
* XXX To be fixed
* should call PR_Send
*/
rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
if (rv > 0)
sent += rv;
if ( rv != iov[index].iov_len )
{
if (sent <= 0)
return -1;
return -1;
}
}
return sent;
}
PRInt32
_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
{
PRInt32 rv;
rv = shutdown(fd->secret->md.osfd, how);
if (rv < 0)
_PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
return rv;
}
PRStatus
_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
{
PRInt32 rv;
rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, (int *)len);
if (rv==0)
return PR_SUCCESS;
else {
_PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
return PR_FAILURE;
}
}
PRStatus
_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
{
PRInt32 rv;
rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, (int*)len);
if (rv==0)
return PR_SUCCESS;
else {
_PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
return PR_FAILURE;
}
}
PRStatus
_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
{
PRInt32 rv;
rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, (int*)optlen);
if (rv==0)
return PR_SUCCESS;
else {
_PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
return PR_FAILURE;
}
}
PRStatus
_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
{
PRInt32 rv;
rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
if (rv==0)
return PR_SUCCESS;
else {
_PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
return PR_FAILURE;
}
}
void
_PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
{
return; // do nothing!
}
/*
** Wait for I/O on a single descriptor.
*
* return 0, if timed-out, else return 1
*/
PRInt32
_PR_WaitForFD(PRInt32 osfd, PRUintn how, PRIntervalTime timeout)
{
_PRWin16PollDesc *pd;
PRPollQueue *pq;
PRIntn is;
PRInt32 rv = 1;
PRThread *me = _PR_MD_CURRENT_THREAD();
PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
pd = &me->md.thr_pd;
pq = &me->md.thr_pq;
if (timeout == PR_INTERVAL_NO_WAIT) return 0;
pd->osfd = osfd;
pd->in_flags = how;
pd->out_flags = 0;
pq->pds = pd;
pq->npds = 1;
_PR_INTSOFF(is);
_PR_MD_IOQ_LOCK();
_PR_THREAD_LOCK(me);
if (_PR_PENDING_INTERRUPT(me)) {
_PR_THREAD_UNLOCK(me);
_PR_MD_IOQ_UNLOCK();
return 0;
}
pq->thr = me;
pq->on_ioq = PR_TRUE;
pq->timeout = timeout;
_PR_ADD_TO_IOQ((*pq), me->cpu);
if (how == PR_POLL_READ) {
FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
(_PR_FD_READ_CNT(me->cpu))[osfd]++;
} else if (how == PR_POLL_WRITE) {
FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
(_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
} else {
FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
}
if (_PR_IOQ_MAX_OSFD(me->cpu) < osfd)
_PR_IOQ_MAX_OSFD(me->cpu) = osfd;
if (_PR_IOQ_TIMEOUT(me->cpu) > timeout)
_PR_IOQ_TIMEOUT(me->cpu) = timeout;
_PR_THREAD_LOCK(me);
_PR_SLEEPQ_LOCK(me->cpu);
_PR_ADD_SLEEPQ(me, timeout);
me->state = _PR_IO_WAIT;
me->io_pending = PR_TRUE;
me->io_suspended = PR_FALSE;
_PR_SLEEPQ_UNLOCK(me->cpu);
_PR_THREAD_UNLOCK(me);
_PR_MD_IOQ_UNLOCK();
_PR_MD_WAIT(me, timeout);
me->io_pending = PR_FALSE;
me->io_suspended = PR_FALSE;
/*
** If we timed out the pollq might still be on the ioq. Remove it
** before continuing.
*/
if (pq->on_ioq) {
_PR_INTSOFF(is);
_PR_MD_IOQ_LOCK();
/*
* Need to check pq.on_ioq again
*/
if (pq->on_ioq) {
PR_REMOVE_LINK(&pq->links);
if (how == PR_POLL_READ) {
if ((--(_PR_FD_READ_CNT(me->cpu))[osfd]) == 0)
FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
} else if (how == PR_POLL_WRITE) {
if ((--(_PR_FD_WRITE_CNT(me->cpu))[osfd]) == 0)
FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
} else {
if ((--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]) == 0)
FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
}
}
_PR_MD_IOQ_UNLOCK();
rv = 0;
}
_PR_FAST_INTSON(is);
return(rv);
}
/*
* Unblock threads waiting for I/O
* used when interrupting threads
*
* NOTE: The thread lock should held when this function is called.
* On return, the thread lock is released.
*/
void _PR_Unblock_IO_Wait(PRThread *thr)
{
int pri = thr->priority;
_PRCPU *cpu = thr->cpu;
PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
_PR_SLEEPQ_LOCK(cpu);
_PR_DEL_SLEEPQ(thr, PR_TRUE);
_PR_SLEEPQ_UNLOCK(cpu);
PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
thr->state = _PR_RUNNABLE;
_PR_RUNQ_LOCK(cpu);
_PR_ADD_RUNQ(thr, cpu, pri);
_PR_RUNQ_UNLOCK(cpu);
_PR_THREAD_UNLOCK(thr);
_PR_MD_WAKEUP_WAITER(thr);
}
/*
** Scan through io queue and find any bad fd's that triggered the error
** from _MD_SELECT
*/
static void FindBadFDs(void)
{
PRCList *q;
PRThread *me = _MD_CURRENT_THREAD();
int sockOpt;
int sockOptLen = sizeof(sockOpt);
PR_ASSERT(!_PR_IS_NATIVE_THREAD(me));
q = (_PR_IOQ(me->cpu)).next;
_PR_IOQ_MAX_OSFD(me->cpu) = -1;
_PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
while (q != &_PR_IOQ(me->cpu)) {
PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
PRBool notify = PR_FALSE;
_PRWin16PollDesc *pds = pq->pds;
_PRWin16PollDesc *epds = pds + pq->npds;
PRInt32 pq_max_osfd = -1;
q = q->next;
for (; pds < epds; pds++) {
PRInt32 osfd = pds->osfd;
pds->out_flags = 0;
PR_ASSERT(osfd >= 0 || pds->in_flags == 0);
if (pds->in_flags == 0) {
continue; /* skip this fd */
}
if ( getsockopt(osfd,
(int)SOL_SOCKET,
SO_TYPE,
(char*)&sockOpt,
&sockOptLen) == SOCKET_ERROR )
{
if ( WSAGetLastError() == WSAENOTSOCK )
{
PR_LOG(_pr_io_lm, PR_LOG_MAX,
("file descriptor %d is bad", osfd));
pds->out_flags = PR_POLL_NVAL;
notify = PR_TRUE;
}
}
if (osfd > pq_max_osfd) {
pq_max_osfd = osfd;
}
}
if (notify) {
PRIntn pri;
PR_REMOVE_LINK(&pq->links);
pq->on_ioq = PR_FALSE;
/*
* Decrement the count of descriptors for each desciptor/event
* because this I/O request is being removed from the
* ioq
*/
pds = pq->pds;
for (; pds < epds; pds++) {
PRInt32 osfd = pds->osfd;
PRInt16 in_flags = pds->in_flags;
PR_ASSERT(osfd >= 0 || in_flags == 0);
if (in_flags & PR_POLL_READ) {
if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
}
if (in_flags & PR_POLL_WRITE) {
if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
}
if (in_flags & PR_POLL_EXCEPT) {
if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
}
}
_PR_THREAD_LOCK(pq->thr);
if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
_PRCPU *cpu = pq->thr->cpu;
_PR_SLEEPQ_LOCK(pq->thr->cpu);
_PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
_PR_SLEEPQ_UNLOCK(pq->thr->cpu);
pri = pq->thr->priority;
pq->thr->state = _PR_RUNNABLE;
_PR_RUNQ_LOCK(cpu);
_PR_ADD_RUNQ(pq->thr, cpu, pri);
_PR_RUNQ_UNLOCK(cpu);
}
_PR_THREAD_UNLOCK(pq->thr);
} else {
if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
_PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
_PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
}
}
} /* end FindBadFDs() */
/*
** Called by the scheduler when there is nothing to do. This means that
** all threads are blocked on some monitor somewhere.
**
** Pause the current CPU. longjmp to the cpu's pause stack
*/
PRInt32 _PR_MD_PAUSE_CPU( PRIntervalTime ticks)
{
PRThread *me = _MD_CURRENT_THREAD();
struct timeval timeout, *tvp;
fd_set r, w, e;
fd_set *rp, *wp, *ep;
PRInt32 max_osfd, nfd;
PRInt32 rv;
PRCList *q;
PRUint32 min_timeout;
PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
/*
* assigment of fd_sets
*/
r = _PR_FD_READ_SET(me->cpu);
w = _PR_FD_WRITE_SET(me->cpu);
e = _PR_FD_EXCEPTION_SET(me->cpu);
rp = &r;
wp = &w;
ep = &e;
max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1;
min_timeout = _PR_IOQ_TIMEOUT(me->cpu);
/*
** Compute the minimum timeout value: make it the smaller of the
** timeouts specified by the i/o pollers or the timeout of the first
** sleeping thread.
*/
q = _PR_SLEEPQ(me->cpu).next;
if (q != &_PR_SLEEPQ(me->cpu)) {
PRThread *t = _PR_THREAD_PTR(q);
if (t->sleep < min_timeout) {
min_timeout = t->sleep;
}
}
if (min_timeout > ticks) {
min_timeout = ticks;
}
if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
tvp = NULL;
} else {
timeout.tv_sec = PR_IntervalToSeconds(min_timeout);
timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout)
% PR_USEC_PER_SEC;
tvp = &timeout;
}
_PR_MD_IOQ_UNLOCK();
_MD_CHECK_FOR_EXIT();
/*
* check for i/o operations
*/
nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp);
_MD_CHECK_FOR_EXIT();
_PR_MD_IOQ_LOCK();
/*
** Notify monitors that are associated with the selected descriptors.
*/
if (nfd > 0) {
q = _PR_IOQ(me->cpu).next;
_PR_IOQ_MAX_OSFD(me->cpu) = -1;
_PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
while (q != &_PR_IOQ(me->cpu)) {
PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
PRBool notify = PR_FALSE;
_PRWin16PollDesc *pds = pq->pds;
_PRWin16PollDesc *epds = pds + pq->npds;
PRInt32 pq_max_osfd = -1;
q = q->next;
for (; pds < epds; pds++) {
PRInt32 osfd = pds->osfd;
PRInt16 in_flags = pds->in_flags;
PRInt16 out_flags = 0;
PR_ASSERT(osfd >= 0 || in_flags == 0);
if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, rp)) {
out_flags |= PR_POLL_READ;
}
if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, wp)) {
out_flags |= PR_POLL_WRITE;
}
if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
out_flags |= PR_POLL_EXCEPT;
}
pds->out_flags = out_flags;
if (out_flags) {
notify = PR_TRUE;
}
if (osfd > pq_max_osfd) {
pq_max_osfd = osfd;
}
}
if (notify == PR_TRUE) {
PRIntn pri;
PRThread *thred;
PR_REMOVE_LINK(&pq->links);
pq->on_ioq = PR_FALSE;
/*
* Decrement the count of descriptors for each desciptor/event
* because this I/O request is being removed from the
* ioq
*/
pds = pq->pds;
for (; pds < epds; pds++) {
PRInt32 osfd = pds->osfd;
PRInt16 in_flags = pds->in_flags;
PR_ASSERT(osfd >= 0 || in_flags == 0);
if (in_flags & PR_POLL_READ) {
if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
}
if (in_flags & PR_POLL_WRITE) {
if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
}
if (in_flags & PR_POLL_EXCEPT) {
if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
}
}
thred = pq->thr;
_PR_THREAD_LOCK(thred);
if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
_PRCPU *cpu = thred->cpu;
_PR_SLEEPQ_LOCK(pq->thr->cpu);
_PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
_PR_SLEEPQ_UNLOCK(pq->thr->cpu);
pri = pq->thr->priority;
pq->thr->state = _PR_RUNNABLE;
pq->thr->cpu = cpu;
_PR_RUNQ_LOCK(cpu);
_PR_ADD_RUNQ(pq->thr, cpu, pri);
_PR_RUNQ_UNLOCK(cpu);
if (_pr_md_idle_cpus > 1)
_PR_MD_WAKEUP_WAITER(thred);
}
_PR_THREAD_UNLOCK(thred);
} else {
if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
_PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
_PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
}
}
} else if (nfd < 0) {
if ( WSAGetLastError() == WSAENOTSOCK )
{
FindBadFDs();
} else {
PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d",
errno));
}
}
_PR_MD_IOQ_UNLOCK();
return(0);
} /* end _PR_MD_PAUSE_CPU() */
/*
** _MD_pr_poll() -- Implement MD polling
**
** The function was snatched (re-used) from the unix implementation.
**
** The native thread stuff was deleted.
** The pollqueue is instantiated on the mdthread structure
** to keep the stack frame from being corrupted when this
** thread is waiting on the poll.
**
*/
extern PRInt32
_MD_PR_POLL(PRPollDesc *pds, PRIntn npds,
PRIntervalTime timeout)
{
PRPollDesc *pd, *epd;
PRInt32 n, err, pdcnt;
PRIntn is;
_PRWin16PollDesc *spds, *spd;
PRThread *me = _PR_MD_CURRENT_THREAD();
PRPollQueue *pq;
pq = &me->md.thr_pq;
/*
* XXX
* PRPollDesc has a PRFileDesc field, fd, while the IOQ
* is a list of PRPollQueue structures, each of which contains
* a _PRWin16PollDesc. A _PRWin16PollDesc struct contains
* the OS file descriptor, osfd, and not a PRFileDesc.
* So, we have allocate memory for _PRWin16PollDesc structures,
* copy the flags information from the pds list and have pq
* point to this list of _PRWin16PollDesc structures.
*
* It would be better if the memory allocation can be avoided.
*/
spds = (_PRWin16PollDesc*) PR_MALLOC(npds * sizeof(_PRWin16PollDesc));
if (!spds) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return -1;
}
spd = spds;
_PR_INTSOFF(is);
_PR_MD_IOQ_LOCK();
_PR_THREAD_LOCK(me);
if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
_PR_THREAD_UNLOCK(me);
_PR_MD_IOQ_UNLOCK();
PR_DELETE(spds);
return -1;
}
pdcnt = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++) {
PRInt32 osfd;
PRInt16 in_flags = pd->in_flags;
PRFileDesc *bottom = pd->fd;
if ((NULL == bottom) || (in_flags == 0)) {
continue;
}
while (bottom->lower != NULL) {
bottom = bottom->lower;
}
osfd = bottom->secret->md.osfd;
PR_ASSERT(osfd >= 0 || in_flags == 0);
spd->osfd = osfd;
spd->in_flags = pd->in_flags;
spd++;
pdcnt++;
if (in_flags & PR_POLL_READ) {
FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
_PR_FD_READ_CNT(me->cpu)[osfd]++;
}
if (in_flags & PR_POLL_WRITE) {
FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
(_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
}
if (in_flags & PR_POLL_EXCEPT) {
FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
}
if (osfd > _PR_IOQ_MAX_OSFD(me->cpu))
_PR_IOQ_MAX_OSFD(me->cpu) = osfd;
}
if (timeout < _PR_IOQ_TIMEOUT(me->cpu))
_PR_IOQ_TIMEOUT(me->cpu) = timeout;
pq->pds = spds;
pq->npds = pdcnt;
pq->thr = me;
pq->on_ioq = PR_TRUE;
pq->timeout = timeout;
_PR_ADD_TO_IOQ((*pq), me->cpu);
_PR_SLEEPQ_LOCK(me->cpu);
_PR_ADD_SLEEPQ(me, timeout);
me->state = _PR_IO_WAIT;
me->io_pending = PR_TRUE;
me->io_suspended = PR_FALSE;
_PR_SLEEPQ_UNLOCK(me->cpu);
_PR_THREAD_UNLOCK(me);
_PR_MD_IOQ_UNLOCK();
_PR_MD_WAIT(me, timeout);
me->io_pending = PR_FALSE;
me->io_suspended = PR_FALSE;
/*
* Copy the out_flags from the _PRWin16PollDesc structures to the
* user's PRPollDesc structures and free the allocated memory
*/
spd = spds;
for (pd = pds, epd = pd + npds; pd < epd; pd++) {
if ((NULL == pd->fd) || (pd->in_flags == 0)) {
pd->out_flags = 0;
continue;
}
pd->out_flags = spd->out_flags;
spd++;
}
PR_DELETE(spds);
/*
** If we timed out the pollq might still be on the ioq. Remove it
** before continuing.
*/
if (pq->on_ioq) {
_PR_INTSOFF(is);
_PR_MD_IOQ_LOCK();
/*
* Need to check pq.on_ioq again
*/
if (pq->on_ioq == PR_TRUE) {
PR_REMOVE_LINK(&pq->links);
for (pd = pds, epd = pd + npds; pd < epd; pd++) {
PRInt32 osfd;
PRInt16 in_flags = pd->in_flags;
PRFileDesc *bottom = pd->fd;
if ((NULL == bottom) || (in_flags == 0)) {
continue;
}
while (bottom->lower != NULL) {
bottom = bottom->lower;
}
osfd = bottom->secret->md.osfd;
PR_ASSERT(osfd >= 0 || in_flags == 0);
if (in_flags & PR_POLL_READ) {
if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
}
if (in_flags & PR_POLL_WRITE) {
if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
}
if (in_flags & PR_POLL_EXCEPT) {
if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
}
}
}
_PR_MD_IOQ_UNLOCK();
_PR_INTSON(is);
}
if (_PR_PENDING_INTERRUPT(me)) {
me->flags &= ~_PR_INTERRUPT;
PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
return -1;
} else {
n = 0;
if (pq->on_ioq == PR_FALSE) {
/* Count the number of ready descriptors */
while (--npds >= 0) {
if (pds->out_flags) {
n++;
}
pds++;
}
}
return n;
}
} /* end _MD_pr_poll() */
syntax highlighted by Code2HTML, v. 0.9.1