mirror of https://github.com/lukechilds/node.git
Browse Source
The event loop's reference counting scheme in this version of libuv has changed. Update the libuv bindings to reflect that fact.v0.9.1-release
Ben Noordhuis
13 years ago
85 changed files with 3508 additions and 1289 deletions
@ -1,80 +0,0 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "uv.h" |
|||
#include "internal.h" |
|||
|
|||
|
|||
static void uv__check(EV_P_ ev_check* w, int revents) { |
|||
uv_check_t* check = container_of(w, uv_check_t, check_watcher); |
|||
|
|||
if (check->check_cb) { |
|||
check->check_cb(check, 0); |
|||
} |
|||
} |
|||
|
|||
|
|||
int uv_check_init(uv_loop_t* loop, uv_check_t* check) { |
|||
uv__handle_init(loop, (uv_handle_t*)check, UV_CHECK); |
|||
loop->counters.check_init++; |
|||
|
|||
ev_check_init(&check->check_watcher, uv__check); |
|||
check->check_cb = NULL; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_check_start(uv_check_t* check, uv_check_cb cb) { |
|||
int was_active = ev_is_active(&check->check_watcher); |
|||
|
|||
check->check_cb = cb; |
|||
|
|||
ev_check_start(check->loop->ev, &check->check_watcher); |
|||
|
|||
if (!was_active) { |
|||
ev_unref(check->loop->ev); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_check_stop(uv_check_t* check) { |
|||
int was_active = ev_is_active(&check->check_watcher); |
|||
|
|||
ev_check_stop(check->loop->ev, &check->check_watcher); |
|||
|
|||
if (was_active) { |
|||
ev_ref(check->loop->ev); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv__check_active(const uv_check_t* handle) { |
|||
return ev_is_active(&handle->check_watcher); |
|||
} |
|||
|
|||
|
|||
void uv__check_close(uv_check_t* handle) { |
|||
uv_check_stop(handle); |
|||
} |
@ -0,0 +1,123 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "uv.h" |
|||
#include "internal.h" |
|||
|
|||
#include <unistd.h> |
|||
#include <assert.h> |
|||
#include <errno.h> |
|||
|
|||
|
|||
static void uv__poll_io(EV_P_ ev_io* watcher, int ev_events) { |
|||
uv_poll_t* handle = watcher->data; |
|||
int events; |
|||
|
|||
if (ev_events & EV_ERROR) { |
|||
/* An error happened. Libev has implicitly stopped the watcher, but we */ |
|||
/* need to fix the refcount. */ |
|||
uv__handle_stop(handle); |
|||
uv__set_sys_error(handle->loop, EBADF); |
|||
handle->poll_cb(handle, -1, 0); |
|||
return; |
|||
} |
|||
|
|||
assert(ev_events & (EV_READ | EV_WRITE)); |
|||
assert((ev_events & ~(EV_READ | EV_WRITE)) == 0); |
|||
|
|||
events = 0; |
|||
if (ev_events & EV_READ) |
|||
events |= UV_READABLE; |
|||
if (ev_events & EV_WRITE) |
|||
events |= UV_WRITABLE; |
|||
|
|||
handle->poll_cb(handle, 0, events); |
|||
} |
|||
|
|||
|
|||
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { |
|||
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL); |
|||
loop->counters.poll_init++; |
|||
|
|||
handle->fd = fd; |
|||
handle->poll_cb = NULL; |
|||
|
|||
ev_init(&handle->io_watcher, uv__poll_io); |
|||
handle->io_watcher.data = handle; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, |
|||
uv_os_sock_t socket) { |
|||
return uv_poll_init(loop, handle, socket); |
|||
} |
|||
|
|||
|
|||
static void uv__poll_stop(uv_poll_t* handle) { |
|||
ev_io_stop(handle->loop->ev, &handle->io_watcher); |
|||
uv__handle_stop(handle); |
|||
} |
|||
|
|||
|
|||
int uv_poll_stop(uv_poll_t* handle) { |
|||
assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); |
|||
uv__poll_stop(handle); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb poll_cb) { |
|||
int ev_events; |
|||
|
|||
assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); |
|||
assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); |
|||
|
|||
if (events == 0) { |
|||
uv__poll_stop(handle); |
|||
return 0; |
|||
} |
|||
|
|||
ev_events = 0; |
|||
if (events & UV_READABLE) |
|||
ev_events |= EV_READ; |
|||
if (events & UV_WRITABLE) |
|||
ev_events |= EV_WRITE; |
|||
|
|||
ev_io_set(&handle->io_watcher, handle->fd, ev_events); |
|||
ev_io_start(handle->loop->ev, &handle->io_watcher); |
|||
|
|||
handle->poll_cb = poll_cb; |
|||
uv__handle_start(handle); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
void uv__poll_close(uv_poll_t* handle) { |
|||
uv__poll_stop(handle); |
|||
} |
|||
|
|||
|
|||
int uv__poll_active(const uv_poll_t* handle) { |
|||
return ev_is_active(&handle->io_watcher); |
|||
} |
@ -1,79 +0,0 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "uv.h" |
|||
#include "internal.h" |
|||
|
|||
|
|||
static void uv__prepare(EV_P_ ev_prepare* w, int revents) { |
|||
uv_prepare_t* prepare = container_of(w, uv_prepare_t, prepare_watcher); |
|||
|
|||
if (prepare->prepare_cb) { |
|||
prepare->prepare_cb(prepare, 0); |
|||
} |
|||
} |
|||
|
|||
|
|||
int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) { |
|||
uv__handle_init(loop, (uv_handle_t*)prepare, UV_PREPARE); |
|||
loop->counters.prepare_init++; |
|||
|
|||
ev_prepare_init(&prepare->prepare_watcher, uv__prepare); |
|||
prepare->prepare_cb = NULL; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) { |
|||
int was_active = ev_is_active(&prepare->prepare_watcher); |
|||
|
|||
prepare->prepare_cb = cb; |
|||
|
|||
ev_prepare_start(prepare->loop->ev, &prepare->prepare_watcher); |
|||
|
|||
if (!was_active) { |
|||
ev_unref(prepare->loop->ev); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_prepare_stop(uv_prepare_t* prepare) { |
|||
int was_active = ev_is_active(&prepare->prepare_watcher); |
|||
|
|||
ev_prepare_stop(prepare->loop->ev, &prepare->prepare_watcher); |
|||
|
|||
if (was_active) { |
|||
ev_ref(prepare->loop->ev); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv__prepare_active(const uv_prepare_t* handle) { |
|||
return ev_is_active(&handle->prepare_watcher); |
|||
} |
|||
|
|||
|
|||
void uv__prepare_close(uv_prepare_t* handle) { |
|||
uv_prepare_stop(handle); |
|||
} |
@ -0,0 +1,620 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
|
|||
#include "uv.h" |
|||
#include "internal.h" |
|||
|
|||
#include <assert.h> |
|||
#include <io.h> |
|||
|
|||
|
|||
static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = { |
|||
{0xe70f1aa0, 0xab8b, 0x11cf, |
|||
{0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}, |
|||
{0xf9eab0c0, 0x26d4, 0x11d0, |
|||
{0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}}, |
|||
{0x9fc48064, 0x7298, 0x43e4, |
|||
{0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}} |
|||
}; |
|||
|
|||
typedef struct uv_single_fd_set_s { |
|||
unsigned int fd_count; |
|||
SOCKET fd_array[1]; |
|||
} uv_single_fd_set_t; |
|||
|
|||
|
|||
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); |
|||
} |
|||
|
|||
/* 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->overlapped, 0, sizeof req->overlapped); |
|||
|
|||
result = uv_msafd_poll((SOCKET) handle->peer_socket, |
|||
afd_poll_info, |
|||
&req->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); |
|||
} |
|||
} |
|||
|
|||
|
|||
static int uv__fast_poll_cancel_poll_reqs(uv_loop_t* loop, uv_poll_t* handle) { |
|||
AFD_POLL_INFO afd_poll_info; |
|||
DWORD result; |
|||
HANDLE event; |
|||
OVERLAPPED overlapped; |
|||
|
|||
event = CreateEvent(NULL, TRUE, FALSE, NULL); |
|||
if (event == NULL) { |
|||
uv__set_sys_error(loop, GetLastError()); |
|||
return -1; |
|||
} |
|||
|
|||
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 = AFD_POLL_ALL; |
|||
|
|||
memset(&overlapped, 0, sizeof overlapped); |
|||
overlapped.hEvent = (HANDLE) ((uintptr_t) event & 1); |
|||
|
|||
result = uv_msafd_poll(handle->socket, |
|||
&afd_poll_info, |
|||
&overlapped); |
|||
|
|||
if (result == SOCKET_ERROR) { |
|||
DWORD error = WSAGetLastError(); |
|||
if (error != WSA_IO_PENDING) { |
|||
uv__set_sys_error(loop, WSAGetLastError()); |
|||
CloseHandle(event); |
|||
return -1; |
|||
} |
|||
} |
|||
|
|||
CloseHandle(event); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, |
|||
uv_req_t* req) { |
|||
unsigned char mask_events; |
|||
AFD_POLL_INFO* afd_poll_info; |
|||
|
|||
if (req == &handle->poll_req_1) { |
|||
afd_poll_info = &handle->afd_poll_info_1; |
|||
handle->submitted_events_1 = 0; |
|||
mask_events = handle->mask_events_1; |
|||
} else if (req == &handle->poll_req_2) { |
|||
afd_poll_info = &handle->afd_poll_info_2; |
|||
handle->submitted_events_2 = 0; |
|||
mask_events = handle->mask_events_2; |
|||
} else { |
|||
assert(0); |
|||
} |
|||
|
|||
/* Report an error unless the select was just interrupted. */ |
|||
if (!REQ_SUCCESS(req)) { |
|||
DWORD error = GET_REQ_SOCK_ERROR(req); |
|||
if (error != WSAEINTR && handle->events != 0) { |
|||
handle->events = 0; /* Stop the watcher */ |
|||
uv__set_sys_error(loop, error); |
|||
handle->poll_cb(handle, -1, 0); |
|||
} |
|||
|
|||
} else if (afd_poll_info->NumberOfHandles >= 1) { |
|||
unsigned char events = 0; |
|||
|
|||
if ((afd_poll_info->Handles[0].Events & (AFD_POLL_RECEIVE | |
|||
AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT)) != 0) { |
|||
events |= UV_READABLE; |
|||
} |
|||
if ((afd_poll_info->Handles[0].Events & (AFD_POLL_SEND | |
|||
AFD_POLL_CONNECT_FAIL)) != 0) { |
|||
events |= UV_WRITABLE; |
|||
} |
|||
|
|||
events &= handle->events & ~mask_events; |
|||
|
|||
if (afd_poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) { |
|||
/* Stop polling. */ |
|||
handle->events = 0; |
|||
} |
|||
|
|||
if (events != 0) { |
|||
handle->poll_cb(handle, 0, events); |
|||
} |
|||
} |
|||
|
|||
if ((handle->events & ~(handle->submitted_events_1 | |
|||
handle->submitted_events_2)) != 0) { |
|||
uv__fast_poll_submit_poll_req(loop, handle); |
|||
} else if ((handle->flags & UV_HANDLE_CLOSING) && |
|||
handle->submitted_events_1 == 0 && |
|||
handle->submitted_events_2 == 0) { |
|||
uv_want_endgame(loop, (uv_handle_t*) handle); |
|||
} |
|||
} |
|||
|
|||
|
|||
static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { |
|||
assert(handle->type == UV_POLL); |
|||
assert(!(handle->flags & UV_HANDLE_CLOSING)); |
|||
assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); |
|||
|
|||
handle->events = events; |
|||
|
|||
if (handle->events != 0) { |
|||
uv__handle_start(handle); |
|||
} else { |
|||
uv__handle_stop(handle); |
|||
} |
|||
|
|||
if ((handle->events & ~(handle->submitted_events_1 | |
|||
handle->submitted_events_2)) != 0) { |
|||
uv__fast_poll_submit_poll_req(handle->loop, handle); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static void uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) { |
|||
handle->events = 0; |
|||
uv__handle_start(handle); |
|||
|
|||
if (handle->submitted_events_1 == 0 && |
|||
handle->submitted_events_2 == 0) { |
|||
uv_want_endgame(loop, (uv_handle_t*) handle); |
|||
} else { |
|||
/* Try to cancel outstanding poll requests. */ |
|||
if (pCancelIoEx) { |
|||
/* Use CancelIoEx to cancel poll requests if available. */ |
|||
if (handle->submitted_events_1) |
|||
pCancelIoEx((HANDLE) handle->socket, &handle->poll_req_1.overlapped); |
|||
if (handle->submitted_events_2) |
|||
pCancelIoEx((HANDLE) handle->socket, &handle->poll_req_2.overlapped); |
|||
} else if (handle->submitted_events_1 | handle->submitted_events_2) { |
|||
/* Execute another unique poll to force the others to return. */ |
|||
uv__fast_poll_cancel_poll_reqs(loop, handle); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp, |
|||
WSAPROTOCOL_INFOW* protocol_info) { |
|||
SOCKET sock = 0; |
|||
|
|||
sock = WSASocketW(protocol_info->iAddressFamily, |
|||
protocol_info->iSocketType, |
|||
protocol_info->iProtocol, |
|||
protocol_info, |
|||
0, |
|||
WSA_FLAG_OVERLAPPED); |
|||
if (sock == INVALID_SOCKET) { |
|||
return INVALID_SOCKET; |
|||
} |
|||
|
|||
if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) { |
|||
goto error; |
|||
}; |
|||
|
|||
if (CreateIoCompletionPort((HANDLE) sock, |
|||
iocp, |
|||
(ULONG_PTR) sock, |
|||
0) == NULL) { |
|||
goto error; |
|||
} |
|||
|
|||
return sock; |
|||
|
|||
error: |
|||
closesocket(sock); |
|||
return INVALID_SOCKET; |
|||
} |
|||
|
|||
|
|||
static SOCKET uv__fast_poll_get_peer_socket(uv_loop_t* loop, |
|||
WSAPROTOCOL_INFOW* protocol_info) { |
|||
int index, i; |
|||
SOCKET peer_socket; |
|||
|
|||
index = -1; |
|||
for (i = 0; i < ARRAY_SIZE(uv_msafd_provider_ids); i++) { |
|||
if (memcmp((void*) &protocol_info->ProviderId, |
|||
(void*) &uv_msafd_provider_ids[i], |
|||
sizeof protocol_info->ProviderId) == 0) { |
|||
index = i; |
|||
} |
|||
} |
|||
|
|||
/* Check if the protocol uses an msafd socket. */ |
|||
if (index < 0) { |
|||
return INVALID_SOCKET; |
|||
} |
|||
|
|||
/* If we didn't (try) to create a peer socket yet, try to make one. Don't */ |
|||
/* try again if the peer socket creation failed earlier for the same */ |
|||
/* protocol. */ |
|||
peer_socket = loop->poll_peer_sockets[index]; |
|||
if (peer_socket == 0) { |
|||
peer_socket = uv__fast_poll_create_peer_socket(loop->iocp, protocol_info); |
|||
loop->poll_peer_sockets[index] = peer_socket; |
|||
} |
|||
|
|||
return peer_socket; |
|||
} |
|||
|
|||
|
|||
static DWORD WINAPI uv__slow_poll_thread_proc(void* arg) { |
|||
uv_req_t* req = (uv_req_t*) arg; |
|||
uv_poll_t* handle = (uv_poll_t*) req->data; |
|||
unsigned char events, reported_events; |
|||
int r; |
|||
uv_single_fd_set_t rfds, wfds, efds; |
|||
struct timeval timeout; |
|||
|
|||
assert(handle->type == UV_POLL); |
|||
assert(req->type == UV_POLL_REQ); |
|||
|
|||
if (req == &handle->poll_req_1) { |
|||
events = handle->submitted_events_1; |
|||
} else if (req == &handle->poll_req_2) { |
|||
events = handle->submitted_events_2; |
|||
} else { |
|||
assert(0); |
|||
} |
|||
|
|||
if (handle->events & UV_READABLE) { |
|||
rfds.fd_count = 1; |
|||
rfds.fd_array[0] = handle->socket; |
|||
} else { |
|||
rfds.fd_count = 0; |
|||
} |
|||
|
|||
if (handle->events & UV_WRITABLE) { |
|||
wfds.fd_count = 1; |
|||
wfds.fd_array[0] = handle->socket; |
|||
efds.fd_count = 1; |
|||
efds.fd_array[0] = handle->socket; |
|||
} else { |
|||
wfds.fd_count = 0; |
|||
efds.fd_count = 0; |
|||
} |
|||
|
|||
/* Make the select() time out after 3 minutes. If select() hangs because */ |
|||
/* the user closed the socket, we will at least not hang indefinitely. */ |
|||
timeout.tv_sec = 3 * 60; |
|||
timeout.tv_usec = 0; |
|||
|
|||
r = select(1, (fd_set*) &rfds, (fd_set*) &wfds, (fd_set*) &efds, &timeout); |
|||
if (r == SOCKET_ERROR) { |
|||
/* Queue this req, reporting an error. */ |
|||
SET_REQ_ERROR(&handle->poll_req_1, WSAGetLastError()); |
|||
POST_COMPLETION_FOR_REQ(handle->loop, req); |
|||
return 0; |
|||
} |
|||
|
|||
reported_events = 0; |
|||
|
|||
if (r > 0) { |
|||
if (rfds.fd_count > 0) { |
|||
assert(rfds.fd_count == 1); |
|||
assert(rfds.fd_array[0] == handle->socket); |
|||
reported_events |= UV_READABLE; |
|||
} |
|||
|
|||
if (wfds.fd_count > 0) { |
|||
assert(wfds.fd_count == 1); |
|||
assert(wfds.fd_array[0] == handle->socket); |
|||
reported_events |= UV_WRITABLE; |
|||
} else if (efds.fd_count > 0) { |
|||
assert(efds.fd_count == 1); |
|||
assert(efds.fd_array[0] == handle->socket); |
|||
reported_events |= UV_WRITABLE; |
|||
} |
|||
} |
|||
|
|||
SET_REQ_SUCCESS(req); |
|||
req->overlapped.InternalHigh = (DWORD) reported_events; |
|||
POST_COMPLETION_FOR_REQ(handle->loop, req); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { |
|||
uv_req_t* req; |
|||
|
|||
/* Find a yet unsubmitted req to submit. */ |
|||
if (handle->submitted_events_1 == 0) { |
|||
req = &handle->poll_req_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; |
|||
handle->submitted_events_2 = handle->events; |
|||
handle->mask_events_1 = handle->events; |
|||
handle->mask_events_2 = 0; |
|||
} else { |
|||
assert(0); |
|||
} |
|||
|
|||
if (!QueueUserWorkItem(uv__slow_poll_thread_proc, |
|||
(void*) req, |
|||
WT_EXECUTELONGFUNCTION)) { |
|||
/* Make this req pending, reporting an error. */ |
|||
SET_REQ_ERROR(req, GetLastError()); |
|||
uv_insert_pending_req(loop, req); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, |
|||
uv_req_t* req) { |
|||
unsigned char mask_events; |
|||
if (req == &handle->poll_req_1) { |
|||
handle->submitted_events_1 = 0; |
|||
mask_events = handle->mask_events_1; |
|||
} else if (req == &handle->poll_req_2) { |
|||
handle->submitted_events_2 = 0; |
|||
mask_events = handle->mask_events_2; |
|||
} else { |
|||
assert(0); |
|||
} |
|||
|
|||
if (!REQ_SUCCESS(req)) { |
|||
/* Error. */ |
|||
if (handle->events != 0) { |
|||
handle->events = 0; /* Stop the watcher */ |
|||
uv__set_sys_error(loop, GET_REQ_ERROR(req)); |
|||
handle->poll_cb(handle, -1, 0); |
|||
} |
|||
} else { |
|||
/* Got some events. */ |
|||
int events = req->overlapped.InternalHigh & handle->events & ~mask_events; |
|||
if (events != 0) { |
|||
handle->poll_cb(handle, 0, events); |
|||
} |
|||
} |
|||
|
|||
if ((handle->events & ~(handle->submitted_events_1 | |
|||
handle->submitted_events_2)) != 0) { |
|||
uv__slow_poll_submit_poll_req(loop, handle); |
|||
} else if ((handle->flags & UV_HANDLE_CLOSING) && |
|||
handle->submitted_events_1 == 0 && |
|||
handle->submitted_events_2 == 0) { |
|||
uv_want_endgame(loop, (uv_handle_t*) handle); |
|||
} |
|||
} |
|||
|
|||
|
|||
static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { |
|||
assert(handle->type == UV_POLL); |
|||
assert(!(handle->flags & UV_HANDLE_CLOSING)); |
|||
assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); |
|||
|
|||
handle->events = events; |
|||
|
|||
if (handle->events != 0) { |
|||
uv__handle_start(handle); |
|||
} else { |
|||
uv__handle_stop(handle); |
|||
} |
|||
|
|||
if ((handle->events & |
|||
~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { |
|||
uv__slow_poll_submit_poll_req(handle->loop, handle); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static void uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) { |
|||
handle->events = 0; |
|||
uv__handle_start(handle); |
|||
|
|||
if (handle->submitted_events_1 == 0 && |
|||
handle->submitted_events_2 == 0) { |
|||
uv_want_endgame(loop, (uv_handle_t*) handle); |
|||
} |
|||
} |
|||
|
|||
|
|||
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { |
|||
return uv_poll_init_socket(loop, handle, (SOCKET) _get_osfhandle(fd)); |
|||
} |
|||
|
|||
|
|||
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; |
|||
|
|||
/* 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); |
|||
handle->type = 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) { |
|||
uv__set_sys_error(loop, WSAGetLastError()); |
|||
return -1; |
|||
} |
|||
|
|||
/* 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; |
|||
} |
|||
|
|||
/* Intialize 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; |
|||
|
|||
loop->counters.poll_init++; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { |
|||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { |
|||
if (uv__fast_poll_set(handle->loop, handle, events) < 0) |
|||
return -1; |
|||
} else { |
|||
if (uv__slow_poll_set(handle->loop, handle, events) < 0) |
|||
return -1; |
|||
} |
|||
|
|||
handle->poll_cb = cb; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv_poll_stop(uv_poll_t* handle) { |
|||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { |
|||
return uv__fast_poll_set(handle->loop, handle, 0); |
|||
} else { |
|||
return uv__slow_poll_set(handle->loop, handle, 0); |
|||
} |
|||
} |
|||
|
|||
|
|||
void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { |
|||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { |
|||
uv__fast_poll_process_poll_req(loop, handle, req); |
|||
} else { |
|||
uv__slow_poll_process_poll_req(loop, handle, req); |
|||
} |
|||
} |
|||
|
|||
|
|||
void uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { |
|||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { |
|||
uv__fast_poll_close(loop, handle); |
|||
} else { |
|||
uv__slow_poll_close(loop, handle); |
|||
} |
|||
} |
|||
|
|||
|
|||
void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { |
|||
assert(handle->flags & UV_HANDLE_CLOSING); |
|||
assert(!(handle->flags & UV_HANDLE_CLOSED)); |
|||
|
|||
assert(handle->submitted_events_1 == 0); |
|||
assert(handle->submitted_events_2 == 0); |
|||
|
|||
handle->flags |= UV_HANDLE_CLOSED; |
|||
uv__handle_stop(handle); |
|||
|
|||
if (handle->close_cb) { |
|||
handle->close_cb((uv_handle_t*)handle); |
|||
} |
|||
} |
@ -0,0 +1,573 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include <errno.h> |
|||
|
|||
#ifndef _WIN32 |
|||
# include <fcntl.h> |
|||
# include <sys/socket.h> |
|||
# include <unistd.h> |
|||
#endif |
|||
|
|||
#include "uv.h" |
|||
#include "task.h" |
|||
|
|||
|
|||
#define NUM_CLIENTS 5 |
|||
#define TRANSFER_BYTES (1 << 16) |
|||
|
|||
#undef MIN |
|||
#define MIN(a, b) (((a) < (b)) ? (a) : (b)); |
|||
|
|||
|
|||
typedef enum { |
|||
UNIDIRECTIONAL, |
|||
DUPLEX |
|||
} test_mode_t; |
|||
|
|||
typedef struct connection_context_s { |
|||
uv_poll_t poll_handle; |
|||
uv_timer_t timer_handle; |
|||
uv_os_sock_t sock; |
|||
size_t read, sent; |
|||
int is_server_connection; |
|||
int open_handles; |
|||
int got_fin, sent_fin; |
|||
unsigned int events, delayed_events; |
|||
} connection_context_t; |
|||
|
|||
typedef struct server_context_s { |
|||
uv_poll_t poll_handle; |
|||
uv_os_sock_t sock; |
|||
int connections; |
|||
} server_context_t; |
|||
|
|||
|
|||
static void delay_timer_cb(uv_timer_t* timer, int status); |
|||
|
|||
|
|||
static test_mode_t test_mode = DUPLEX; |
|||
|
|||
static int closed_connections = 0; |
|||
|
|||
static int valid_writable_wakeups = 0; |
|||
static int spurious_writable_wakeups = 0; |
|||
|
|||
|
|||
static int got_eagain() { |
|||
#ifdef _WIN32 |
|||
return WSAGetLastError() == WSAEWOULDBLOCK; |
|||
#else |
|||
return errno == EAGAIN |
|||
|| errno == EINPROGRESS |
|||
#ifdef EWOULDBLOCK |
|||
|| errno == EWOULDBLOCK; |
|||
#endif |
|||
; |
|||
#endif |
|||
} |
|||
|
|||
|
|||
static void set_nonblocking(uv_os_sock_t sock) { |
|||
int r; |
|||
#ifdef _WIN32 |
|||
unsigned long on = 1; |
|||
r = ioctlsocket(sock, FIONBIO, &on); |
|||
ASSERT(r == 0); |
|||
#else |
|||
int flags = fcntl(sock, F_GETFL, 0); |
|||
ASSERT(flags >= 0); |
|||
r = fcntl(sock, F_SETFL, flags | O_NONBLOCK); |
|||
ASSERT(r >= 0); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
static uv_os_sock_t create_nonblocking_bound_socket( |
|||
struct sockaddr_in bind_addr) { |
|||
uv_os_sock_t sock; |
|||
int r; |
|||
|
|||
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); |
|||
#ifdef _WIN32 |
|||
ASSERT(sock != INVALID_SOCKET); |
|||
#else |
|||
ASSERT(sock >= 0); |
|||
#endif |
|||
|
|||
set_nonblocking(sock); |
|||
|
|||
#ifndef _WIN32 |
|||
{ |
|||
/* Allow reuse of the port. */ |
|||
int yes = 1; |
|||
r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); |
|||
ASSERT(r == 0); |
|||
} |
|||
#endif |
|||
|
|||
r = bind(sock, (const struct sockaddr*) &bind_addr, sizeof bind_addr); |
|||
ASSERT(r == 0); |
|||
|
|||
return sock; |
|||
} |
|||
|
|||
|
|||
static void close_socket(uv_os_sock_t sock) { |
|||
int r; |
|||
#ifdef _WIN32 |
|||
r = closesocket(sock); |
|||
#else |
|||
r = close(sock); |
|||
#endif |
|||
ASSERT(r == 0); |
|||
} |
|||
|
|||
|
|||
static connection_context_t* create_connection_context( |
|||
uv_os_sock_t sock, int is_server_connection) { |
|||
int r; |
|||
connection_context_t* context; |
|||
|
|||
context = (connection_context_t*) malloc(sizeof *context); |
|||
ASSERT(context != NULL); |
|||
|
|||
context->sock = sock; |
|||
context->is_server_connection = is_server_connection; |
|||
context->read = 0; |
|||
context->sent = 0; |
|||
context->open_handles = 0; |
|||
context->events = 0; |
|||
context->delayed_events = 0; |
|||
context->got_fin = 0; |
|||
context->sent_fin = 0; |
|||
|
|||
r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock); |
|||
context->open_handles++; |
|||
context->poll_handle.data = context; |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_timer_init(uv_default_loop(), &context->timer_handle); |
|||
context->open_handles++; |
|||
context->timer_handle.data = context; |
|||
ASSERT(r == 0); |
|||
|
|||
return context; |
|||
} |
|||
|
|||
|
|||
static void connection_close_cb(uv_handle_t* handle) { |
|||
connection_context_t* context = (connection_context_t*) handle->data; |
|||
|
|||
if (--context->open_handles == 0) { |
|||
if (test_mode == DUPLEX || context->is_server_connection) { |
|||
ASSERT(context->read == TRANSFER_BYTES); |
|||
} else { |
|||
ASSERT(context->read == 0); |
|||
} |
|||
|
|||
if (test_mode == DUPLEX || !context->is_server_connection) { |
|||
ASSERT(context->sent == TRANSFER_BYTES); |
|||
} else { |
|||
ASSERT(context->sent == 0); |
|||
} |
|||
|
|||
closed_connections++; |
|||
|
|||
free(context); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void destroy_connection_context(connection_context_t* context) { |
|||
uv_close((uv_handle_t*) &context->poll_handle, connection_close_cb); |
|||
uv_close((uv_handle_t*) &context->timer_handle, connection_close_cb); |
|||
} |
|||
|
|||
|
|||
static void connection_poll_cb(uv_poll_t* handle, int status, int events) { |
|||
connection_context_t* context = (connection_context_t*) handle->data; |
|||
int new_events; |
|||
int r; |
|||
|
|||
ASSERT(status == 0); |
|||
ASSERT(events & context->events); |
|||
ASSERT(!(events & ~context->events)); |
|||
|
|||
new_events = context->events; |
|||
|
|||
if (events & UV_READABLE) { |
|||
int action = rand() % 7; |
|||
|
|||
switch (action) { |
|||
case 0: |
|||
case 1: { |
|||
/* Read a couple of bytes. */ |
|||
static char buffer[74]; |
|||
r = recv(context->sock, buffer, sizeof buffer, 0); |
|||
ASSERT(r >= 0); |
|||
|
|||
if (r > 0) { |
|||
context->read += r; |
|||
} else { |
|||
/* Got FIN. */ |
|||
context->got_fin = 1; |
|||
new_events &= ~UV_READABLE; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
case 2: |
|||
case 3: { |
|||
/* Read until EAGAIN. */ |
|||
static char buffer[931]; |
|||
r = recv(context->sock, buffer, sizeof buffer, 0); |
|||
ASSERT(r >= 0); |
|||
|
|||
while (r > 0) { |
|||
context->read += r; |
|||
r = recv(context->sock, buffer, sizeof buffer, 0); |
|||
} |
|||
|
|||
if (r == 0) { |
|||
/* Got FIN. */ |
|||
context->got_fin = 1; |
|||
new_events &= ~UV_READABLE; |
|||
} else { |
|||
ASSERT(got_eagain()); |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
case 4: |
|||
/* Ignore. */ |
|||
break; |
|||
|
|||
case 5: |
|||
/* Stop reading for a while. Restart in timer callback. */ |
|||
new_events &= ~UV_READABLE; |
|||
if (!uv_is_active((uv_handle_t*) &context->timer_handle)) { |
|||
context->delayed_events = UV_READABLE; |
|||
uv_timer_start(&context->timer_handle, delay_timer_cb, 10, 0); |
|||
} else { |
|||
context->delayed_events |= UV_READABLE; |
|||
} |
|||
break; |
|||
|
|||
case 6: |
|||
/* Fudge with the event mask. */ |
|||
uv_poll_start(&context->poll_handle, UV_WRITABLE, connection_poll_cb); |
|||
uv_poll_start(&context->poll_handle, UV_READABLE, connection_poll_cb); |
|||
context->events = UV_READABLE; |
|||
break; |
|||
|
|||
default: |
|||
ASSERT(0); |
|||
} |
|||
} |
|||
|
|||
if (events & UV_WRITABLE) { |
|||
if (context->sent < TRANSFER_BYTES && |
|||
!(test_mode == UNIDIRECTIONAL && context->is_server_connection)) { |
|||
/* We have to send more bytes. */ |
|||
int action = rand() % 7; |
|||
|
|||
switch (action) { |
|||
case 0: |
|||
case 1: { |
|||
/* Send a couple of bytes. */ |
|||
static char buffer[103]; |
|||
|
|||
int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); |
|||
ASSERT(send_bytes > 0); |
|||
|
|||
r = send(context->sock, buffer, send_bytes, 0); |
|||
|
|||
if (r < 0) { |
|||
ASSERT(got_eagain()); |
|||
spurious_writable_wakeups++; |
|||
break; |
|||
} |
|||
|
|||
ASSERT(r > 0); |
|||
context->sent += r; |
|||
valid_writable_wakeups++; |
|||
break; |
|||
} |
|||
|
|||
case 2: |
|||
case 3: { |
|||
/* Send until EAGAIN. */ |
|||
static char buffer[1234]; |
|||
|
|||
int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); |
|||
ASSERT(send_bytes > 0); |
|||
|
|||
r = send(context->sock, buffer, send_bytes, 0); |
|||
|
|||
if (r < 0) { |
|||
ASSERT(got_eagain()); |
|||
spurious_writable_wakeups++; |
|||
break; |
|||
} |
|||
|
|||
ASSERT(r > 0); |
|||
valid_writable_wakeups++; |
|||
context->sent += r; |
|||
|
|||
while (context->sent < TRANSFER_BYTES) { |
|||
send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); |
|||
ASSERT(send_bytes > 0); |
|||
|
|||
r = send(context->sock, buffer, send_bytes, 0); |
|||
|
|||
if (r <= 0) break; |
|||
context->sent += r; |
|||
} |
|||
ASSERT(r > 0 || got_eagain()); |
|||
break; |
|||
} |
|||
|
|||
case 4: |
|||
/* Ignore. */ |
|||
break; |
|||
|
|||
case 5: |
|||
/* Stop sending for a while. Restart in timer callback. */ |
|||
new_events &= ~UV_WRITABLE; |
|||
if (!uv_is_active((uv_handle_t*) &context->timer_handle)) { |
|||
context->delayed_events = UV_WRITABLE; |
|||
uv_timer_start(&context->timer_handle, delay_timer_cb, 100, 0); |
|||
} else { |
|||
context->delayed_events |= UV_WRITABLE; |
|||
} |
|||
break; |
|||
|
|||
case 6: |
|||
/* Fudge with the event mask. */ |
|||
uv_poll_start(&context->poll_handle, |
|||
UV_READABLE, |
|||
connection_poll_cb); |
|||
uv_poll_start(&context->poll_handle, |
|||
UV_WRITABLE, |
|||
connection_poll_cb); |
|||
context->events = UV_WRITABLE; |
|||
break; |
|||
|
|||
default: |
|||
ASSERT(0); |
|||
} |
|||
|
|||
} else { |
|||
/* Nothing more to write. Send FIN. */ |
|||
int r; |
|||
#ifdef _WIN32 |
|||
r = shutdown(context->sock, SD_SEND); |
|||
#else |
|||
r = shutdown(context->sock, SHUT_WR); |
|||
#endif |
|||
ASSERT(r == 0); |
|||
context->sent_fin = 1; |
|||
new_events &= ~UV_WRITABLE; |
|||
} |
|||
} |
|||
|
|||
if (context->got_fin && context->sent_fin) { |
|||
/* Sent and received FIN. Close and destroy context. */ |
|||
close_socket(context->sock); |
|||
destroy_connection_context(context); |
|||
context->events = 0; |
|||
|
|||
} else if (new_events != context->events) { |
|||
/* Poll mask changed. Call uv_poll_start again. */ |
|||
context->events = new_events; |
|||
uv_poll_start(handle, new_events, connection_poll_cb); |
|||
} |
|||
|
|||
/* Assert that uv_is_active works correctly for poll handles. */ |
|||
if (context->events != 0) { |
|||
ASSERT(uv_is_active((uv_handle_t*) handle)); |
|||
} else { |
|||
ASSERT(!uv_is_active((uv_handle_t*) handle)); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void delay_timer_cb(uv_timer_t* timer, int status) { |
|||
connection_context_t* context = (connection_context_t*) timer->data; |
|||
int r; |
|||
|
|||
/* Timer should auto stop. */ |
|||
ASSERT(!uv_is_active((uv_handle_t*) timer)); |
|||
|
|||
/* Add the requested events to the poll mask. */ |
|||
ASSERT(context->delayed_events != 0); |
|||
context->events |= context->delayed_events; |
|||
context->delayed_events = 0; |
|||
|
|||
r = uv_poll_start(&context->poll_handle, |
|||
context->events, |
|||
connection_poll_cb); |
|||
ASSERT(r == 0); |
|||
} |
|||
|
|||
|
|||
static server_context_t* create_server_context( |
|||
uv_os_sock_t sock) { |
|||
int r; |
|||
server_context_t* context; |
|||
|
|||
context = (server_context_t*) malloc(sizeof *context); |
|||
ASSERT(context != NULL); |
|||
|
|||
context->sock = sock; |
|||
context->connections = 0; |
|||
|
|||
r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock); |
|||
context->poll_handle.data = context; |
|||
ASSERT(r == 0); |
|||
|
|||
return context; |
|||
} |
|||
|
|||
|
|||
static void server_close_cb(uv_handle_t* handle) { |
|||
server_context_t* context = (server_context_t*) handle->data; |
|||
free(context); |
|||
} |
|||
|
|||
|
|||
static void destroy_server_context(server_context_t* context) { |
|||
uv_close((uv_handle_t*) &context->poll_handle, server_close_cb); |
|||
} |
|||
|
|||
|
|||
static void server_poll_cb(uv_poll_t* handle, int status, int events) { |
|||
server_context_t* server_context = (server_context_t*) |
|||
handle->data; |
|||
connection_context_t* connection_context; |
|||
struct sockaddr_in addr; |
|||
socklen_t addr_len; |
|||
uv_os_sock_t sock; |
|||
int r; |
|||
|
|||
addr_len = sizeof addr; |
|||
sock = accept(server_context->sock, (struct sockaddr*) &addr, &addr_len); |
|||
#ifdef _WIN32 |
|||
ASSERT(sock != INVALID_SOCKET); |
|||
#else |
|||
ASSERT(sock >= 0); |
|||
#endif |
|||
|
|||
set_nonblocking(sock); |
|||
|
|||
connection_context = create_connection_context(sock, 1); |
|||
connection_context->events = UV_READABLE | UV_WRITABLE; |
|||
r = uv_poll_start(&connection_context->poll_handle, |
|||
UV_READABLE | UV_WRITABLE, |
|||
connection_poll_cb); |
|||
ASSERT(r == 0); |
|||
|
|||
if (++server_context->connections == NUM_CLIENTS) { |
|||
close_socket(server_context->sock); |
|||
destroy_server_context(server_context); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void start_server() { |
|||
uv_os_sock_t sock; |
|||
server_context_t* context; |
|||
int r; |
|||
|
|||
sock = create_nonblocking_bound_socket(uv_ip4_addr("127.0.0.1", TEST_PORT)); |
|||
context = create_server_context(sock); |
|||
|
|||
r = listen(sock, 100); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_poll_start(&context->poll_handle, UV_READABLE, server_poll_cb); |
|||
ASSERT(r == 0); |
|||
} |
|||
|
|||
|
|||
static void start_client() { |
|||
uv_os_sock_t sock; |
|||
connection_context_t* context; |
|||
struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
|||
int r; |
|||
|
|||
sock = create_nonblocking_bound_socket(uv_ip4_addr("0.0.0.0", 0)); |
|||
context = create_connection_context(sock, 0); |
|||
|
|||
context->events = UV_READABLE | UV_WRITABLE; |
|||
r = uv_poll_start(&context->poll_handle, |
|||
UV_READABLE | UV_WRITABLE, |
|||
connection_poll_cb); |
|||
ASSERT(r == 0); |
|||
|
|||
r = connect(sock, (struct sockaddr*) &server_addr, sizeof server_addr); |
|||
ASSERT(r == 0 || got_eagain()); |
|||
} |
|||
|
|||
|
|||
static void start_poll_test() { |
|||
int i, r; |
|||
|
|||
#ifdef _WIN32 |
|||
{ |
|||
struct WSAData wsa_data; |
|||
r = WSAStartup(MAKEWORD(2, 2), &wsa_data); |
|||
ASSERT(r == 0); |
|||
} |
|||
#endif |
|||
|
|||
start_server(); |
|||
|
|||
for (i = 0; i < NUM_CLIENTS; i++) |
|||
start_client(); |
|||
|
|||
r = uv_run(uv_default_loop()); |
|||
ASSERT(r == 0); |
|||
|
|||
/* Assert that at most one percent of the writable wakeups was spurious. */ |
|||
ASSERT(spurious_writable_wakeups == 0 || |
|||
(valid_writable_wakeups + spurious_writable_wakeups) / |
|||
spurious_writable_wakeups > 100); |
|||
|
|||
ASSERT(closed_connections == NUM_CLIENTS * 2); |
|||
} |
|||
|
|||
|
|||
TEST_IMPL(poll_duplex) { |
|||
test_mode = DUPLEX; |
|||
start_poll_test(); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
TEST_IMPL(poll_unidirectional) { |
|||
test_mode = UNIDIRECTIONAL; |
|||
start_poll_test(); |
|||
return 0; |
|||
} |
@ -0,0 +1,85 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "uv.h" |
|||
#include "task.h" |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
|
|||
|
|||
static int connect_cb_called; |
|||
static int close_cb_called; |
|||
|
|||
static uv_connect_t connect_req; |
|||
static uv_timer_t timer; |
|||
static uv_tcp_t conn; |
|||
|
|||
static void connect_cb(uv_connect_t* req, int status); |
|||
static void timer_cb(uv_timer_t* handle, int status); |
|||
static void close_cb(uv_handle_t* handle); |
|||
|
|||
|
|||
static void connect_cb(uv_connect_t* req, int status) { |
|||
ASSERT(req == &connect_req); |
|||
ASSERT(status == -1); |
|||
connect_cb_called++; |
|||
} |
|||
|
|||
|
|||
static void timer_cb(uv_timer_t* handle, int status) { |
|||
ASSERT(handle == &timer); |
|||
uv_close((uv_handle_t*)&conn, close_cb); |
|||
uv_close((uv_handle_t*)&timer, close_cb); |
|||
} |
|||
|
|||
|
|||
static void close_cb(uv_handle_t* handle) { |
|||
ASSERT(handle == (uv_handle_t*)&conn || handle == (uv_handle_t*)&timer); |
|||
close_cb_called++; |
|||
} |
|||
|
|||
|
|||
/* Verify that connecting to an unreachable address or port doesn't hang
|
|||
* the event loop. |
|||
*/ |
|||
TEST_IMPL(tcp_connect_timeout) { |
|||
struct sockaddr_in addr; |
|||
int r; |
|||
|
|||
addr = uv_ip4_addr("8.8.8.8", 9999); |
|||
|
|||
r = uv_timer_init(uv_default_loop(), &timer); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_timer_start(&timer, timer_cb, 50, 0); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_tcp_init(uv_default_loop(), &conn); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_tcp_connect(&connect_req, &conn, addr, connect_cb); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_run(uv_default_loop()); |
|||
ASSERT(r == 0); |
|||
|
|||
return 0; |
|||
} |
Loading…
Reference in new issue