xbox-kernel/private/ntos/xnet/winsock/select.c
2020-09-30 17:17:25 +02:00

363 lines
8.9 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
select.c
Abstract:
Implementation of asynchronous notification related Winsock APIs:
select
WSAGetOverlappedResult
WSACancelOverlappedIO
Revision History:
06/02/2000 davidx
Created it.
--*/
#include "precomp.h"
//
// Count the total number of socket handles
//
#define SOCKETS_IN_SET(_set) ((_set) ? ((_set)->fd_count & 0xffff) : 0)
//
// Information about sockets that was passed to the select calls
//
typedef struct _SELECTINFO {
SOCKET s;
fd_set* fdset;
INT eventMasks;
PCB* pcb;
INT pcbMasks;
} SELECTINFO;
//
// Select event masks
//
#define SELECT_READ_EVENTS (PCBEVENT_READ|PCBEVENT_ACCEPT|PCBEVENT_CLOSE|PCBEVENT_CONNRESET)
#define SELECT_WRITE_EVENTS (PCBEVENT_WRITE|PCBEVENT_CONNECT)
#define SELECT_EXCEPT_EVENTS (PCBEVENT_CONNRESET)
//
// Number of KWAIT_BLOCKs allocated on the stack for select() calls.
//
#define SELECT_STACK_KWAIT_BLOCKS 3
PRIVATE INT
SockLockSelectSockets(
fd_set* fdset,
SELECTINFO* selinfo,
INT offset,
INT eventMasks
)
/*++
Routine Description:
Lock the socket handles that was passed to the select API
Arguments:
fdset - Points to the socket set
selinfo - Points to an array of SELECTINFO structures
for storing the locked socket information
eventMasks - Specifies the interested events
Return Value:
Winsock error code
--*/
{
INT i, count;
// Nothing to do if the set is empty
count = SOCKETS_IN_SET(fdset);
for (i=0; i < count; i++) {
SOCKET s = fdset->fd_array[i];
INT j, k = offset + i;
selinfo[k].s = s;
selinfo[k].fdset = fdset;
selinfo[k].eventMasks = eventMasks;
// Check to see if the socket is already used
// in the same select call
for (j=0; j < k && selinfo[j].s != s; j++)
;
if (j == k) {
//
// The socket isn't seen already
//
selinfo[k].pcbMasks = eventMasks;
selinfo[k].pcb = SockLock(s);
if (!selinfo[k].pcb)
return GetLastError();
} else {
//
// The socket is already seen
//
selinfo[j].pcbMasks |= eventMasks;
selinfo[k].pcbMasks = 0;
selinfo[k].pcb = selinfo[j].pcb;
}
}
return NO_ERROR;
}
int WSAAPI
select(
int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
const struct timeval* timeout
)
/*++
Routine Description:
Refer to XAPI SDK documentation.
--*/
{
SELECTINFO tempinfo;
PRKEVENT tempevent;
KWAIT_BLOCK tempWaitBlocks[SELECT_STACK_KWAIT_BLOCKS];
SELECTINFO* selinfo;
PRKEVENT* events;
INT index, rdcnt, rwcnt, selcnt = 0;
PCB* pcb;
LARGE_INTEGER waittime;
LARGE_INTEGER* pwait;
PKWAIT_BLOCK waitBlockArray = tempWaitBlocks;
WinsockApiProlog_(select, SOCKET_ERROR);
// Count the total number of sockets
// (ignore the input nfds parameter)
rdcnt = SOCKETS_IN_SET(readfds);
rwcnt = rdcnt + SOCKETS_IN_SET(writefds);
nfds = rwcnt + SOCKETS_IN_SET(exceptfds);
if (nfds == 0) {
WinsockApiReturnError_(WSAEINVAL, SOCKET_ERROR);
}
if (nfds == 1) {
// Use temporary stack buffers for the special case
// where there is only one socket. This saves us from
// two extra memory allocations.
events = &tempevent;
selinfo = &tempinfo;
ZeroMem(selinfo, sizeof(SELECTINFO));
} else {
selinfo = (SELECTINFO*) MAlloc0(nfds*sizeof(SELECTINFO));
events = (PRKEVENT*) MAlloc(nfds*sizeof(PRKEVENT));
if (!selinfo || !events) {
nfds = 0;
WinsockApiGotoExit_(WSAENOBUFS);
}
}
// Lock all the socket handles
if ((err = SockLockSelectSockets(readfds, selinfo, 0, SELECT_READ_EVENTS)) != 0 ||
(err = SockLockSelectSockets(writefds, selinfo, rdcnt, SELECT_WRITE_EVENTS)) != 0 ||
(err = SockLockSelectSockets(exceptfds, selinfo, rwcnt, SELECT_EXCEPT_EVENTS)) != 0) {
goto exit;
}
// Compute the wait time in 100ns unit
if (timeout) {
pwait = &waittime;
waittime.QuadPart = Int32x32To64(timeout->tv_sec, -10000000) +
Int32x32To64(timeout->tv_usec, -10);
} else {
pwait = NULL;
}
// Check if we to wait:
// if we do, set up the socket event flags
if (!pwait || pwait->QuadPart) {
INT waitCount = 0;
for (index=0; index < nfds; index++) {
pcb = selinfo[index].pcb;
if (selinfo[index].pcbMasks) {
if (PcbCheckSelectEvents(pcb, selinfo[index].pcbMasks, -1)) break;
events[waitCount++] = GetPcbWaitEvent(pcb);
}
}
if (index == nfds) {
if (waitCount > SELECT_STACK_KWAIT_BLOCKS) {
waitBlockArray = (PKWAIT_BLOCK) MAlloc0(waitCount * sizeof(KWAIT_BLOCK));
if (!waitBlockArray) {
WinsockApiGotoExit_(WSAENOBUFS);
}
}
err = KeWaitForMultipleObjects(
waitCount,
events,
WaitAny,
UserRequest,
UserMode,
FALSE,
pwait,
waitBlockArray);
if ((err < 0 || err >= waitCount) && err != STATUS_TIMEOUT) {
WinsockApiGotoExit_(WSAEFAULT);
}
}
}
// Determine which socket events are ready
// and return appropriate information
if (readfds) { FD_ZERO(readfds); }
if (writefds) { FD_ZERO(writefds); }
if (exceptfds) { FD_ZERO(exceptfds); }
for (index=selcnt=0; index < nfds; index++) {
if (PcbCheckSelectEvents(
selinfo[index].pcb,
selinfo[index].eventMasks,
0)) {
FD_SET(selinfo[index].s, selinfo[index].fdset);
selcnt++;
}
}
err = NO_ERROR;
exit:
for (index=0; index < nfds; index++) {
pcb = selinfo[index].pcb;
if (pcb && selinfo[index].pcbMasks) {
PcbClearSelectEvents(pcb);
SockUnlock(pcb);
}
}
if (waitBlockArray != tempWaitBlocks) { Free(waitBlockArray); }
if (selinfo != &tempinfo) { Free(selinfo); }
if (events != &tempevent) { Free(events); }
WinsockApiCheckError_(SOCKET_ERROR);
return selcnt;
}
BOOL WSAAPI
WSAGetOverlappedResult(
SOCKET s,
LPWSAOVERLAPPED overlapped,
LPDWORD byteCount,
BOOL fWait,
LPDWORD flags
)
/*++
Routine Description:
Refer to XAPI SDK documentation.
--*/
{
WinsockApiPrologSockLock_(WSAGetOverlappedResult, FALSE);
WinsockApiParamCheck_(
overlapped != NULL &&
overlapped->hEvent != NULL &&
byteCount != NULL &&
flags != NULL);
//
// Check if we need to wait for the I/O request to complete
//
if (overlapped->_iostatus == NETERR_PENDING && fWait) {
KIRQL irql;
WaitForSingleObject(overlapped->hEvent, INFINITE);
irql = RaiseToDpc();
if (overlapped->_iostatus == NETERR_PENDING) {
err = overlapped->_ioxfercnt ? NETERR_OK : NETERR_CANCELLED;
PcbCompleteOverlappedSendRecv(
(PcbOverlappedReq*) overlapped->_ioreq,
err);
}
LowerFromDpc(irql);
}
//
// If the I/O request was completed,
// return the completion status information
//
if ((err = overlapped->_iostatus) != NETERR_PENDING && NT_SUCCESS(err)) {
*byteCount = overlapped->_ioxfercnt;
*flags = overlapped->_ioflags;
}
if (err == NETERR_PENDING) {
WinsockApiGotoExit_(WSA_IO_INCOMPLETE);
} else {
MapNtStatusToWinsockError_(err);
}
WinsockApiExitSockUnlock_(TRUE, FALSE);
}
INT WSAAPI
WSACancelOverlappedIO(
SOCKET s
)
/*++
Routine Description:
Cancel out any outstanding overlapped I/O requests on a socket
Arguments:
s - Specifies the socket handle
Return Value:
0 if successful, SOCKET_ERROR if there is an error
--*/
{
KIRQL irql;
WinsockApiPrologSockLock_(WSACancelOverlappedIO, SOCKET_ERROR);
irql = RaiseToDpc();
PcbClearOverlappedRecvs(pcb, NETERR_CANCELLED);
PcbClearOverlappedSends(pcb, NETERR_CANCELLED);
LowerFromDpc(irql);
WinsockApiGotoExit_(NO_ERROR);
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
}