Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1874 lines
48 KiB

/*++
Copyright (c) 1992-1996 Microsoft Corporation
Module Name:
select.c
Abstract:
This module contains support for the select( ) and WSASelectWindow
WinSock APIs.
Author:
David Treadwell (davidtr) 4-Apr-1992
Revision History:
--*/
//
// FD_SET uses the FD_SETSIZE macro, so define it to a huge value here so
// that apps can pass a very large number of sockets to select(), which
// uses the FD_SET macro.
//
#define FD_SETSIZE 65536
#include "winsockp.h"
#define HANDLES_IN_SET(set) ( (set) == NULL ? 0 : (set->fd_count & 0xFFFF) )
#define IS_EVENT_ENABLED(event, socket) \
( (socket->DisabledAsyncSelectEvents & event) == 0 && \
(socket->AsyncSelectlEvent & event) != 0 )
typedef struct _POLL_CONTEXT_BLOCK {
SOCKET SocketHandle;
DWORD SocketSerialNumber;
DWORD AsyncSelectSerialNumber;
IO_STATUS_BLOCK IoStatus;
AFD_POLL_INFO PollInfo;
} POLL_CONTEXT_BLOCK, *PPOLL_CONTEXT_BLOCK;
VOID
AsyncSelectCompletionApc (
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
);
int
WSPAPI
WSPSelect (
int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timeval *timeout,
LPINT lpErrno
)
/*++
Routine Description:
This function is used to determine the status of one or more
sockets. For each socket, the caller may request information on
read, write or error status. The set of sockets for which a given
status is requested is indicated by an fd_set structure. Upon
return, the structure is updated to reflect the subset of these
sockets which meet the specified condition, and select() returns the
number of sockets meeting the conditions. A set of macros is
provided for manipulating an fd_set. These macros are compatible
with those used in the Berkeley software, but the underlying
representation is completely different.
The parameter readfds identifies those sockets which are to be
checked for readability. If the socket is currently listen()ing, it
will be marked as readable if an incoming connection request has
been received, so that an accept() is guaranteed to complete without
blocking. For other sockets, readability means that queued data is
available for reading, so that a recv() or recvfrom() is guaranteed
to complete without blocking. The presence of out-of-band data will
be checked if the socket option SO_OOBINLINE has been enabled (see
setsockopt()).
The parameter writefds identifies those sockets which are to be
checked for writeability. If a socket is connect()ing
(non-blocking), writeability means that the connection establishment
is complete. For other sockets, writeability means that a send() or
sendto() will complete without blocking. [It is not specified how
long this guarantee can be assumed to be valid, particularly in a
multithreaded environment.]
The parameter exceptfds identifies those sockets which are to be
checked for the presence of out- of-band data or any exceptional
error conditions. Note that out-of-band data will only be reported
in this way if the option SO_OOBINLINE is FALSE. For a SOCK_STREAM,
the breaking of the connection by the peer or due to KEEPALIVE
failure will be indicated as an exception. This specification does
not define which other errors will be included.
Any of readfds, writefds, or exceptfds may be given as NULL if no
descriptors are of interest.
Four macros are defined in the header file winsock.h for
manipulating the descriptor sets. The variable FD_SETSIZE
determines the maximum number of descriptors in a set. (The default
value of FD_SETSIZE is 64, which may be modified by #defining
FD_SETSIZE to another value before #including winsock.h.)
Internally, an fd_set is represented as an array of SOCKETs; the
last valid entry is followed by an element set to INVALID_SOCKET.
The macros are:
FD_CLR(s, *set) Removes the descriptor s from set.
FD_ISSET(s, *set) Nonzero if s is a member of the set, zero otherwise.
FD_SET(s, *set) Adds descriptor s to set.
FD_ZERO(*set) Initializes the set to the NULL set.
The parameter timeout controls how long the select() may take to
complete. If timeout is a null pointer, select() will block
indefinitely until at least one descriptor meets the specified
criteria. Otherwise, timeout points to a struct timeval which
specifies the maximum time that select() should wait before
returning. If the timeval is initialized to {0, 0}, select() will
return immediately; this is used to "poll" the state of the selected
sockets.
Arguments:
nfds - This argument is ignored and included only for the sake of
compatibility.
readfds - A set of sockets to be checked for readability.
writefds - A set of sockets to be checked for writeability
exceptfds - set of sockets to be checked for errors.
timeout The maximum time for select() to wait, or NULL for blocking
operation.
Return Value:
select() returns the total number of descriptors which are ready and
contained in the fd_set structures, or 0 if the time limit expired.
--*/
{
NTSTATUS status;
int err;
PAFD_POLL_INFO pollInfo;
ULONG pollBufferSize;
PAFD_POLL_HANDLE_INFO pollHandleInfo;
ULONG handleCount;
ULONG i;
IO_STATUS_BLOCK ioStatusBlock;
ULONG handlesReady;
UCHAR pollBuffer[MAX_FAST_AFD_POLL];
WS_ENTER( "WSPSelect", readfds, writefds, exceptfds, (PVOID)timeout );
WS_ASSERT( lpErrno != NULL );
err = SockEnterApi( TRUE, TRUE, FALSE );
if( err != NO_ERROR ) {
WS_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
//
// Set up locals so that we know how to clean up on exit.
//
pollInfo = NULL;
handlesReady = 0;
//
// Determine how many handles we're going to check so that we can
// allocate a buffer large enough to hold information about all of
// them.
//
handleCount = HANDLES_IN_SET( readfds ) +
HANDLES_IN_SET( writefds ) +
HANDLES_IN_SET( exceptfds );
//
// If there are no handles specified, just return.
//
if ( handleCount == 0 ) {
WS_EXIT( "WSPSelect", 0, FALSE );
return 0;
}
//
// Allocate space to hold the input buffer for the poll IOCTL.
//
pollBufferSize = sizeof(AFD_POLL_INFO) +
handleCount * sizeof(AFD_POLL_HANDLE_INFO);
if( pollBufferSize <= sizeof(pollBuffer) ) {
pollInfo = (PVOID)pollBuffer;
} else {
pollInfo = ALLOCATE_HEAP( pollBufferSize );
if ( pollInfo == NULL ) {
err = WSAENOBUFS;
goto exit;
}
}
//
// Initialize the poll buffer.
//
pollInfo->NumberOfHandles = handleCount;
pollInfo->Unique = FALSE;
pollHandleInfo = pollInfo->Handles;
for ( i = 0; readfds != NULL && i < (readfds->fd_count & 0xFFFF); i++ ) {
//
// If the connection is disconnected, either abortively or
// orderly, then it is considered possible to read immediately
// on the socket, so include these events in addition to receive.
//
pollHandleInfo->Handle = (HANDLE)readfds->fd_array[i];
pollHandleInfo->PollEvents =
AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ABORT;
pollHandleInfo++;
}
for ( i = 0; writefds != NULL && i < (writefds->fd_count & 0xFFFF); i++ ) {
pollHandleInfo->Handle = (HANDLE)writefds->fd_array[i];
pollHandleInfo->PollEvents = AFD_POLL_SEND;
pollHandleInfo++;
}
for ( i = 0; exceptfds != NULL && i < (exceptfds->fd_count & 0xFFFF); i++ ) {
pollHandleInfo->Handle = (HANDLE)exceptfds->fd_array[i];
pollHandleInfo->PollEvents =
AFD_POLL_RECEIVE_EXPEDITED | AFD_POLL_CONNECT_FAIL;
pollHandleInfo++;
}
//
// If a timeout was specified, convert it to NT format. Since it is
// a relative time, it must be negative.
//
if ( timeout != NULL ) {
LARGE_INTEGER microseconds;
pollInfo->Timeout = RtlEnlargedIntegerMultiply(
timeout->tv_sec,
-10*1000*1000
);
microseconds = RtlEnlargedIntegerMultiply( timeout->tv_usec, -10 );
pollInfo->Timeout.QuadPart =
pollInfo->Timeout.QuadPart + microseconds.QuadPart;
} else {
//
// No timeout was specified, just set the timeout value
// to the largest possible value, in effect using an infinite
// timeout.
//
pollInfo->Timeout.LowPart = 0xFFFFFFFF;
pollInfo->Timeout.HighPart = 0x7FFFFFFF;
}
//
// Send the IOCTL to AFD. AFD will complete the request as soon as
// one or more of the specified handles is ready for the specified
// operation.
//
// Just use the first handle as the handle for the request. Any
// handle is fine; we just need a handle to AFD so that it gets to the
// driver.
//
// Note that the same buffer is used for both input and output.
// Since IOCTL_AFD_POLL is a method 0 (buffered) IOCTL, this
// shouldn't cause problems.
//
WS_ASSERT( (IOCTL_AFD_POLL & 0x03) == METHOD_BUFFERED );
status = NtDeviceIoControlFile(
pollInfo->Handles[0].Handle,
SockThreadEvent,
NULL, // APC Routine
NULL, // APC Context
&ioStatusBlock,
IOCTL_AFD_POLL,
pollInfo,
pollBufferSize,
pollInfo,
pollBufferSize
);
if ( status == STATUS_PENDING ) {
SockWaitForSingleObject(
SockThreadEvent,
(SOCKET)pollInfo->Handles[0].Handle,
pollInfo->Timeout.QuadPart == 0 ?
SOCK_NEVER_CALL_BLOCKING_HOOK :
SOCK_ALWAYS_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
status = ioStatusBlock.Status;
}
if ( !NT_SUCCESS(status) ) {
err = SockNtStatusToSocketError( status );
goto exit;
}
//
// Use the information provided by the driver to set up the fd_set
// structures to return to the caller. First zero out the structures.
//
if ( readfds != NULL ) {
FD_ZERO( readfds );
}
if ( writefds != NULL ) {
FD_ZERO( writefds );
}
if ( exceptfds != NULL ) {
FD_ZERO( exceptfds );
}
//
// Walk the poll buffer returned by AFD, setting up the fd_set
// structures as we go.
//
pollHandleInfo = pollInfo->Handles;
for ( i = 0; i < pollInfo->NumberOfHandles; i++ ) {
WS_ASSERT( pollHandleInfo->PollEvents != 0 );
if ( (pollHandleInfo->PollEvents & AFD_POLL_RECEIVE) != 0 ) {
WS_ASSERT( readfds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, readfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, readfds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx ready for reading.\n",
pollHandleInfo->Handle ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_SEND) != 0 ) {
WS_ASSERT( writefds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, writefds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, writefds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx ready for writing.\n",
pollHandleInfo->Handle ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_RECEIVE_EXPEDITED) != 0 ) {
WS_ASSERT( exceptfds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, exceptfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, exceptfds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx ready for expedited reading.\n",
pollHandleInfo->Handle ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_ACCEPT) != 0 ) {
WS_ASSERT( readfds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, readfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, readfds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx ready for accept.\n",
pollHandleInfo->Handle ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_CONNECT) != 0 ) {
WS_ASSERT( NT_SUCCESS(pollHandleInfo->Status) );
WS_ASSERT( writefds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, writefds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, writefds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx completed connect, status %lx\n",
pollHandleInfo->Handle, pollHandleInfo->Status ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_CONNECT_FAIL) != 0 ) {
WS_ASSERT( !NT_SUCCESS(pollHandleInfo->Status) );
WS_ASSERT( exceptfds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, exceptfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, exceptfds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx completed connect, status %lx\n",
pollHandleInfo->Handle, pollHandleInfo->Status ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_DISCONNECT) != 0 ) {
WS_ASSERT( readfds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, readfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, readfds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx disconnected.\n",
pollHandleInfo->Handle ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_ABORT) != 0 ) {
WS_ASSERT( readfds != NULL );
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, readfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, readfds );
handlesReady++;
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx aborted.\n",
pollHandleInfo->Handle ));
}
}
if ( (pollHandleInfo->PollEvents & AFD_POLL_LOCAL_CLOSE) != 0 ) {
//
// If the app does a closesocket() on a handle that has a
// select() outstanding on it, this event may get set by
// AFD even though we didn't request notification of it.
// If exceptfds is NULL, then this is an error condition.
//
if ( readfds == NULL ) {
handlesReady = 0;
err = WSAENOTSOCK;
goto exit;
} else {
if ( !FD_ISSET( (SOCKET)pollHandleInfo->Handle, readfds ) ) {
FD_SET( (SOCKET)pollHandleInfo->Handle, readfds );
handlesReady++;
}
}
IF_DEBUG(SELECT) {
WS_PRINT(( "select handle %lx closed locally.\n",
pollHandleInfo->Handle ));
}
}
pollHandleInfo++;
}
exit:
IF_DEBUG(SELECT) {
if ( err != NO_ERROR ) {
WS_PRINT(( "select failed: %ld.\n", err ));
} else {
WS_PRINT(( "select succeeded, %ld readfds, %ld writefds, "
"%ld exceptfds\n",
HANDLES_IN_SET( readfds ),
HANDLES_IN_SET( writefds ),
HANDLES_IN_SET( exceptfds ) ));
}
}
if ( pollInfo != NULL && pollInfo != (PVOID)pollBuffer ) {
FREE_HEAP( pollInfo );
}
if ( err != NO_ERROR ) {
WS_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
WS_ASSERT( (ULONG)handlesReady == (ULONG)(HANDLES_IN_SET( readfds ) +
HANDLES_IN_SET( writefds ) +
HANDLES_IN_SET( exceptfds )) );
WS_EXIT( "WSPSelect", handlesReady, FALSE );
return handlesReady;
} // WSPSelect
int
WSPAPI
WSPAsyncSelect (
SOCKET Handle,
HWND hWnd,
unsigned int wMsg,
long lEvent,
LPINT lpErrno
)
/*++
Routine Description:
This function is used to request that the Windows Sockets DLL should
send a message to the window hWnd whenever it detects any of the
network events specified by the lEvent parameter. The message which
should be sent is specified by the wMsg parameter. The socket for
which notification is required is identified by s.
The lEvent parameter is constructed by or'ing any of the values
specified in the following list:
Value Meaning
FD_READ Want to receive notification of readiness for reading.
FD_WRITE Want to receive notification of readiness for writing.
FD_OOB Want to receive notification of the arrival of
out-of-band data.
FD_ACCEPT Want to receive notification of incoming connections.
FD_CONNECT Want to receive notification of completed connection.
FD_CLOSE Want to receive notification of socket closure.
Issuing a WSAAsyncSelect() for a socket cancels any previous
WSAAsyncSelect() for the same socket. For example, to receive
notification for both reading and writing, the application must call
WSAAsyncSelect() with both FD_READ and FD_WRITE, as follows:
rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ | FD_WRITE);
It is not possible to specify different messages for different
events. The following code will not work; the second call will
cancel the effects of the first, and only FD_WRITE events will be
reported with message wMsg2:
rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ);
rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE);
To cancel all notification i.e., to indicate that the Windows
Sockets implementation should send no further messages related to
network events on the socket lEvent should be set to zero.
rc = WSAAsyncSelect(s, hWnd, 0, 0);
Although the WSAAsyncSelect() takes effect immediately, it is
possible that messages may be waiting in the application's message
queue. The application must therefore be prepared to receive
network event messages even after cancellation.
This function automatically sets socket s to non-blocking mode.
When one of the nominated network events occurs on the specified
socket s, the application's window hWnd receives message wMsg. The
wParam argument identifies the socket on which a network event has
occurred. The low word of lParam specifies the network event that
has occurred. The high word of lParam contains any error code. The
error code be any error as defined in winsock.h.
The error and event codes may be extracted from the lParam using the
macros WSAGETSELECTERROR and WSAGETSELECTEVENT, defined in winsock.h
as:
#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
The use of these macros will maximize the portability of the source
code for the application.
The possible network event codes which may be returned are as
follows:
Value Meaning
FD_READ Socket s ready for reading.
FD_WRITE Socket s ready for writing.
FD_OOB Out-of-band data ready for reading on socket s.
FD_ACCEPT Socket s ready for accepting a new incoming connection.
FD_CONNECT Connection on socket s completed.
FD_CLOSE Connection identified by socket s has been closed.
Arguments:
s - A descriptor identifying the socket for which event notification
is required.
hWnd - A handle identifying the window which should receive a
message when a network event occurs.
wMsg - The message to be received when a network event occurs.
lEvent - A bitmask which specifies a combination of network events
in which the application is interested.
Return Value:
The return value is 0 if the application's declaration of interest
in the network event set was successful. Otherwise the value
SOCKET_ERROR is returned, and a specific error number may be
retrieved by calling WSAGetLastError().
--*/
{
PSOCKET_INFORMATION socket;
int err;
BOOLEAN blocking;
WS_ENTER( "WSPAsyncSelect", (PVOID)Handle, (PVOID)hWnd, (PVOID)wMsg, (PVOID)lEvent );
WS_ASSERT( lpErrno != NULL );
err = SockEnterApi( TRUE, TRUE, FALSE );
if( err != NO_ERROR ) {
WS_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
//
// Remember that WSAAsyncSelect has been called in this process.
// We'll use this information in the send and receive routines to
// know if and when to attempt to reenable async select events.
//
SockAsyncSelectCalled = TRUE;
//
// Initialize the async thread if it hasn't already been started.
//
if ( !SockCheckAndInitAsyncThread( ) ) {
// !!! better error code?
WS_EXIT( "WSPAsyncSelect", SOCKET_ERROR, TRUE );
*lpErrno = WSAENOBUFS;
return SOCKET_ERROR;
}
//
// Initialize locals so that we know how to clean up on exit.
//
socket = NULL;
//
// Find a pointer to the socket structure corresponding to the
// passed-in handle.
//
socket = SockFindAndReferenceSocket( Handle, TRUE );
if ( socket == NULL ) {
err = WSAENOTSOCK;
goto exit;
}
//
// If this is a non-overlapped socket, then fail the request.
//
if( ( socket->CreationFlags & WSA_FLAG_OVERLAPPED ) == 0 ) {
err = WSAEINVAL;
goto exit;
}
//
// Set the socket to nonblocking.
//
blocking = TRUE;
err = SockSetInformation(
socket,
AFD_NONBLOCKING_MODE,
&blocking,
NULL,
NULL
);
if ( err != NO_ERROR ) {
goto exit;
}
socket->NonBlocking = TRUE;
//
// If there's a WSPEventSelect active on this socket, deactivate it.
//
if( socket->EventSelectlNetworkEvents ) {
err = SockEventSelectHelper(
socket,
NULL,
0
);
if( err != NO_ERROR ) {
goto exit;
}
}
//
// Check that the hWnd passed in is a valid Windows handle.
//
if ( !IsWindow( hWnd ) ) {
err = WSAEINVAL;
goto exit;
}
//
// Make sure that only valid bits are specified in lEvent.
//
// !!! should we also make sure that the bits make sense for the
// state of the socket, i.e. don't allow FD_ACCEPT on a
// connected socket?
//
if ( (lEvent & ~FD_ALL_EVENTS) != 0 ) {
err = WSAEINVAL;
goto exit;
}
//
// Let the helper do the dirty work.
//
err = SockAsyncSelectHelper(
socket,
hWnd,
wMsg,
lEvent
);
exit:
if ( socket != NULL ) {
SockDereferenceSocket( socket );
}
if ( err != NO_ERROR) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "WSPAsyncSelect failed: %ld\n", err ));
}
WS_EXIT( "WSPAsyncSelect", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "WSPAsyncSelect successfully posted request, "
"socket = %lx\n", socket ));
}
WS_EXIT( "WSPAsyncSelect", NO_ERROR, FALSE );
return NO_ERROR;
} // WSPAsyncSelect
int
SockAsyncSelectHelper(
PSOCKET_INFORMATION Socket,
HWND hWnd,
unsigned int wMsg,
long lEvent
)
/*++
Routine Description:
x
Arguments:
x
Return Value:
x
--*/
{
PWINSOCK_CONTEXT_BLOCK contextBlock;
//
// Get an async context block.
//
contextBlock = SockAllocateContextBlock( );
if ( contextBlock == NULL ) {
return WSAENOBUFS;
}
//
// Acquire the lock that protects this socket. We hold this lock
// throughout this routine to synchronize against other callers
// performing operations on the socket we're using.
//
SockAcquireSocketLockExclusive( Socket );
//
// Bump the async select serial number on this socket. This field
// allows us to stop sending a particular message as soon as
// WSPAsyncSelect is called to change the settings on the socket.
// When an AFD poll completes for an async select, if the serial
// number has changed then no message is sent.
//
Socket->AsyncSelectSerialNumber++;
//
// Store the specified window handle, message, and event mask in
// the socket information structure.
//
Socket->AsyncSelecthWnd = hWnd;
Socket->AsyncSelectwMsg = wMsg;
Socket->AsyncSelectlEvent = lEvent;
Socket->DisabledAsyncSelectEvents = 0;
//
// If the socket is not connected and is not a datagram socket,
// disable FD_WRITE events. We'll reenable them when the socket
// becomes connected. Disabling them here prevents a race condition
// between FD_WRITE events and FD_CONNECT events, and we don't want
// FD_WRITE events to be posted before FD_CONNECT.
//
if ( !SockIsSocketConnected( Socket ) &&
!IS_DGRAM_SOCK(Socket->SocketType) ) {
Socket->DisabledAsyncSelectEvents |= FD_WRITE;
}
//
// Initialize the async context block for this operation.
//
contextBlock->OpCode = WS_OPCODE_ASYNC_SELECT;
contextBlock->Overlay.AsyncSelect.SocketHandle = Socket->Handle;
contextBlock->Overlay.AsyncSelect.SocketSerialNumber =
Socket->SocketSerialNumber;
contextBlock->Overlay.AsyncSelect.AsyncSelectSerialNumber =
Socket->AsyncSelectSerialNumber;
//
// Queue the request to the async thread.
//
SockQueueRequestToAsyncThread( contextBlock );
//
// Release the socket lock & return.
//
SockReleaseSocketLock( Socket );
return NO_ERROR;
} // SockAsyncSelectHelper
VOID
SockProcessAsyncSelect (
SOCKET Handle,
DWORD SocketSerialNumber,
DWORD AsyncSelectSerialNumber
)
{
PSOCKET_INFORMATION socket;
PPOLL_CONTEXT_BLOCK context;
ULONG eventsToPoll;
NTSTATUS status;
//
// Find a pointer to the socket structure corresponding to the
// passed-in handle.
//
socket = SockFindAndReferenceSocket( Handle, FALSE );
//
// If the socket has been closed, just return.
//
if ( socket == NULL || socket->State == SocketStateClosing ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "SockProcessAsyncSelect: tossing request on handle %lx"
"--closed\n", Handle ));
}
if ( socket != NULL ) {
SockDereferenceSocket( socket );
}
return;
}
//
// Acquire the lock that protects this socket. We hold this lock
// throughout this routine to synchronize against other callers
// performing operations on the socket we're using.
//
SockAcquireSocketLockExclusive( socket );
//
// Make sure that the serial numbers on the socket match those on
// this request. If they are different, throw out this request
// because there is either another async select request waiting for
// us on the queue or the socket has been closed.
//
if ( socket->SocketSerialNumber != SocketSerialNumber ||
socket->AsyncSelectSerialNumber != AsyncSelectSerialNumber ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "SockProcessAsyncSelect: tossing request on handle %lx, "
"SSN = %ld, req SSN = %ld, ASN = %ld, req ASN = %ld\n",
Handle,
socket->SocketSerialNumber,
SocketSerialNumber,
socket->AsyncSelectSerialNumber,
AsyncSelectSerialNumber ));
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
return;
}
//
// Allocate space to hold the poll context block.
//
// !!! need a better failure--post a message about the failure to
// the app.
context = ALLOCATE_HEAP( sizeof(POLL_CONTEXT_BLOCK) );
if ( context == NULL ) {
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
return;
}
//
// Initialize the context and poll buffer. Use an infinite timeout
// and just the one socket handle. Also, this should be a unique
// poll, so if another unique poll is outstanding on this socket
// it will be canceleld.
//
context->SocketHandle = Handle;
context->SocketSerialNumber = SocketSerialNumber;
context->AsyncSelectSerialNumber = AsyncSelectSerialNumber;
context->PollInfo.Timeout.HighPart = 0x7FFFFFFF;
context->PollInfo.Timeout.LowPart = 0xFFFFFFFF;
context->PollInfo.NumberOfHandles = 1;
context->PollInfo.Unique = TRUE;
context->PollInfo.Handles[0].Handle = (HANDLE)socket->Handle;
context->PollInfo.Handles[0].PollEvents = 0;
//
// Determine which events we want to poll.
//
eventsToPoll = socket->AsyncSelectlEvent &
(~socket->DisabledAsyncSelectEvents);
//
// If there are no events that we can poll on, just return. When a
// reenabling function or WSPAsyncSelect is called a new request
// will be queued to the async thread, and a new poll will be initiated.
//
if ( eventsToPoll == 0 ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "SockProcessAsyncSelect: no events to poll on, "
"socket = %lx, lEvent = %lx, disabled = %lx\n",
socket, socket->AsyncSelectlEvent,
socket->DisabledAsyncSelectEvents ));
}
FREE_HEAP( context );
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
return;
}
//
// Set up the events to poll on. Note that we don't put down
// AFD_POLL_CONNECT or AFD_POLL_CONNECT_FAIL requests even if
// FD_CONNECT is specified-- this event is handled manually by the
// connect() routine because of it's inherently different semantics.
//
if ( (eventsToPoll & FD_READ) != 0 && socket->State != SocketStateListening ) {
context->PollInfo.Handles[0].PollEvents |= AFD_POLL_RECEIVE;
}
if ( (eventsToPoll & FD_WRITE) != 0 && socket->State != SocketStateListening ) {
context->PollInfo.Handles[0].PollEvents |= AFD_POLL_SEND;
}
if ( (eventsToPoll & FD_OOB) != 0 && socket->State != SocketStateListening ) {
context->PollInfo.Handles[0].PollEvents |= AFD_POLL_RECEIVE_EXPEDITED;
}
if ( (eventsToPoll & FD_ACCEPT) != 0 ) {
context->PollInfo.Handles[0].PollEvents |= AFD_POLL_ACCEPT;
}
if ( (eventsToPoll & FD_CLOSE) != 0 && socket->State != SocketStateListening ) {
context->PollInfo.Handles[0].PollEvents |= AFD_POLL_DISCONNECT |
AFD_POLL_ABORT |
AFD_POLL_LOCAL_CLOSE;
}
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "SockProcessAsyncSelect: socket = %lx, handle = %lx, "
"lEvent = %lx, disabled = %lx, actual = %lx\n",
socket, socket->Handle, socket->AsyncSelectlEvent,
socket->DisabledAsyncSelectEvents, eventsToPoll ));
}
//
// Start the actual poll.
//
WS_ASSERT( (IOCTL_AFD_POLL & 0x03) == METHOD_BUFFERED );
status = NtDeviceIoControlFile(
(HANDLE)Handle,
NULL, // Event
AsyncSelectCompletionApc,
context,
&context->IoStatus,
IOCTL_AFD_POLL,
&context->PollInfo,
sizeof(context->PollInfo),
&context->PollInfo,
sizeof(context->PollInfo)
);
if ( NT_ERROR(status) ) {
//
// If the request failed, call the APC since the system didn't
// do it.
//
context->IoStatus.Status = status;
AsyncSelectCompletionApc( context, &context->IoStatus, 0 );
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
return;
} // SockProcessAsyncSelect
VOID
AsyncSelectCompletionApc (
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
)
{
PSOCKET_INFORMATION socket;
PPOLL_CONTEXT_BLOCK context = ApcContext;
BOOLEAN posted = FALSE;
ULONG error;
DWORD socketSerialNumber;
DWORD pollEvents;
UNREFERENCED_PARAMETER( Reserved );
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "AsyncSelectCompletionApc: socket %lx, status %lx, "
"poll events %lx\n" ,
context->SocketHandle, IoStatusBlock->Status,
context->PollInfo.Handles[0].PollEvents ));
}
//
// If the request was cancelled, just dereference the socket and free
// resources.
//
if ( IoStatusBlock->Status == STATUS_CANCELLED ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "AsyncSelectCompletionApc: request on handle %lx "
"cancelled.\n", context->SocketHandle ));
}
FREE_HEAP( context );
return;
}
//
// If the DLL is terminating, just bag out now, we don't need to
// bother to free resources since the system will do it for us.
// Note that if we did attempt the FREE_HEAP calls below we could
// deadlock the application's exit.
//
if ( SockTerminating ) {
return;
}
//
// Find a pointer to the socket structure corresponding to the
// passed-in handle.
//
socket = SockFindAndReferenceSocket( context->SocketHandle, FALSE );
//
// If the socket has been closed, just return.
//
if ( socket == NULL || socket->State == SocketStateClosing ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "AsyncSelectCompletionApc: tossing request on handle %lx"
"--%s\n",
context->SocketHandle,
socket == NULL ? "NULL" : "closed" ));
}
FREE_HEAP( context );
if ( socket != NULL ) {
SockDereferenceSocket( socket );
}
return;
}
//
// Acquire the socket's lock to keep it's state constant while
// we do the message posts.
//
// !!! are there issues with acquiring a resource in an APC?
// what if the thread doing the alertable wait holds a
// resource!?!
SockAcquireSocketLockExclusive( socket );
//
// If the serial number on the socket has changed since this request
// was initialized, or if the original socket was closed, just bag
// this notification. Another poll request should be on the way if the
// socket is still open.
//
if ( context->AsyncSelectSerialNumber != socket->AsyncSelectSerialNumber ||
context->SocketSerialNumber != socket->SocketSerialNumber ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "AsyncSelectCompletionApc: tossing req on handle %lx, "
"SSN %ld req SSN %ld ASN %ld req ASN %ld\n",
socket->Handle,
socket->SocketSerialNumber,
context->SocketSerialNumber,
socket->AsyncSelectSerialNumber,
context->AsyncSelectSerialNumber ));
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
FREE_HEAP( context );
return;
}
//
// If the request failed, post a message indicating the failure.
//
if ( !NT_SUCCESS(IoStatusBlock->Status) ) {
ULONG error = SockNtStatusToSocketError( IoStatusBlock->Status );
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( 0, error )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "0", error ));
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
FREE_HEAP( context );
return;
}
//
// We should never get AFD_POLL_CONNECT or AFD_POLL_CONNECT_FAIL
// events, since we never ask for them. FD_CONNECT is handled
// manually yb the connect() routine.
//
pollEvents = context->PollInfo.Handles[0].PollEvents;
WS_ASSERT( (pollEvents & AFD_POLL_CONNECT) == 0 );
WS_ASSERT( (pollEvents & AFD_POLL_CONNECT_FAIL) == 0 );
//
// Post messages based on the event(s) that occurred.
//
if ( IS_EVENT_ENABLED( FD_READ, socket ) &&
(pollEvents & AFD_POLL_RECEIVE) != 0 ) {
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( FD_READ, 0 )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "FD_READ", 0 ));
}
//
// Disable this event. It will be reenabled when the app does a
// recv().
//
socket->DisabledAsyncSelectEvents |= FD_READ;
}
if ( IS_EVENT_ENABLED( FD_OOB, socket ) &&
(pollEvents & AFD_POLL_RECEIVE_EXPEDITED) != 0 ) {
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( FD_OOB, 0 )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "FD_OOB", 0 ));
}
//
// Disable this event. It will be reenabled when the app does a
// recv( MSG_OOB ).
//
// !!! need synchronization?
//
socket->DisabledAsyncSelectEvents |= FD_OOB;
}
if ( IS_EVENT_ENABLED( FD_WRITE, socket ) &&
(pollEvents & AFD_POLL_SEND) != 0 ) {
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( FD_WRITE, 0 )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "FD_WRITE", 0 ));
}
//
// Disable this event. It will be reenabled when the app does a
// send().
//
socket->DisabledAsyncSelectEvents |= FD_WRITE;
}
if ( IS_EVENT_ENABLED( FD_ACCEPT, socket ) &&
(pollEvents & AFD_POLL_ACCEPT) != 0 ) {
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( FD_ACCEPT, 0 )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "FD_ACCEPT", 0 ));
}
//
// Disable this event. It will be reenabled when the app does an
// accept().
//
socket->DisabledAsyncSelectEvents |= FD_ACCEPT;
}
if ( IS_EVENT_ENABLED( FD_CLOSE, socket ) &&
((pollEvents & AFD_POLL_DISCONNECT) != 0 ||
(pollEvents & AFD_POLL_ABORT) != 0 ||
(pollEvents & AFD_POLL_LOCAL_CLOSE) != 0) ) {
if ( (pollEvents & AFD_POLL_ABORT) != 0 ) {
error = WSAECONNABORTED;
} else {
error = NO_ERROR;
}
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( FD_CLOSE, error )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "FD_CLOSE", 0 ));
}
//
// Disable this event. It will never be reenabled.
//
socket->DisabledAsyncSelectEvents |= FD_CLOSE;
}
socketSerialNumber = socket->SocketSerialNumber;
SockReleaseSocketLock( socket );
//
// Start another poll.
//
SockProcessAsyncSelect(
socket->Handle,
socketSerialNumber,
context->AsyncSelectSerialNumber
);
//
// Clean up and return.
//
SockDereferenceSocket( socket );
FREE_HEAP( context );
return;
} // AsyncSelectCompletionApc
VOID
SockReenableAsyncSelectEvent (
IN PSOCKET_INFORMATION Socket,
IN ULONG Event
)
{
PWINSOCK_CONTEXT_BLOCK contextBlock;
//
// Check whether the specified event is in the list of disabled
// async select events for the socket. If it isn't, we don't
// need to do anything.
//
if ( (Socket->DisabledAsyncSelectEvents & Event) == 0 ) {
return;
}
//
// If the socket is closing, don't reenable the select event.
//
SockAcquireGlobalLockExclusive( );
if ( Socket->State == SocketStateClosing ) {
SockReleaseGlobalLock( );
return;
}
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "SockReenableSelectEvent: reenabling event %lx for socket "
"%lx (%lx)\n", Event, Socket->Handle, Socket ));
}
//
// The specified event is currently disabled. Get an async context
// block.
//
// !!! Need a mechanism to handle failure to allocate a context block!
//
contextBlock = SockAllocateContextBlock( );
if ( contextBlock == NULL ) {
WS_ASSERT( FALSE );
SockReleaseGlobalLock( );
return;
}
//
// *** Note that it is assumed that the socket lock is held on entry
// to this routine!
//
//
// Reset the bit for this event so that it is no longer disabled.
//
Socket->DisabledAsyncSelectEvents &= ~Event;
//
// Initialize the async context block for this operation.
//
contextBlock->OpCode = WS_OPCODE_ASYNC_SELECT;
contextBlock->Overlay.AsyncSelect.SocketHandle = Socket->Handle;
contextBlock->Overlay.AsyncSelect.SocketSerialNumber =
Socket->SocketSerialNumber;
contextBlock->Overlay.AsyncSelect.AsyncSelectSerialNumber =
Socket->AsyncSelectSerialNumber;
//
// Queue the request to the async thread.
//
SockQueueRequestToAsyncThread( contextBlock );
SockReleaseGlobalLock( );
return;
} // SockReenableAsyncSelectEvent
VOID
SockRemoveAsyncSelectRequests (
IN SOCKET Handle
)
{
PLIST_ENTRY entry;
PWINSOCK_CONTEXT_BLOCK contextBlock;
//
// If the async thread has not been initialized, there will be nothing
// queued to it, so return.
//
if ( !SockAsyncThreadInitialized ) {
return;
}
//
// Hold the lock that protects the async thread context block queue
// while we do this. This prevents the async thread from starting
// new requests while cancel any async select requests for this socket
// handle.
//
SockAcquireGlobalLockExclusive( );
//
// Look for async selects on this socket handle.
//
for ( entry = SockAsyncQueueHead.Flink;
entry != &SockAsyncQueueHead; ) {
contextBlock = CONTAINING_RECORD(
entry,
WINSOCK_CONTEXT_BLOCK,
AsyncThreadQueueListEntry
);
if ( contextBlock->OpCode == WS_OPCODE_ASYNC_SELECT &&
contextBlock->Overlay.AsyncSelect.SocketHandle == Handle ) {
IF_DEBUG(ASYNC_SELECT) {
WS_PRINT(( "SockRemoveAsyncSelectRequests: found async select "
"context block %lx for socket %lx\n",
contextBlock, Handle ));
}
//
// Remote the context block from the list and free it.
//
RemoveEntryList( entry );
//
// Get the next entry in the list now, since we're about to
// free the context block and won't be able to access the
// next entry after we free the context block.
//
entry = entry->Flink;
SockFreeContextBlock( contextBlock );
} else {
entry = entry->Flink;
}
//
// Continue seaching for other async select requests, in
// case there were multiple requests on the queue.
//
}
//
// Release the lock and return.
//
SockReleaseGlobalLock( );
return;
} // SockRemoveAsyncSelectRequests
int PASCAL FAR
__WSAFDIsSet (
SOCKET fd,
fd_set FAR *set
)
/*++
Routine Description:
This routine is used by the FD_ISSET macro; applications should
not call it directly. It determines whether a socket handle is
included in an fd_set structure.
Arguments:
fd - The socket handle to look for.
set - The fd_set structure to examine.
Return Value:
TRUE if the socket handle is in the set, FALSE if it is not.
--*/
{
int i = (set->fd_count & 0xFFFF);
while (i--) {
if (set->fd_array[i] == fd) {
return 1;
}
}
return 0;
} // __WSAFDIsSet