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