|
这个巧妙的做法是取得socket底层的afd的句柄,然后将select的调用变成NtDeviceIoControlFile调用,就可以跟iocp进行关联起来了。
有人可能说这个没有用,这个的用处在于使用第三方库。
注:在xp,2003平台上无效,具体原因看代码。
int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
uv_os_sock_t socket) {
WSAPROTOCOL_INFOW protocol_info;
int len;
SOCKET peer_socket, base_socket;
DWORD bytes;
DWORD yes = 1;
/* Set the socket to nonblocking mode */
if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR)
return uv_translate_sys_error(WSAGetLastError());
/* Try to obtain a base handle for the socket. This increases this chances */
/* that we find an AFD handle and are able to use the fast poll mechanism. */
/* This will always fail on windows XP/2k3, since they don't support the */
/* SIO_BASE_HANDLE ioctl. */
#ifndef NDEBUG
base_socket = INVALID_SOCKET;
#endif
if (WSAIoctl(socket,
SIO_BASE_HANDLE,
NULL,
0,
&base_socket,
sizeof base_socket,
&bytes,
NULL,
NULL) == 0) {
assert(base_socket != 0 && base_socket != INVALID_SOCKET);
socket = base_socket;
}
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
handle->socket = socket;
handle->events = 0;
/* Obtain protocol information about the socket. */
len = sizeof protocol_info;
if (getsockopt(socket,
SOL_SOCKET,
SO_PROTOCOL_INFOW,
(char*) &protocol_info,
&len) != 0) {
return uv_translate_sys_error(WSAGetLastError());
}
/* Get the peer socket that is needed to enable fast poll. If the returned */
/* value is NULL, the protocol is not implemented by MSAFD and we'll have */
/* to use slow mode. */
peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info);
if (peer_socket != INVALID_SOCKET) {
/* Initialize fast poll specific fields. */
handle->peer_socket = peer_socket;
} else {
/* Initialize slow poll specific fields. */
handle->flags |= UV_HANDLE_POLL_SLOW;
}
/* Initialize 2 poll reqs. */
handle->submitted_events_1 = 0;
uv_req_init(loop, (uv_req_t*) &(handle->poll_req_1));
handle->poll_req_1.type = UV_POLL_REQ;
handle->poll_req_1.data = handle;
handle->submitted_events_2 = 0;
uv_req_init(loop, (uv_req_t*) &(handle->poll_req_2));
handle->poll_req_2.type = UV_POLL_REQ;
handle->poll_req_2.data = handle;
return 0;
}
static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
uv_req_t* req;
AFD_POLL_INFO* afd_poll_info;
DWORD result;
/* Find a yet unsubmitted req to submit. */
if (handle->submitted_events_1 == 0) {
req = &handle->poll_req_1;
afd_poll_info = &handle->afd_poll_info_1;
handle->submitted_events_1 = handle->events;
handle->mask_events_1 = 0;
handle->mask_events_2 = handle->events;
} else if (handle->submitted_events_2 == 0) {
req = &handle->poll_req_2;
afd_poll_info = &handle->afd_poll_info_2;
handle->submitted_events_2 = handle->events;
handle->mask_events_1 = handle->events;
handle->mask_events_2 = 0;
} else {
assert(0);
return;
}
/* Setting Exclusive to TRUE makes the other poll request return if there */
/* is any. */
afd_poll_info->Exclusive = TRUE;
afd_poll_info->NumberOfHandles = 1;
afd_poll_info->Timeout.QuadPart = INT64_MAX;
afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket;
afd_poll_info->Handles[0].Status = 0;
afd_poll_info->Handles[0].Events = 0;
if (handle->events & UV_READABLE) {
afd_poll_info->Handles[0].Events |= AFD_POLL_RECEIVE |
AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT;
}
if (handle->events & UV_WRITABLE) {
afd_poll_info->Handles[0].Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL;
}
memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped);
result = uv_msafd_poll((SOCKET) handle->peer_socket,
afd_poll_info,
afd_poll_info,
&req->u.io.overlapped);
if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) {
/* Queue this req, reporting an error. */
SET_REQ_ERROR(req, WSAGetLastError());
uv_insert_pending_req(loop, req);
}
}
int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
IO_STATUS_BLOCK iosb;
IO_STATUS_BLOCK* iosb_ptr;
HANDLE event = NULL;
void* apc_context;
NTSTATUS status;
DWORD error;
if (overlapped != NULL) {
/* Overlapped operation. */
iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;
event = overlapped->hEvent;
/* Do not report iocp completion if hEvent is tagged. */
if ((uintptr_t) event & 1) {
event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
apc_context = NULL;
} else {
apc_context = overlapped;
}
} else {
/* Blocking operation. */
iosb_ptr = &iosb;
event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (event == NULL) {
return SOCKET_ERROR;
}
apc_context = NULL;
}
iosb_ptr->Status = STATUS_PENDING;
status = pNtDeviceIoControlFile((HANDLE) socket,
event,
NULL,
apc_context,
iosb_ptr,
IOCTL_AFD_POLL,
info_in,
sizeof *info_in,
info_out,
sizeof *info_out);
if (overlapped == NULL) {
/* If this is a blocking operation, wait for the event to become */
/* signaled, and then grab the real status from the io status block. */
if (status == STATUS_PENDING) {
DWORD r = WaitForSingleObject(event, INFINITE);
if (r == WAIT_FAILED) {
DWORD saved_error = GetLastError();
CloseHandle(event);
WSASetLastError(saved_error);
return SOCKET_ERROR;
}
status = iosb.Status;
}
CloseHandle(event);
}
switch (status) {
case STATUS_SUCCESS:
error = ERROR_SUCCESS;
break;
case STATUS_PENDING:
error = WSA_IO_PENDING;
break;
default:
error = uv_ntstatus_to_winsock_error(status);
break;
}
WSASetLastError(error);
if (error == ERROR_SUCCESS) {
return 0;
} else {
return SOCKET_ERROR;
}
}
|
|