mirror of https://github.com/lukechilds/node.git
Ben Noordhuis
12 years ago
34 changed files with 1340 additions and 251 deletions
@ -0,0 +1,132 @@ |
|||
/* 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> |
|||
#include <errno.h> |
|||
|
|||
#ifndef HAVE_KQUEUE |
|||
# if __APPLE__ || __DragonFly__ || __FreeBSD__ || __OpenBSD__ || __NetBSD__ |
|||
# define HAVE_KQUEUE 1 |
|||
# endif |
|||
#endif |
|||
|
|||
#ifndef HAVE_EPOLL |
|||
# if defined(__linux__) |
|||
# define HAVE_EPOLL 1 |
|||
# endif |
|||
#endif |
|||
|
|||
#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) |
|||
|
|||
#if defined(HAVE_KQUEUE) |
|||
# include <sys/types.h> |
|||
# include <sys/event.h> |
|||
# include <sys/time.h> |
|||
#endif |
|||
|
|||
#if defined(HAVE_EPOLL) |
|||
# include <sys/epoll.h> |
|||
#endif |
|||
|
|||
static uv_thread_t embed_thread; |
|||
static uv_sem_t embed_sem; |
|||
static uv_timer_t embed_timer; |
|||
static uv_async_t embed_async; |
|||
static volatile int embed_closed; |
|||
|
|||
static int embed_timer_called; |
|||
|
|||
|
|||
static void embed_thread_runner(void* arg) { |
|||
int r; |
|||
int fd; |
|||
int timeout; |
|||
|
|||
while (!embed_closed) { |
|||
fd = uv_backend_fd(uv_default_loop()); |
|||
timeout = uv_backend_timeout(uv_default_loop()); |
|||
|
|||
do { |
|||
#if defined(HAVE_KQUEUE) |
|||
struct timespec ts; |
|||
ts.tv_sec = timeout / 1000; |
|||
ts.tv_nsec = (timeout % 1000) * 1000000; |
|||
r = kevent(fd, NULL, 0, NULL, 0, &ts); |
|||
#elif defined(HAVE_EPOLL) |
|||
r = epoll_wait(fd, NULL, 0, timeout); |
|||
#endif |
|||
} while (r == -1 && errno == EINTR); |
|||
uv_async_send(&embed_async); |
|||
uv_sem_wait(&embed_sem); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void embed_cb(uv_async_t* async, int status) { |
|||
uv_run_once(uv_default_loop()); |
|||
|
|||
uv_sem_post(&embed_sem); |
|||
} |
|||
|
|||
|
|||
static void embed_timer_cb(uv_timer_t* timer, int status) { |
|||
embed_timer_called++; |
|||
embed_closed = 1; |
|||
|
|||
uv_close((uv_handle_t*) &embed_async, NULL); |
|||
} |
|||
#endif |
|||
|
|||
|
|||
TEST_IMPL(embed) { |
|||
#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) |
|||
uv_loop_t* external; |
|||
|
|||
external = uv_loop_new(); |
|||
ASSERT(external != NULL); |
|||
|
|||
embed_timer_called = 0; |
|||
embed_closed = 0; |
|||
|
|||
uv_async_init(external, &embed_async, embed_cb); |
|||
|
|||
/* Start timer in default loop */ |
|||
uv_timer_init(uv_default_loop(), &embed_timer); |
|||
uv_timer_start(&embed_timer, embed_timer_cb, 250, 0); |
|||
|
|||
/* Start worker that will interrupt external loop */ |
|||
uv_sem_init(&embed_sem, 0); |
|||
uv_thread_create(&embed_thread, embed_thread_runner, NULL); |
|||
|
|||
/* But run external loop */ |
|||
uv_run(external); |
|||
|
|||
uv_thread_join(&embed_thread); |
|||
uv_loop_delete(external); |
|||
|
|||
ASSERT(embed_timer_called == 1); |
|||
#endif |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,73 @@ |
|||
/* 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" |
|||
|
|||
static uv_timer_t timer_handle; |
|||
static uv_tcp_t tcp_handle; |
|||
static uv_write_t write_req; |
|||
|
|||
|
|||
static void fail_cb(void) { |
|||
ASSERT(0 && "fail_cb called"); |
|||
} |
|||
|
|||
|
|||
static void write_cb(uv_write_t* req, int status) { |
|||
uv_close((uv_handle_t*) &timer_handle, NULL); |
|||
uv_close((uv_handle_t*) &tcp_handle, NULL); |
|||
} |
|||
|
|||
|
|||
static void timer_cb(uv_timer_t* handle, int status) { |
|||
uv_buf_t buf = uv_buf_init("PING", 4); |
|||
ASSERT(0 == uv_write(&write_req, |
|||
(uv_stream_t*) &tcp_handle, |
|||
&buf, |
|||
1, |
|||
write_cb)); |
|||
ASSERT(0 == uv_read_stop((uv_stream_t*) &tcp_handle)); |
|||
} |
|||
|
|||
|
|||
static void connect_cb(uv_connect_t* req, int status) { |
|||
ASSERT(0 == status); |
|||
ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); |
|||
ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_handle, |
|||
(uv_alloc_cb) fail_cb, |
|||
(uv_read_cb) fail_cb)); |
|||
} |
|||
|
|||
|
|||
TEST_IMPL(tcp_read_stop) { |
|||
uv_connect_t connect_req; |
|||
struct sockaddr_in addr; |
|||
|
|||
addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
|||
ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); |
|||
ASSERT(0 == uv_tcp_init(uv_default_loop(), &tcp_handle)); |
|||
ASSERT(0 == uv_tcp_connect(&connect_req, &tcp_handle, addr, connect_cb)); |
|||
ASSERT(0 == uv_run(uv_default_loop())); |
|||
MAKE_VALGRIND_HAPPY(); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,266 @@ |
|||
/* 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" |
|||
|
|||
#define INIT_CANCEL_INFO(ci, what) \ |
|||
do { \ |
|||
(ci)->reqs = (what); \ |
|||
(ci)->nreqs = ARRAY_SIZE(what); \ |
|||
(ci)->stride = sizeof((what)[0]); \ |
|||
} \ |
|||
while (0) |
|||
|
|||
struct cancel_info { |
|||
void* reqs; |
|||
unsigned nreqs; |
|||
unsigned stride; |
|||
uv_timer_t timer_handle; |
|||
}; |
|||
|
|||
static uv_cond_t signal_cond; |
|||
static uv_mutex_t signal_mutex; |
|||
static uv_mutex_t wait_mutex; |
|||
static unsigned num_threads; |
|||
static unsigned fs_cb_called; |
|||
static unsigned work_cb_called; |
|||
static unsigned done_cb_called; |
|||
static unsigned done2_cb_called; |
|||
static unsigned timer_cb_called; |
|||
static unsigned getaddrinfo_cb_called; |
|||
|
|||
|
|||
static void work_cb(uv_work_t* req) { |
|||
uv_mutex_lock(&signal_mutex); |
|||
uv_cond_signal(&signal_cond); |
|||
uv_mutex_unlock(&signal_mutex); |
|||
|
|||
uv_mutex_lock(&wait_mutex); |
|||
uv_mutex_unlock(&wait_mutex); |
|||
|
|||
work_cb_called++; |
|||
} |
|||
|
|||
|
|||
static void done_cb(uv_work_t* req, int status) { |
|||
done_cb_called++; |
|||
free(req); |
|||
} |
|||
|
|||
|
|||
static void saturate_threadpool(void) { |
|||
uv_work_t* req; |
|||
|
|||
ASSERT(0 == uv_cond_init(&signal_cond)); |
|||
ASSERT(0 == uv_mutex_init(&signal_mutex)); |
|||
ASSERT(0 == uv_mutex_init(&wait_mutex)); |
|||
|
|||
uv_mutex_lock(&signal_mutex); |
|||
uv_mutex_lock(&wait_mutex); |
|||
|
|||
for (num_threads = 0; /* empty */; num_threads++) { |
|||
req = malloc(sizeof(*req)); |
|||
ASSERT(req != NULL); |
|||
ASSERT(0 == uv_queue_work(uv_default_loop(), req, work_cb, done_cb)); |
|||
|
|||
/* Expect to get signalled within 350 ms, otherwise assume that
|
|||
* the thread pool is saturated. As with any timing dependent test, |
|||
* this is obviously not ideal. |
|||
*/ |
|||
if (uv_cond_timedwait(&signal_cond, &signal_mutex, 350 * 1e6)) { |
|||
ASSERT(0 == uv_cancel((uv_req_t*) req)); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
static void unblock_threadpool(void) { |
|||
uv_mutex_unlock(&signal_mutex); |
|||
uv_mutex_unlock(&wait_mutex); |
|||
} |
|||
|
|||
|
|||
static void cleanup_threadpool(void) { |
|||
ASSERT(done_cb_called == num_threads + 1); /* +1 == cancelled work req. */ |
|||
ASSERT(work_cb_called == num_threads); |
|||
|
|||
uv_cond_destroy(&signal_cond); |
|||
uv_mutex_destroy(&signal_mutex); |
|||
uv_mutex_destroy(&wait_mutex); |
|||
} |
|||
|
|||
|
|||
static void fs_cb(uv_fs_t* req) { |
|||
ASSERT(req->errorno == UV_ECANCELED); |
|||
uv_fs_req_cleanup(req); |
|||
fs_cb_called++; |
|||
} |
|||
|
|||
|
|||
static void getaddrinfo_cb(uv_getaddrinfo_t* req, |
|||
int status, |
|||
struct addrinfo* res) { |
|||
ASSERT(UV_ECANCELED == uv_last_error(req->loop).code); |
|||
ASSERT(UV_ECANCELED == status); |
|||
getaddrinfo_cb_called++; |
|||
} |
|||
|
|||
|
|||
static void work2_cb(uv_work_t* req) { |
|||
ASSERT(0 && "work2_cb called"); |
|||
} |
|||
|
|||
|
|||
static void done2_cb(uv_work_t* req, int status) { |
|||
ASSERT(uv_last_error(req->loop).code == UV_ECANCELED); |
|||
ASSERT(status == -1); |
|||
done2_cb_called++; |
|||
} |
|||
|
|||
|
|||
static void timer_cb(uv_timer_t* handle, int status) { |
|||
struct cancel_info* ci; |
|||
uv_req_t* req; |
|||
unsigned i; |
|||
|
|||
ci = container_of(handle, struct cancel_info, timer_handle); |
|||
|
|||
for (i = 0; i < ci->nreqs; i++) { |
|||
req = (uv_req_t*) ((char*) ci->reqs + i * ci->stride); |
|||
ASSERT(0 == uv_cancel(req)); |
|||
} |
|||
|
|||
uv_close((uv_handle_t*) &ci->timer_handle, NULL); |
|||
unblock_threadpool(); |
|||
timer_cb_called++; |
|||
} |
|||
|
|||
|
|||
TEST_IMPL(threadpool_cancel_getaddrinfo) { |
|||
uv_getaddrinfo_t reqs[4]; |
|||
struct cancel_info ci; |
|||
struct addrinfo hints; |
|||
uv_loop_t* loop; |
|||
int r; |
|||
|
|||
INIT_CANCEL_INFO(&ci, reqs); |
|||
loop = uv_default_loop(); |
|||
saturate_threadpool(); |
|||
|
|||
r = uv_getaddrinfo(loop, reqs + 0, getaddrinfo_cb, "fail", NULL, NULL); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_getaddrinfo(loop, reqs + 1, getaddrinfo_cb, NULL, "fail", NULL); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_getaddrinfo(loop, reqs + 2, getaddrinfo_cb, "fail", "fail", NULL); |
|||
ASSERT(r == 0); |
|||
|
|||
r = uv_getaddrinfo(loop, reqs + 3, getaddrinfo_cb, "fail", NULL, &hints); |
|||
ASSERT(r == 0); |
|||
|
|||
ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
|||
ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
|||
ASSERT(0 == uv_run(loop)); |
|||
ASSERT(1 == timer_cb_called); |
|||
|
|||
cleanup_threadpool(); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
TEST_IMPL(threadpool_cancel_work) { |
|||
struct cancel_info ci; |
|||
uv_work_t reqs[16]; |
|||
uv_loop_t* loop; |
|||
unsigned i; |
|||
|
|||
INIT_CANCEL_INFO(&ci, reqs); |
|||
loop = uv_default_loop(); |
|||
saturate_threadpool(); |
|||
|
|||
for (i = 0; i < ARRAY_SIZE(reqs); i++) |
|||
ASSERT(0 == uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); |
|||
|
|||
ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
|||
ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
|||
ASSERT(0 == uv_run(loop)); |
|||
ASSERT(1 == timer_cb_called); |
|||
ASSERT(ARRAY_SIZE(reqs) == done2_cb_called); |
|||
|
|||
cleanup_threadpool(); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
TEST_IMPL(threadpool_cancel_fs) { |
|||
struct cancel_info ci; |
|||
uv_fs_t reqs[25]; |
|||
uv_loop_t* loop; |
|||
unsigned n; |
|||
|
|||
INIT_CANCEL_INFO(&ci, reqs); |
|||
loop = uv_default_loop(); |
|||
saturate_threadpool(); |
|||
|
|||
/* Needs to match ARRAY_SIZE(fs_reqs). */ |
|||
n = 0; |
|||
ASSERT(0 == uv_fs_chmod(loop, reqs + n++, "/", 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_chown(loop, reqs + n++, "/", 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_close(loop, reqs + n++, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_link(loop, reqs + n++, "/", "/", fs_cb)); |
|||
ASSERT(0 == uv_fs_lstat(loop, reqs + n++, "/", fs_cb)); |
|||
ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_open(loop, reqs + n++, "/", 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_read(loop, reqs + n++, 0, NULL, 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_readdir(loop, reqs + n++, "/", 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); |
|||
ASSERT(0 == uv_fs_rename(loop, reqs + n++, "/", "/", fs_cb)); |
|||
ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_stat(loop, reqs + n++, "/", fs_cb)); |
|||
ASSERT(0 == uv_fs_symlink(loop, reqs + n++, "/", "/", 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_unlink(loop, reqs + n++, "/", fs_cb)); |
|||
ASSERT(0 == uv_fs_utime(loop, reqs + n++, "/", 0, 0, fs_cb)); |
|||
ASSERT(0 == uv_fs_write(loop, reqs + n++, 0, NULL, 0, 0, fs_cb)); |
|||
ASSERT(n == ARRAY_SIZE(reqs)); |
|||
|
|||
ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
|||
ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
|||
ASSERT(0 == uv_run(loop)); |
|||
ASSERT(n == fs_cb_called); |
|||
ASSERT(1 == timer_cb_called); |
|||
|
|||
cleanup_threadpool(); |
|||
|
|||
return 0; |
|||
} |
Loading…
Reference in new issue