diff options
author | 2016-02-21 11:48:45 +0100 | |
---|---|---|
committer | 2016-02-21 11:48:45 +0100 | |
commit | cc24a339d8c0517259084b5c178d784626ba965c (patch) | |
tree | 9868e9687b5802ae0a3733712a3bbeb3bc75c953 /3rdparty/libuv/test | |
parent | b5daabda5495dea5c50e17961ecfed2ea8619d76 (diff) |
Merge remote-tracking branch 'refs/remotes/mamedev/master'
Second attempt
Diffstat (limited to '3rdparty/libuv/test')
154 files changed, 28482 insertions, 0 deletions
diff --git a/3rdparty/libuv/test/benchmark-async-pummel.c b/3rdparty/libuv/test/benchmark-async-pummel.c new file mode 100644 index 00000000000..cca3de1062b --- /dev/null +++ b/3rdparty/libuv/test/benchmark-async-pummel.c @@ -0,0 +1,119 @@ +/* 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 "task.h" +#include "uv.h" + +#include <stdio.h> +#include <stdlib.h> + +#define NUM_PINGS (1000 * 1000) +#define ACCESS_ONCE(type, var) (*(volatile type*) &(var)) + +static unsigned int callbacks; +static volatile int done; + +static const char running[] = "running"; +static const char stop[] = "stop"; +static const char stopped[] = "stopped"; + + +static void async_cb(uv_async_t* handle) { + if (++callbacks == NUM_PINGS) { + /* Tell the pummel thread to stop. */ + ACCESS_ONCE(const char*, handle->data) = stop; + + /* Wait for for the pummel thread to acknowledge that it has stoppped. */ + while (ACCESS_ONCE(const char*, handle->data) != stopped) + uv_sleep(0); + + uv_close((uv_handle_t*) handle, NULL); + } +} + + +static void pummel(void* arg) { + uv_async_t* handle = (uv_async_t*) arg; + + while (ACCESS_ONCE(const char*, handle->data) == running) + uv_async_send(handle); + + /* Acknowledge that we've seen handle->data change. */ + ACCESS_ONCE(const char*, handle->data) = stopped; +} + + +static int test_async_pummel(int nthreads) { + uv_thread_t* tids; + uv_async_t handle; + uint64_t time; + int i; + + tids = calloc(nthreads, sizeof(tids[0])); + ASSERT(tids != NULL); + + ASSERT(0 == uv_async_init(uv_default_loop(), &handle, async_cb)); + ACCESS_ONCE(const char*, handle.data) = running; + + for (i = 0; i < nthreads; i++) + ASSERT(0 == uv_thread_create(tids + i, pummel, &handle)); + + time = uv_hrtime(); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + time = uv_hrtime() - time; + done = 1; + + for (i = 0; i < nthreads; i++) + ASSERT(0 == uv_thread_join(tids + i)); + + printf("async_pummel_%d: %s callbacks in %.2f seconds (%s/sec)\n", + nthreads, + fmt(callbacks), + time / 1e9, + fmt(callbacks / (time / 1e9))); + + free(tids); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +BENCHMARK_IMPL(async_pummel_1) { + return test_async_pummel(1); +} + + +BENCHMARK_IMPL(async_pummel_2) { + return test_async_pummel(2); +} + + +BENCHMARK_IMPL(async_pummel_4) { + return test_async_pummel(4); +} + + +BENCHMARK_IMPL(async_pummel_8) { + return test_async_pummel(8); +} diff --git a/3rdparty/libuv/test/benchmark-async.c b/3rdparty/libuv/test/benchmark-async.c new file mode 100644 index 00000000000..e44165f2b81 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-async.c @@ -0,0 +1,141 @@ +/* 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 "task.h" +#include "uv.h" + +#include <stdio.h> +#include <stdlib.h> + +#define NUM_PINGS (1000 * 1000) + +struct ctx { + uv_loop_t loop; + uv_thread_t thread; + uv_async_t main_async; /* wake up main thread */ + uv_async_t worker_async; /* wake up worker */ + unsigned int nthreads; + unsigned int main_sent; + unsigned int main_seen; + unsigned int worker_sent; + unsigned int worker_seen; +}; + + +static void worker_async_cb(uv_async_t* handle) { + struct ctx* ctx = container_of(handle, struct ctx, worker_async); + + ASSERT(0 == uv_async_send(&ctx->main_async)); + ctx->worker_sent++; + ctx->worker_seen++; + + if (ctx->worker_sent >= NUM_PINGS) + uv_close((uv_handle_t*) &ctx->worker_async, NULL); +} + + +static void main_async_cb(uv_async_t* handle) { + struct ctx* ctx = container_of(handle, struct ctx, main_async); + + ASSERT(0 == uv_async_send(&ctx->worker_async)); + ctx->main_sent++; + ctx->main_seen++; + + if (ctx->main_sent >= NUM_PINGS) + uv_close((uv_handle_t*) &ctx->main_async, NULL); +} + + +static void worker(void* arg) { + struct ctx* ctx = arg; + ASSERT(0 == uv_async_send(&ctx->main_async)); + ASSERT(0 == uv_run(&ctx->loop, UV_RUN_DEFAULT)); + uv_loop_close(&ctx->loop); +} + + +static int test_async(int nthreads) { + struct ctx* threads; + struct ctx* ctx; + uint64_t time; + int i; + + threads = calloc(nthreads, sizeof(threads[0])); + ASSERT(threads != NULL); + + for (i = 0; i < nthreads; i++) { + ctx = threads + i; + ctx->nthreads = nthreads; + ASSERT(0 == uv_loop_init(&ctx->loop)); + ASSERT(0 == uv_async_init(&ctx->loop, &ctx->worker_async, worker_async_cb)); + ASSERT(0 == uv_async_init(uv_default_loop(), + &ctx->main_async, + main_async_cb)); + ASSERT(0 == uv_thread_create(&ctx->thread, worker, ctx)); + } + + time = uv_hrtime(); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + for (i = 0; i < nthreads; i++) + ASSERT(0 == uv_thread_join(&threads[i].thread)); + + time = uv_hrtime() - time; + + for (i = 0; i < nthreads; i++) { + ctx = threads + i; + ASSERT(ctx->worker_sent == NUM_PINGS); + ASSERT(ctx->worker_seen == NUM_PINGS); + ASSERT(ctx->main_sent == (unsigned int) NUM_PINGS); + ASSERT(ctx->main_seen == (unsigned int) NUM_PINGS); + } + + printf("async%d: %.2f sec (%s/sec)\n", + nthreads, + time / 1e9, + fmt(NUM_PINGS / (time / 1e9))); + + free(threads); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +BENCHMARK_IMPL(async1) { + return test_async(1); +} + + +BENCHMARK_IMPL(async2) { + return test_async(2); +} + + +BENCHMARK_IMPL(async4) { + return test_async(4); +} + + +BENCHMARK_IMPL(async8) { + return test_async(8); +} diff --git a/3rdparty/libuv/test/benchmark-fs-stat.c b/3rdparty/libuv/test/benchmark-fs-stat.c new file mode 100644 index 00000000000..32d2589586c --- /dev/null +++ b/3rdparty/libuv/test/benchmark-fs-stat.c @@ -0,0 +1,136 @@ +/* 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 "task.h" +#include "uv.h" + +#include <stdio.h> +#include <stdlib.h> + +#define NUM_SYNC_REQS (10 * 1e5) +#define NUM_ASYNC_REQS (1 * (int) 1e5) +#define MAX_CONCURRENT_REQS 32 + +#define sync_stat(req, path) \ + do { \ + uv_fs_stat(NULL, (req), (path), NULL); \ + uv_fs_req_cleanup((req)); \ + } \ + while (0) + +struct async_req { + const char* path; + uv_fs_t fs_req; + int* count; +}; + + +static void warmup(const char* path) { + uv_fs_t reqs[MAX_CONCURRENT_REQS]; + unsigned int i; + + /* warm up the thread pool */ + for (i = 0; i < ARRAY_SIZE(reqs); i++) + uv_fs_stat(uv_default_loop(), reqs + i, path, uv_fs_req_cleanup); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + /* warm up the OS dirent cache */ + for (i = 0; i < 16; i++) + sync_stat(reqs + 0, path); +} + + +static void sync_bench(const char* path) { + uint64_t before; + uint64_t after; + uv_fs_t req; + int i; + + /* do the sync benchmark */ + before = uv_hrtime(); + + for (i = 0; i < NUM_SYNC_REQS; i++) + sync_stat(&req, path); + + after = uv_hrtime(); + + printf("%s stats (sync): %.2fs (%s/s)\n", + fmt(1.0 * NUM_SYNC_REQS), + (after - before) / 1e9, + fmt((1.0 * NUM_SYNC_REQS) / ((after - before) / 1e9))); + fflush(stdout); +} + + +static void stat_cb(uv_fs_t* fs_req) { + struct async_req* req = container_of(fs_req, struct async_req, fs_req); + uv_fs_req_cleanup(&req->fs_req); + if (*req->count == 0) return; + uv_fs_stat(uv_default_loop(), &req->fs_req, req->path, stat_cb); + (*req->count)--; +} + + +static void async_bench(const char* path) { + struct async_req reqs[MAX_CONCURRENT_REQS]; + struct async_req* req; + uint64_t before; + uint64_t after; + int count; + int i; + + for (i = 1; i <= MAX_CONCURRENT_REQS; i++) { + count = NUM_ASYNC_REQS; + + for (req = reqs; req < reqs + i; req++) { + req->path = path; + req->count = &count; + uv_fs_stat(uv_default_loop(), &req->fs_req, req->path, stat_cb); + } + + before = uv_hrtime(); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + after = uv_hrtime(); + + printf("%s stats (%d concurrent): %.2fs (%s/s)\n", + fmt(1.0 * NUM_ASYNC_REQS), + i, + (after - before) / 1e9, + fmt((1.0 * NUM_ASYNC_REQS) / ((after - before) / 1e9))); + fflush(stdout); + } +} + + +/* This benchmark aims to measure the overhead of doing I/O syscalls from + * the thread pool. The stat() syscall was chosen because its results are + * easy for the operating system to cache, taking the actual I/O overhead + * out of the equation. + */ +BENCHMARK_IMPL(fs_stat) { + const char path[] = "."; + warmup(path); + sync_bench(path); + async_bench(path); + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-getaddrinfo.c b/3rdparty/libuv/test/benchmark-getaddrinfo.c new file mode 100644 index 00000000000..1dbc23ddba0 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-getaddrinfo.c @@ -0,0 +1,92 @@ +/* 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 <stdlib.h> + +#define CONCURRENT_CALLS 10 +#define TOTAL_CALLS 10000 + +static const char* name = "localhost"; + +static uv_loop_t* loop; + +static uv_getaddrinfo_t handles[CONCURRENT_CALLS]; + +static int calls_initiated = 0; +static int calls_completed = 0; +static int64_t start_time; +static int64_t end_time; + + +static void getaddrinfo_initiate(uv_getaddrinfo_t* handle); + + +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, + struct addrinfo* res) { + ASSERT(status == 0); + calls_completed++; + if (calls_initiated < TOTAL_CALLS) { + getaddrinfo_initiate(handle); + } + + uv_freeaddrinfo(res); +} + + +static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { + int r; + + calls_initiated++; + + r = uv_getaddrinfo(loop, handle, &getaddrinfo_cb, name, NULL, NULL); + ASSERT(r == 0); +} + + +BENCHMARK_IMPL(getaddrinfo) { + int i; + + loop = uv_default_loop(); + + uv_update_time(loop); + start_time = uv_now(loop); + + for (i = 0; i < CONCURRENT_CALLS; i++) { + getaddrinfo_initiate(&handles[i]); + } + + uv_run(loop, UV_RUN_DEFAULT); + + uv_update_time(loop); + end_time = uv_now(loop); + + ASSERT(calls_initiated == TOTAL_CALLS); + ASSERT(calls_completed == TOTAL_CALLS); + + fprintf(stderr, "getaddrinfo: %.0f req/s\n", + (double) calls_completed / (double) (end_time - start_time) * 1000.0); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-list.h b/3rdparty/libuv/test/benchmark-list.h new file mode 100644 index 00000000000..1e843071c01 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-list.h @@ -0,0 +1,163 @@ +/* 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. + */ + +BENCHMARK_DECLARE (sizes) +BENCHMARK_DECLARE (loop_count) +BENCHMARK_DECLARE (loop_count_timed) +BENCHMARK_DECLARE (ping_pongs) +BENCHMARK_DECLARE (tcp_write_batch) +BENCHMARK_DECLARE (tcp4_pound_100) +BENCHMARK_DECLARE (tcp4_pound_1000) +BENCHMARK_DECLARE (pipe_pound_100) +BENCHMARK_DECLARE (pipe_pound_1000) +BENCHMARK_DECLARE (tcp_pump100_client) +BENCHMARK_DECLARE (tcp_pump1_client) +BENCHMARK_DECLARE (pipe_pump100_client) +BENCHMARK_DECLARE (pipe_pump1_client) + +BENCHMARK_DECLARE (tcp_multi_accept2) +BENCHMARK_DECLARE (tcp_multi_accept4) +BENCHMARK_DECLARE (tcp_multi_accept8) + +/* Run until X packets have been sent/received. */ +BENCHMARK_DECLARE (udp_pummel_1v1) +BENCHMARK_DECLARE (udp_pummel_1v10) +BENCHMARK_DECLARE (udp_pummel_1v100) +BENCHMARK_DECLARE (udp_pummel_1v1000) +BENCHMARK_DECLARE (udp_pummel_10v10) +BENCHMARK_DECLARE (udp_pummel_10v100) +BENCHMARK_DECLARE (udp_pummel_10v1000) +BENCHMARK_DECLARE (udp_pummel_100v100) +BENCHMARK_DECLARE (udp_pummel_100v1000) +BENCHMARK_DECLARE (udp_pummel_1000v1000) + +/* Run until X seconds have elapsed. */ +BENCHMARK_DECLARE (udp_timed_pummel_1v1) +BENCHMARK_DECLARE (udp_timed_pummel_1v10) +BENCHMARK_DECLARE (udp_timed_pummel_1v100) +BENCHMARK_DECLARE (udp_timed_pummel_1v1000) +BENCHMARK_DECLARE (udp_timed_pummel_10v10) +BENCHMARK_DECLARE (udp_timed_pummel_10v100) +BENCHMARK_DECLARE (udp_timed_pummel_10v1000) +BENCHMARK_DECLARE (udp_timed_pummel_100v100) +BENCHMARK_DECLARE (udp_timed_pummel_100v1000) +BENCHMARK_DECLARE (udp_timed_pummel_1000v1000) + +BENCHMARK_DECLARE (getaddrinfo) +BENCHMARK_DECLARE (fs_stat) +BENCHMARK_DECLARE (async1) +BENCHMARK_DECLARE (async2) +BENCHMARK_DECLARE (async4) +BENCHMARK_DECLARE (async8) +BENCHMARK_DECLARE (async_pummel_1) +BENCHMARK_DECLARE (async_pummel_2) +BENCHMARK_DECLARE (async_pummel_4) +BENCHMARK_DECLARE (async_pummel_8) +BENCHMARK_DECLARE (spawn) +BENCHMARK_DECLARE (thread_create) +BENCHMARK_DECLARE (million_async) +BENCHMARK_DECLARE (million_timers) +HELPER_DECLARE (tcp4_blackhole_server) +HELPER_DECLARE (tcp_pump_server) +HELPER_DECLARE (pipe_pump_server) +HELPER_DECLARE (tcp4_echo_server) +HELPER_DECLARE (pipe_echo_server) +HELPER_DECLARE (dns_server) + +TASK_LIST_START + BENCHMARK_ENTRY (sizes) + BENCHMARK_ENTRY (loop_count) + BENCHMARK_ENTRY (loop_count_timed) + + BENCHMARK_ENTRY (ping_pongs) + BENCHMARK_HELPER (ping_pongs, tcp4_echo_server) + + BENCHMARK_ENTRY (tcp_write_batch) + BENCHMARK_HELPER (tcp_write_batch, tcp4_blackhole_server) + + BENCHMARK_ENTRY (tcp_pump100_client) + BENCHMARK_HELPER (tcp_pump100_client, tcp_pump_server) + + BENCHMARK_ENTRY (tcp_pump1_client) + BENCHMARK_HELPER (tcp_pump1_client, tcp_pump_server) + + BENCHMARK_ENTRY (tcp4_pound_100) + BENCHMARK_HELPER (tcp4_pound_100, tcp4_echo_server) + + BENCHMARK_ENTRY (tcp4_pound_1000) + BENCHMARK_HELPER (tcp4_pound_1000, tcp4_echo_server) + + BENCHMARK_ENTRY (pipe_pump100_client) + BENCHMARK_HELPER (pipe_pump100_client, pipe_pump_server) + + BENCHMARK_ENTRY (pipe_pump1_client) + BENCHMARK_HELPER (pipe_pump1_client, pipe_pump_server) + + BENCHMARK_ENTRY (pipe_pound_100) + BENCHMARK_HELPER (pipe_pound_100, pipe_echo_server) + + BENCHMARK_ENTRY (pipe_pound_1000) + BENCHMARK_HELPER (pipe_pound_1000, pipe_echo_server) + + BENCHMARK_ENTRY (tcp_multi_accept2) + BENCHMARK_ENTRY (tcp_multi_accept4) + BENCHMARK_ENTRY (tcp_multi_accept8) + + BENCHMARK_ENTRY (udp_pummel_1v1) + BENCHMARK_ENTRY (udp_pummel_1v10) + BENCHMARK_ENTRY (udp_pummel_1v100) + BENCHMARK_ENTRY (udp_pummel_1v1000) + BENCHMARK_ENTRY (udp_pummel_10v10) + BENCHMARK_ENTRY (udp_pummel_10v100) + BENCHMARK_ENTRY (udp_pummel_10v1000) + BENCHMARK_ENTRY (udp_pummel_100v100) + BENCHMARK_ENTRY (udp_pummel_100v1000) + BENCHMARK_ENTRY (udp_pummel_1000v1000) + + BENCHMARK_ENTRY (udp_timed_pummel_1v1) + BENCHMARK_ENTRY (udp_timed_pummel_1v10) + BENCHMARK_ENTRY (udp_timed_pummel_1v100) + BENCHMARK_ENTRY (udp_timed_pummel_1v1000) + BENCHMARK_ENTRY (udp_timed_pummel_10v10) + BENCHMARK_ENTRY (udp_timed_pummel_10v100) + BENCHMARK_ENTRY (udp_timed_pummel_10v1000) + BENCHMARK_ENTRY (udp_timed_pummel_100v100) + BENCHMARK_ENTRY (udp_timed_pummel_100v1000) + BENCHMARK_ENTRY (udp_timed_pummel_1000v1000) + + BENCHMARK_ENTRY (getaddrinfo) + + BENCHMARK_ENTRY (fs_stat) + + BENCHMARK_ENTRY (async1) + BENCHMARK_ENTRY (async2) + BENCHMARK_ENTRY (async4) + BENCHMARK_ENTRY (async8) + BENCHMARK_ENTRY (async_pummel_1) + BENCHMARK_ENTRY (async_pummel_2) + BENCHMARK_ENTRY (async_pummel_4) + BENCHMARK_ENTRY (async_pummel_8) + + BENCHMARK_ENTRY (spawn) + BENCHMARK_ENTRY (thread_create) + BENCHMARK_ENTRY (million_async) + BENCHMARK_ENTRY (million_timers) +TASK_LIST_END diff --git a/3rdparty/libuv/test/benchmark-loop-count.c b/3rdparty/libuv/test/benchmark-loop-count.c new file mode 100644 index 00000000000..970a94c2fec --- /dev/null +++ b/3rdparty/libuv/test/benchmark-loop-count.c @@ -0,0 +1,92 @@ +/* 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 "task.h" +#include "uv.h" + +#include <stdio.h> +#include <stdlib.h> + +#define NUM_TICKS (2 * 1000 * 1000) + +static unsigned long ticks; +static uv_idle_t idle_handle; +static uv_timer_t timer_handle; + + +static void idle_cb(uv_idle_t* handle) { + if (++ticks == NUM_TICKS) + uv_idle_stop(handle); +} + + +static void idle2_cb(uv_idle_t* handle) { + ticks++; +} + + +static void timer_cb(uv_timer_t* handle) { + uv_idle_stop(&idle_handle); + uv_timer_stop(&timer_handle); +} + + +BENCHMARK_IMPL(loop_count) { + uv_loop_t* loop = uv_default_loop(); + uint64_t ns; + + uv_idle_init(loop, &idle_handle); + uv_idle_start(&idle_handle, idle_cb); + + ns = uv_hrtime(); + uv_run(loop, UV_RUN_DEFAULT); + ns = uv_hrtime() - ns; + + ASSERT(ticks == NUM_TICKS); + + fprintf(stderr, "loop_count: %d ticks in %.2fs (%.0f/s)\n", + NUM_TICKS, + ns / 1e9, + NUM_TICKS / (ns / 1e9)); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +BENCHMARK_IMPL(loop_count_timed) { + uv_loop_t* loop = uv_default_loop(); + + uv_idle_init(loop, &idle_handle); + uv_idle_start(&idle_handle, idle2_cb); + + uv_timer_init(loop, &timer_handle); + uv_timer_start(&timer_handle, timer_cb, 5000, 0); + + uv_run(loop, UV_RUN_DEFAULT); + + fprintf(stderr, "loop_count: %lu ticks (%.0f ticks/s)\n", ticks, ticks / 5.0); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-million-async.c b/3rdparty/libuv/test/benchmark-million-async.c new file mode 100644 index 00000000000..5395ed54bab --- /dev/null +++ b/3rdparty/libuv/test/benchmark-million-async.c @@ -0,0 +1,112 @@ +/* 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 "task.h" +#include "uv.h" + +struct async_container { + unsigned async_events; + unsigned handles_seen; + uv_async_t async_handles[1024 * 1024]; +}; + +static volatile int done; +static uv_thread_t thread_id; +static struct async_container* container; + + +static unsigned fastrand(void) { + static unsigned g = 0; + g = g * 214013 + 2531011; + return g; +} + + +static void thread_cb(void* arg) { + unsigned i; + + while (done == 0) { + i = fastrand() % ARRAY_SIZE(container->async_handles); + uv_async_send(container->async_handles + i); + } +} + + +static void async_cb(uv_async_t* handle) { + container->async_events++; + handle->data = handle; +} + + +static void timer_cb(uv_timer_t* handle) { + unsigned i; + + done = 1; + ASSERT(0 == uv_thread_join(&thread_id)); + + for (i = 0; i < ARRAY_SIZE(container->async_handles); i++) { + uv_async_t* handle = container->async_handles + i; + + if (handle->data != NULL) + container->handles_seen++; + + uv_close((uv_handle_t*) handle, NULL); + } + + uv_close((uv_handle_t*) handle, NULL); +} + + +BENCHMARK_IMPL(million_async) { + uv_timer_t timer_handle; + uv_async_t* handle; + uv_loop_t* loop; + int timeout; + unsigned i; + + loop = uv_default_loop(); + timeout = 5000; + + container = malloc(sizeof(*container)); + ASSERT(container != NULL); + container->async_events = 0; + container->handles_seen = 0; + + for (i = 0; i < ARRAY_SIZE(container->async_handles); i++) { + handle = container->async_handles + i; + ASSERT(0 == uv_async_init(loop, handle, async_cb)); + handle->data = NULL; + } + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, timeout, 0)); + ASSERT(0 == uv_thread_create(&thread_id, thread_cb, NULL)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + printf("%s async events in %.1f seconds (%s/s, %s unique handles seen)\n", + fmt(container->async_events), + timeout / 1000., + fmt(container->async_events / (timeout / 1000.)), + fmt(container->handles_seen)); + free(container); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-million-timers.c b/3rdparty/libuv/test/benchmark-million-timers.c new file mode 100644 index 00000000000..60a308bef13 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-million-timers.c @@ -0,0 +1,86 @@ +/* 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 "task.h" +#include "uv.h" + +#define NUM_TIMERS (10 * 1000 * 1000) + +static int timer_cb_called; +static int close_cb_called; + + +static void timer_cb(uv_timer_t* handle) { + timer_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +BENCHMARK_IMPL(million_timers) { + uv_timer_t* timers; + uv_loop_t* loop; + uint64_t before_all; + uint64_t before_run; + uint64_t after_run; + uint64_t after_all; + int timeout; + int i; + + timers = malloc(NUM_TIMERS * sizeof(timers[0])); + ASSERT(timers != NULL); + + loop = uv_default_loop(); + timeout = 0; + + before_all = uv_hrtime(); + for (i = 0; i < NUM_TIMERS; i++) { + if (i % 1000 == 0) timeout++; + ASSERT(0 == uv_timer_init(loop, timers + i)); + ASSERT(0 == uv_timer_start(timers + i, timer_cb, timeout, 0)); + } + + before_run = uv_hrtime(); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + after_run = uv_hrtime(); + + for (i = 0; i < NUM_TIMERS; i++) + uv_close((uv_handle_t*) (timers + i), close_cb); + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + after_all = uv_hrtime(); + + ASSERT(timer_cb_called == NUM_TIMERS); + ASSERT(close_cb_called == NUM_TIMERS); + free(timers); + + fprintf(stderr, "%.2f seconds total\n", (after_all - before_all) / 1e9); + fprintf(stderr, "%.2f seconds init\n", (before_run - before_all) / 1e9); + fprintf(stderr, "%.2f seconds dispatch\n", (after_run - before_run) / 1e9); + fprintf(stderr, "%.2f seconds cleanup\n", (after_all - after_run) / 1e9); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-multi-accept.c b/3rdparty/libuv/test/benchmark-multi-accept.c new file mode 100644 index 00000000000..2f32c0caf4a --- /dev/null +++ b/3rdparty/libuv/test/benchmark-multi-accept.c @@ -0,0 +1,447 @@ +/* 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 "task.h" +#include "uv.h" + +#define IPC_PIPE_NAME TEST_PIPENAME +#define NUM_CONNECTS (250 * 1000) + +union stream_handle { + uv_pipe_t pipe; + uv_tcp_t tcp; +}; + +/* Use as (uv_stream_t *) &handle_storage -- it's kind of clunky but it + * avoids aliasing warnings. + */ +typedef unsigned char handle_storage_t[sizeof(union stream_handle)]; + +/* Used for passing around the listen handle, not part of the benchmark proper. + * We have an overabundance of server types here. It works like this: + * + * 1. The main thread starts an IPC pipe server. + * 2. The worker threads connect to the IPC server and obtain a listen handle. + * 3. The worker threads start accepting requests on the listen handle. + * 4. The main thread starts connecting repeatedly. + * + * Step #4 should perhaps be farmed out over several threads. + */ +struct ipc_server_ctx { + handle_storage_t server_handle; + unsigned int num_connects; + uv_pipe_t ipc_pipe; +}; + +struct ipc_peer_ctx { + handle_storage_t peer_handle; + uv_write_t write_req; +}; + +struct ipc_client_ctx { + uv_connect_t connect_req; + uv_stream_t* server_handle; + uv_pipe_t ipc_pipe; + char scratch[16]; +}; + +/* Used in the actual benchmark. */ +struct server_ctx { + handle_storage_t server_handle; + unsigned int num_connects; + uv_async_t async_handle; + uv_thread_t thread_id; + uv_sem_t semaphore; +}; + +struct client_ctx { + handle_storage_t client_handle; + unsigned int num_connects; + uv_connect_t connect_req; + uv_idle_t idle_handle; +}; + +static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status); +static void ipc_write_cb(uv_write_t* req, int status); +static void ipc_close_cb(uv_handle_t* handle); +static void ipc_connect_cb(uv_connect_t* req, int status); +static void ipc_read_cb(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf); +static void ipc_alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf); + +static void sv_async_cb(uv_async_t* handle); +static void sv_connection_cb(uv_stream_t* server_handle, int status); +static void sv_read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf); +static void sv_alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf); + +static void cl_connect_cb(uv_connect_t* req, int status); +static void cl_idle_cb(uv_idle_t* handle); +static void cl_close_cb(uv_handle_t* handle); + +static struct sockaddr_in listen_addr; + + +static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status) { + struct ipc_server_ctx* sc; + struct ipc_peer_ctx* pc; + uv_loop_t* loop; + uv_buf_t buf; + + loop = ipc_pipe->loop; + buf = uv_buf_init("PING", 4); + sc = container_of(ipc_pipe, struct ipc_server_ctx, ipc_pipe); + pc = calloc(1, sizeof(*pc)); + ASSERT(pc != NULL); + + if (ipc_pipe->type == UV_TCP) + ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle)); + else if (ipc_pipe->type == UV_NAMED_PIPE) + ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &pc->peer_handle, 1)); + else + ASSERT(0); + + ASSERT(0 == uv_accept(ipc_pipe, (uv_stream_t*) &pc->peer_handle)); + ASSERT(0 == uv_write2(&pc->write_req, + (uv_stream_t*) &pc->peer_handle, + &buf, + 1, + (uv_stream_t*) &sc->server_handle, + ipc_write_cb)); + + if (--sc->num_connects == 0) + uv_close((uv_handle_t*) ipc_pipe, NULL); +} + + +static void ipc_write_cb(uv_write_t* req, int status) { + struct ipc_peer_ctx* ctx; + ctx = container_of(req, struct ipc_peer_ctx, write_req); + uv_close((uv_handle_t*) &ctx->peer_handle, ipc_close_cb); +} + + +static void ipc_close_cb(uv_handle_t* handle) { + struct ipc_peer_ctx* ctx; + ctx = container_of(handle, struct ipc_peer_ctx, peer_handle); + free(ctx); +} + + +static void ipc_connect_cb(uv_connect_t* req, int status) { + struct ipc_client_ctx* ctx; + ctx = container_of(req, struct ipc_client_ctx, connect_req); + ASSERT(0 == status); + ASSERT(0 == uv_read_start((uv_stream_t*) &ctx->ipc_pipe, + ipc_alloc_cb, + ipc_read_cb)); +} + + +static void ipc_alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + struct ipc_client_ctx* ctx; + ctx = container_of(handle, struct ipc_client_ctx, ipc_pipe); + buf->base = ctx->scratch; + buf->len = sizeof(ctx->scratch); +} + + +static void ipc_read_cb(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + struct ipc_client_ctx* ctx; + uv_loop_t* loop; + uv_handle_type type; + uv_pipe_t* ipc_pipe; + + ipc_pipe = (uv_pipe_t*) handle; + ctx = container_of(ipc_pipe, struct ipc_client_ctx, ipc_pipe); + loop = ipc_pipe->loop; + + ASSERT(1 == uv_pipe_pending_count(ipc_pipe)); + type = uv_pipe_pending_type(ipc_pipe); + if (type == UV_TCP) + ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) ctx->server_handle)); + else if (type == UV_NAMED_PIPE) + ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) ctx->server_handle, 0)); + else + ASSERT(0); + + ASSERT(0 == uv_accept(handle, ctx->server_handle)); + uv_close((uv_handle_t*) &ctx->ipc_pipe, NULL); +} + + +/* Set up an IPC pipe server that hands out listen sockets to the worker + * threads. It's kind of cumbersome for such a simple operation, maybe we + * should revive uv_import() and uv_export(). + */ +static void send_listen_handles(uv_handle_type type, + unsigned int num_servers, + struct server_ctx* servers) { + struct ipc_server_ctx ctx; + uv_loop_t* loop; + unsigned int i; + + loop = uv_default_loop(); + ctx.num_connects = num_servers; + + if (type == UV_TCP) { + ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &ctx.server_handle)); + ASSERT(0 == uv_tcp_bind((uv_tcp_t*) &ctx.server_handle, + (const struct sockaddr*) &listen_addr, + 0)); + } + else + ASSERT(0); + + ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); + ASSERT(0 == uv_pipe_bind(&ctx.ipc_pipe, IPC_PIPE_NAME)); + ASSERT(0 == uv_listen((uv_stream_t*) &ctx.ipc_pipe, 128, ipc_connection_cb)); + + for (i = 0; i < num_servers; i++) + uv_sem_post(&servers[i].semaphore); + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + uv_close((uv_handle_t*) &ctx.server_handle, NULL); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + for (i = 0; i < num_servers; i++) + uv_sem_wait(&servers[i].semaphore); +} + + +static void get_listen_handle(uv_loop_t* loop, uv_stream_t* server_handle) { + struct ipc_client_ctx ctx; + + ctx.server_handle = server_handle; + ctx.server_handle->data = "server handle"; + + ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); + uv_pipe_connect(&ctx.connect_req, + &ctx.ipc_pipe, + IPC_PIPE_NAME, + ipc_connect_cb); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); +} + + +static void server_cb(void *arg) { + struct server_ctx *ctx; + uv_loop_t loop; + + ctx = arg; + ASSERT(0 == uv_loop_init(&loop)); + + ASSERT(0 == uv_async_init(&loop, &ctx->async_handle, sv_async_cb)); + uv_unref((uv_handle_t*) &ctx->async_handle); + + /* Wait until the main thread is ready. */ + uv_sem_wait(&ctx->semaphore); + get_listen_handle(&loop, (uv_stream_t*) &ctx->server_handle); + uv_sem_post(&ctx->semaphore); + + /* Now start the actual benchmark. */ + ASSERT(0 == uv_listen((uv_stream_t*) &ctx->server_handle, + 128, + sv_connection_cb)); + ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + + uv_loop_close(&loop); +} + + +static void sv_async_cb(uv_async_t* handle) { + struct server_ctx* ctx; + ctx = container_of(handle, struct server_ctx, async_handle); + uv_close((uv_handle_t*) &ctx->server_handle, NULL); + uv_close((uv_handle_t*) &ctx->async_handle, NULL); +} + + +static void sv_connection_cb(uv_stream_t* server_handle, int status) { + handle_storage_t* storage; + struct server_ctx* ctx; + + ctx = container_of(server_handle, struct server_ctx, server_handle); + ASSERT(status == 0); + + storage = malloc(sizeof(*storage)); + ASSERT(storage != NULL); + + if (server_handle->type == UV_TCP) + ASSERT(0 == uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage)); + else if (server_handle->type == UV_NAMED_PIPE) + ASSERT(0 == uv_pipe_init(server_handle->loop, (uv_pipe_t*) storage, 0)); + else + ASSERT(0); + + ASSERT(0 == uv_accept(server_handle, (uv_stream_t*) storage)); + ASSERT(0 == uv_read_start((uv_stream_t*) storage, sv_alloc_cb, sv_read_cb)); + ctx->num_connects++; +} + + +static void sv_alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[32]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void sv_read_cb(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + ASSERT(nread == UV_EOF); + uv_close((uv_handle_t*) handle, (uv_close_cb) free); +} + + +static void cl_connect_cb(uv_connect_t* req, int status) { + struct client_ctx* ctx = container_of(req, struct client_ctx, connect_req); + uv_idle_start(&ctx->idle_handle, cl_idle_cb); + ASSERT(0 == status); +} + + +static void cl_idle_cb(uv_idle_t* handle) { + struct client_ctx* ctx = container_of(handle, struct client_ctx, idle_handle); + uv_close((uv_handle_t*) &ctx->client_handle, cl_close_cb); + uv_idle_stop(&ctx->idle_handle); +} + + +static void cl_close_cb(uv_handle_t* handle) { + struct client_ctx* ctx; + + ctx = container_of(handle, struct client_ctx, client_handle); + + if (--ctx->num_connects == 0) { + uv_close((uv_handle_t*) &ctx->idle_handle, NULL); + return; + } + + ASSERT(0 == uv_tcp_init(handle->loop, (uv_tcp_t*) &ctx->client_handle)); + ASSERT(0 == uv_tcp_connect(&ctx->connect_req, + (uv_tcp_t*) &ctx->client_handle, + (const struct sockaddr*) &listen_addr, + cl_connect_cb)); +} + + +static int test_tcp(unsigned int num_servers, unsigned int num_clients) { + struct server_ctx* servers; + struct client_ctx* clients; + uv_loop_t* loop; + uv_tcp_t* handle; + unsigned int i; + double time; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &listen_addr)); + loop = uv_default_loop(); + + servers = calloc(num_servers, sizeof(servers[0])); + clients = calloc(num_clients, sizeof(clients[0])); + ASSERT(servers != NULL); + ASSERT(clients != NULL); + + /* We're making the assumption here that from the perspective of the + * OS scheduler, threads are functionally equivalent to and interchangeable + * with full-blown processes. + */ + for (i = 0; i < num_servers; i++) { + struct server_ctx* ctx = servers + i; + ASSERT(0 == uv_sem_init(&ctx->semaphore, 0)); + ASSERT(0 == uv_thread_create(&ctx->thread_id, server_cb, ctx)); + } + + send_listen_handles(UV_TCP, num_servers, servers); + + for (i = 0; i < num_clients; i++) { + struct client_ctx* ctx = clients + i; + ctx->num_connects = NUM_CONNECTS / num_clients; + handle = (uv_tcp_t*) &ctx->client_handle; + handle->data = "client handle"; + ASSERT(0 == uv_tcp_init(loop, handle)); + ASSERT(0 == uv_tcp_connect(&ctx->connect_req, + handle, + (const struct sockaddr*) &listen_addr, + cl_connect_cb)); + ASSERT(0 == uv_idle_init(loop, &ctx->idle_handle)); + } + + { + uint64_t t = uv_hrtime(); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + t = uv_hrtime() - t; + time = t / 1e9; + } + + for (i = 0; i < num_servers; i++) { + struct server_ctx* ctx = servers + i; + uv_async_send(&ctx->async_handle); + ASSERT(0 == uv_thread_join(&ctx->thread_id)); + uv_sem_destroy(&ctx->semaphore); + } + + printf("accept%u: %.0f accepts/sec (%u total)\n", + num_servers, + NUM_CONNECTS / time, + NUM_CONNECTS); + + for (i = 0; i < num_servers; i++) { + struct server_ctx* ctx = servers + i; + printf(" thread #%u: %.0f accepts/sec (%u total, %.1f%%)\n", + i, + ctx->num_connects / time, + ctx->num_connects, + ctx->num_connects * 100.0 / NUM_CONNECTS); + } + + free(clients); + free(servers); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +BENCHMARK_IMPL(tcp_multi_accept2) { + return test_tcp(2, 40); +} + + +BENCHMARK_IMPL(tcp_multi_accept4) { + return test_tcp(4, 40); +} + + +BENCHMARK_IMPL(tcp_multi_accept8) { + return test_tcp(8, 40); +} diff --git a/3rdparty/libuv/test/benchmark-ping-pongs.c b/3rdparty/libuv/test/benchmark-ping-pongs.c new file mode 100644 index 00000000000..646a7df9447 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-ping-pongs.c @@ -0,0 +1,221 @@ +/* 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 <stdlib.h> +#include <stdio.h> + +/* Run the benchmark for this many ms */ +#define TIME 5000 + + +typedef struct { + int pongs; + int state; + uv_tcp_t tcp; + uv_connect_t connect_req; + uv_shutdown_t shutdown_req; +} pinger_t; + +typedef struct buf_s { + uv_buf_t uv_buf_t; + struct buf_s* next; +} buf_t; + + +static char PING[] = "PING\n"; + +static uv_loop_t* loop; + +static buf_t* buf_freelist = NULL; +static int pinger_shutdown_cb_called; +static int completed_pingers = 0; +static int64_t start_time; + + +static void buf_alloc(uv_handle_t* tcp, size_t size, uv_buf_t* buf) { + buf_t* ab; + + ab = buf_freelist; + if (ab != NULL) + buf_freelist = ab->next; + else { + ab = malloc(size + sizeof(*ab)); + ab->uv_buf_t.len = size; + ab->uv_buf_t.base = (char*) (ab + 1); + } + + *buf = ab->uv_buf_t; +} + + +static void buf_free(const uv_buf_t* buf) { + buf_t* ab = (buf_t*) buf->base - 1; + ab->next = buf_freelist; + buf_freelist = ab; +} + + +static void pinger_close_cb(uv_handle_t* handle) { + pinger_t* pinger; + + pinger = (pinger_t*)handle->data; + fprintf(stderr, "ping_pongs: %d roundtrips/s\n", (1000 * pinger->pongs) / TIME); + fflush(stderr); + + free(pinger); + + completed_pingers++; +} + + +static void pinger_write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + + free(req); +} + + +static void pinger_write_ping(pinger_t* pinger) { + uv_write_t* req; + uv_buf_t buf; + + buf = uv_buf_init(PING, sizeof(PING) - 1); + + req = malloc(sizeof *req); + if (uv_write(req, (uv_stream_t*) &pinger->tcp, &buf, 1, pinger_write_cb)) { + FATAL("uv_write failed"); + } +} + + +static void pinger_shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(status == 0); + pinger_shutdown_cb_called++; + + /* + * The close callback has not been triggered yet. We must wait for EOF + * until we close the connection. + */ + ASSERT(completed_pingers == 0); +} + + +static void pinger_read_cb(uv_stream_t* tcp, + ssize_t nread, + const uv_buf_t* buf) { + ssize_t i; + pinger_t* pinger; + + pinger = (pinger_t*)tcp->data; + + if (nread < 0) { + ASSERT(nread == UV_EOF); + + if (buf->base) { + buf_free(buf); + } + + ASSERT(pinger_shutdown_cb_called == 1); + uv_close((uv_handle_t*)tcp, pinger_close_cb); + + return; + } + + /* Now we count the pings */ + for (i = 0; i < nread; i++) { + ASSERT(buf->base[i] == PING[pinger->state]); + pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); + if (pinger->state == 0) { + pinger->pongs++; + if (uv_now(loop) - start_time > TIME) { + uv_shutdown(&pinger->shutdown_req, + (uv_stream_t*) tcp, + pinger_shutdown_cb); + break; + } else { + pinger_write_ping(pinger); + } + } + } + + buf_free(buf); +} + + +static void pinger_connect_cb(uv_connect_t* req, int status) { + pinger_t *pinger = (pinger_t*)req->handle->data; + + ASSERT(status == 0); + + pinger_write_ping(pinger); + + if (uv_read_start(req->handle, buf_alloc, pinger_read_cb)) { + FATAL("uv_read_start failed"); + } +} + + +static void pinger_new(void) { + struct sockaddr_in client_addr; + struct sockaddr_in server_addr; + pinger_t *pinger; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + pinger = malloc(sizeof(*pinger)); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connect to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(loop, &pinger->tcp); + ASSERT(!r); + + pinger->tcp.data = pinger; + + ASSERT(0 == uv_tcp_bind(&pinger->tcp, + (const struct sockaddr*) &client_addr, + 0)); + + r = uv_tcp_connect(&pinger->connect_req, + &pinger->tcp, + (const struct sockaddr*) &server_addr, + pinger_connect_cb); + ASSERT(!r); +} + + +BENCHMARK_IMPL(ping_pongs) { + loop = uv_default_loop(); + + start_time = uv_now(loop); + + pinger_new(); + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(completed_pingers == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-pound.c b/3rdparty/libuv/test/benchmark-pound.c new file mode 100644 index 00000000000..79f36345037 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-pound.c @@ -0,0 +1,351 @@ +/* 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 "task.h" +#include "uv.h" + +/* Update this is you're going to run > 1000 concurrent requests. */ +#define MAX_CONNS 1000 + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +#undef DEBUG +#define DEBUG 0 + +struct conn_rec_s; + +typedef void (*setup_fn)(int num, void* arg); +typedef void (*make_connect_fn)(struct conn_rec_s* conn); +typedef int (*connect_fn)(int num, make_connect_fn make_connect, void* arg); + +/* Base class for tcp_conn_rec and pipe_conn_rec. + * The ordering of fields matters! + */ +typedef struct conn_rec_s { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_stream_t stream; +} conn_rec; + +typedef struct { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_tcp_t stream; +} tcp_conn_rec; + +typedef struct { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_pipe_t stream; +} pipe_conn_rec; + +static char buffer[] = "QS"; + +static uv_loop_t* loop; + +static tcp_conn_rec tcp_conns[MAX_CONNS]; +static pipe_conn_rec pipe_conns[MAX_CONNS]; + +static uint64_t start; /* in ms */ +static int closed_streams; +static int conns_failed; + +static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); +static void connect_cb(uv_connect_t* conn_req, int status); +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); +static void close_cb(uv_handle_t* handle); + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void after_write(uv_write_t* req, int status) { + if (status != 0) { + fprintf(stderr, "write error %s\n", uv_err_name(status)); + uv_close((uv_handle_t*)req->handle, close_cb); + conns_failed++; + return; + } +} + + +static void connect_cb(uv_connect_t* req, int status) { + conn_rec* conn; + uv_buf_t buf; + int r; + + if (status != 0) { +#if DEBUG + fprintf(stderr, "connect error %s\n", uv_err_name(status)); +#endif + uv_close((uv_handle_t*)req->handle, close_cb); + conns_failed++; + return; + } + + ASSERT(req != NULL); + ASSERT(status == 0); + + conn = (conn_rec*)req->data; + ASSERT(conn != NULL); + +#if DEBUG + printf("connect_cb %d\n", conn->i); +#endif + + r = uv_read_start(&conn->stream, alloc_cb, read_cb); + ASSERT(r == 0); + + buf.base = buffer; + buf.len = sizeof(buffer) - 1; + + r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write); + ASSERT(r == 0); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + + ASSERT(stream != NULL); + +#if DEBUG + printf("read_cb %d\n", p->i); +#endif + + uv_close((uv_handle_t*)stream, close_cb); + + if (nread < 0) { + if (nread == UV_EOF) { + ; + } else if (nread == UV_ECONNRESET) { + conns_failed++; + } else { + fprintf(stderr, "read error %s\n", uv_err_name(nread)); + ASSERT(0); + } + } +} + + +static void close_cb(uv_handle_t* handle) { + conn_rec* p = (conn_rec*)handle->data; + + ASSERT(handle != NULL); + closed_streams++; + +#if DEBUG + printf("close_cb %d\n", p->i); +#endif + + if (uv_now(loop) - start < 10000) { + p->make_connect(p); + } +} + + +static void tcp_do_setup(int num, void* arg) { + int i; + + for (i = 0; i < num; i++) { + tcp_conns[i].i = i; + } +} + + +static void pipe_do_setup(int num, void* arg) { + int i; + + for (i = 0; i < num; i++) { + pipe_conns[i].i = i; + } +} + + +static void tcp_make_connect(conn_rec* p) { + struct sockaddr_in addr; + tcp_conn_rec* tp; + int r; + + tp = (tcp_conn_rec*) p; + + r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_connect(&tp->conn_req, + (uv_tcp_t*) &p->stream, + (const struct sockaddr*) &addr, + connect_cb); + if (r) { + fprintf(stderr, "uv_tcp_connect error %s\n", uv_err_name(r)); + ASSERT(0); + } + +#if DEBUG + printf("make connect %d\n", p->i); +#endif + + p->conn_req.data = p; + p->write_req.data = p; + p->stream.data = p; +} + + +static void pipe_make_connect(conn_rec* p) { + int r; + + r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream, 0); + ASSERT(r == 0); + + uv_pipe_connect(&((pipe_conn_rec*) p)->conn_req, + (uv_pipe_t*) &p->stream, + TEST_PIPENAME, + connect_cb); + +#if DEBUG + printf("make connect %d\n", p->i); +#endif + + p->conn_req.data = p; + p->write_req.data = p; + p->stream.data = p; +} + + +static int tcp_do_connect(int num, make_connect_fn make_connect, void* arg) { + int i; + + for (i = 0; i < num; i++) { + tcp_make_connect((conn_rec*)&tcp_conns[i]); + tcp_conns[i].make_connect = make_connect; + } + + return 0; +} + + +static int pipe_do_connect(int num, make_connect_fn make_connect, void* arg) { + int i; + + for (i = 0; i < num; i++) { + pipe_make_connect((conn_rec*)&pipe_conns[i]); + pipe_conns[i].make_connect = make_connect; + } + + return 0; +} + + +static int pound_it(int concurrency, + const char* type, + setup_fn do_setup, + connect_fn do_connect, + make_connect_fn make_connect, + void* arg) { + double secs; + int r; + uint64_t start_time; /* in ns */ + uint64_t end_time; + + loop = uv_default_loop(); + + uv_update_time(loop); + start = uv_now(loop); + + /* Run benchmark for at least five seconds. */ + start_time = uv_hrtime(); + + do_setup(concurrency, arg); + + r = do_connect(concurrency, make_connect, arg); + ASSERT(!r); + + uv_run(loop, UV_RUN_DEFAULT); + + end_time = uv_hrtime(); + + /* Number of fractional seconds it took to run the benchmark. */ + secs = (double)(end_time - start_time) / NANOSEC; + + fprintf(stderr, "%s-conn-pound-%d: %.0f accepts/s (%d failed)\n", + type, + concurrency, + closed_streams / secs, + conns_failed); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +BENCHMARK_IMPL(tcp4_pound_100) { + return pound_it(100, + "tcp", + tcp_do_setup, + tcp_do_connect, + tcp_make_connect, + NULL); +} + + +BENCHMARK_IMPL(tcp4_pound_1000) { + return pound_it(1000, + "tcp", + tcp_do_setup, + tcp_do_connect, + tcp_make_connect, + NULL); +} + + +BENCHMARK_IMPL(pipe_pound_100) { + return pound_it(100, + "pipe", + pipe_do_setup, + pipe_do_connect, + pipe_make_connect, + NULL); +} + + +BENCHMARK_IMPL(pipe_pound_1000) { + return pound_it(1000, + "pipe", + pipe_do_setup, + pipe_do_connect, + pipe_make_connect, + NULL); +} diff --git a/3rdparty/libuv/test/benchmark-pump.c b/3rdparty/libuv/test/benchmark-pump.c new file mode 100644 index 00000000000..88f2dc5c658 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-pump.c @@ -0,0 +1,476 @@ +/* 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 "task.h" +#include "uv.h" + +#include <math.h> +#include <stdio.h> + + +static int TARGET_CONNECTIONS; +#define WRITE_BUFFER_SIZE 8192 +#define MAX_SIMULTANEOUS_CONNECTS 100 + +#define PRINT_STATS 0 +#define STATS_INTERVAL 1000 /* msec */ +#define STATS_COUNT 5 + + +static void do_write(uv_stream_t*); +static void maybe_connect_some(); + +static uv_req_t* req_alloc(); +static void req_free(uv_req_t* uv_req); + +static void buf_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf); +static void buf_free(const uv_buf_t* buf); + +static uv_loop_t* loop; + +static uv_tcp_t tcpServer; +static uv_pipe_t pipeServer; +static uv_stream_t* server; +static struct sockaddr_in listen_addr; +static struct sockaddr_in connect_addr; + +static int64_t start_time; + +static int max_connect_socket = 0; +static int max_read_sockets = 0; +static int read_sockets = 0; +static int write_sockets = 0; + +static int64_t nrecv = 0; +static int64_t nrecv_total = 0; +static int64_t nsent = 0; +static int64_t nsent_total = 0; + +static int stats_left = 0; + +static char write_buffer[WRITE_BUFFER_SIZE]; + +/* Make this as large as you need. */ +#define MAX_WRITE_HANDLES 1000 + +static stream_type type; + +static uv_tcp_t tcp_write_handles[MAX_WRITE_HANDLES]; +static uv_pipe_t pipe_write_handles[MAX_WRITE_HANDLES]; + +static uv_timer_t timer_handle; + + +static double gbit(int64_t bytes, int64_t passed_ms) { + double gbits = ((double)bytes / (1024 * 1024 * 1024)) * 8; + return gbits / ((double)passed_ms / 1000); +} + + +static void show_stats(uv_timer_t* handle) { + int64_t diff; + int i; + +#if PRINT_STATS + fprintf(stderr, "connections: %d, write: %.1f gbit/s\n", + write_sockets, + gbit(nsent, STATS_INTERVAL)); + fflush(stderr); +#endif + + /* Exit if the show is over */ + if (!--stats_left) { + + uv_update_time(loop); + diff = uv_now(loop) - start_time; + + fprintf(stderr, "%s_pump%d_client: %.1f gbit/s\n", + type == TCP ? "tcp" : "pipe", + write_sockets, + gbit(nsent_total, diff)); + fflush(stderr); + + for (i = 0; i < write_sockets; i++) { + if (type == TCP) + uv_close((uv_handle_t*) &tcp_write_handles[i], NULL); + else + uv_close((uv_handle_t*) &pipe_write_handles[i], NULL); + } + + exit(0); + } + + /* Reset read and write counters */ + nrecv = 0; + nsent = 0; +} + + +static void read_show_stats(void) { + int64_t diff; + + uv_update_time(loop); + diff = uv_now(loop) - start_time; + + fprintf(stderr, "%s_pump%d_server: %.1f gbit/s\n", + type == TCP ? "tcp" : "pipe", + max_read_sockets, + gbit(nrecv_total, diff)); + fflush(stderr); +} + + + +static void read_sockets_close_cb(uv_handle_t* handle) { + free(handle); + read_sockets--; + + /* If it's past the first second and everyone has closed their connection + * Then print stats. + */ + if (uv_now(loop) - start_time > 1000 && read_sockets == 0) { + read_show_stats(); + uv_close((uv_handle_t*)server, NULL); + } +} + + +static void start_stats_collection(void) { + int r; + + /* Show-stats timer */ + stats_left = STATS_COUNT; + r = uv_timer_init(loop, &timer_handle); + ASSERT(r == 0); + r = uv_timer_start(&timer_handle, show_stats, STATS_INTERVAL, STATS_INTERVAL); + ASSERT(r == 0); + + uv_update_time(loop); + start_time = uv_now(loop); +} + + +static void read_cb(uv_stream_t* stream, ssize_t bytes, const uv_buf_t* buf) { + if (nrecv_total == 0) { + ASSERT(start_time == 0); + uv_update_time(loop); + start_time = uv_now(loop); + } + + if (bytes < 0) { + uv_close((uv_handle_t*)stream, read_sockets_close_cb); + return; + } + + buf_free(buf); + + nrecv += bytes; + nrecv_total += bytes; +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + + req_free((uv_req_t*) req); + + nsent += sizeof write_buffer; + nsent_total += sizeof write_buffer; + + do_write((uv_stream_t*) req->handle); +} + + +static void do_write(uv_stream_t* stream) { + uv_write_t* req; + uv_buf_t buf; + int r; + + buf.base = (char*) &write_buffer; + buf.len = sizeof write_buffer; + + req = (uv_write_t*) req_alloc(); + r = uv_write(req, stream, &buf, 1, write_cb); + ASSERT(r == 0); +} + + +static void connect_cb(uv_connect_t* req, int status) { + int i; + + if (status) { + fprintf(stderr, "%s", uv_strerror(status)); + fflush(stderr); + } + ASSERT(status == 0); + + write_sockets++; + req_free((uv_req_t*) req); + + maybe_connect_some(); + + if (write_sockets == TARGET_CONNECTIONS) { + start_stats_collection(); + + /* Yay! start writing */ + for (i = 0; i < write_sockets; i++) { + if (type == TCP) + do_write((uv_stream_t*) &tcp_write_handles[i]); + else + do_write((uv_stream_t*) &pipe_write_handles[i]); + } + } +} + + +static void maybe_connect_some(void) { + uv_connect_t* req; + uv_tcp_t* tcp; + uv_pipe_t* pipe; + int r; + + while (max_connect_socket < TARGET_CONNECTIONS && + max_connect_socket < write_sockets + MAX_SIMULTANEOUS_CONNECTS) { + if (type == TCP) { + tcp = &tcp_write_handles[max_connect_socket++]; + + r = uv_tcp_init(loop, tcp); + ASSERT(r == 0); + + req = (uv_connect_t*) req_alloc(); + r = uv_tcp_connect(req, + tcp, + (const struct sockaddr*) &connect_addr, + connect_cb); + ASSERT(r == 0); + } else { + pipe = &pipe_write_handles[max_connect_socket++]; + + r = uv_pipe_init(loop, pipe, 0); + ASSERT(r == 0); + + req = (uv_connect_t*) req_alloc(); + uv_pipe_connect(req, pipe, TEST_PIPENAME, connect_cb); + } + } +} + + +static void connection_cb(uv_stream_t* s, int status) { + uv_stream_t* stream; + int r; + + ASSERT(server == s); + ASSERT(status == 0); + + if (type == TCP) { + stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t)); + r = uv_tcp_init(loop, (uv_tcp_t*)stream); + ASSERT(r == 0); + } else { + stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t)); + r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); + ASSERT(r == 0); + } + + r = uv_accept(s, stream); + ASSERT(r == 0); + + r = uv_read_start(stream, buf_alloc, read_cb); + ASSERT(r == 0); + + read_sockets++; + max_read_sockets++; +} + + +/* + * Request allocator + */ + +typedef struct req_list_s { + union uv_any_req uv_req; + struct req_list_s* next; +} req_list_t; + + +static req_list_t* req_freelist = NULL; + + +static uv_req_t* req_alloc(void) { + req_list_t* req; + + req = req_freelist; + if (req != NULL) { + req_freelist = req->next; + return (uv_req_t*) req; + } + + req = (req_list_t*) malloc(sizeof *req); + return (uv_req_t*) req; +} + + +static void req_free(uv_req_t* uv_req) { + req_list_t* req = (req_list_t*) uv_req; + + req->next = req_freelist; + req_freelist = req; +} + + +/* + * Buffer allocator + */ + +typedef struct buf_list_s { + uv_buf_t uv_buf_t; + struct buf_list_s* next; +} buf_list_t; + + +static buf_list_t* buf_freelist = NULL; + + +static void buf_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf_list_t* ab; + + ab = buf_freelist; + if (ab != NULL) + buf_freelist = ab->next; + else { + ab = malloc(size + sizeof(*ab)); + ab->uv_buf_t.len = size; + ab->uv_buf_t.base = (char*) (ab + 1); + } + + *buf = ab->uv_buf_t; +} + + +static void buf_free(const uv_buf_t* buf) { + buf_list_t* ab = (buf_list_t*) buf->base - 1; + ab->next = buf_freelist; + buf_freelist = ab; +} + + +HELPER_IMPL(tcp_pump_server) { + int r; + + type = TCP; + loop = uv_default_loop(); + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &listen_addr)); + + /* Server */ + server = (uv_stream_t*)&tcpServer; + r = uv_tcp_init(loop, &tcpServer); + ASSERT(r == 0); + r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &listen_addr, 0); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + return 0; +} + + +HELPER_IMPL(pipe_pump_server) { + int r; + type = PIPE; + + loop = uv_default_loop(); + + /* Server */ + server = (uv_stream_t*)&pipeServer; + r = uv_pipe_init(loop, &pipeServer, 0); + ASSERT(r == 0); + r = uv_pipe_bind(&pipeServer, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void tcp_pump(int n) { + ASSERT(n <= MAX_WRITE_HANDLES); + TARGET_CONNECTIONS = n; + type = TCP; + + loop = uv_default_loop(); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &connect_addr)); + + /* Start making connections */ + maybe_connect_some(); + + uv_run(loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); +} + + +static void pipe_pump(int n) { + ASSERT(n <= MAX_WRITE_HANDLES); + TARGET_CONNECTIONS = n; + type = PIPE; + + loop = uv_default_loop(); + + /* Start making connections */ + maybe_connect_some(); + + uv_run(loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); +} + + +BENCHMARK_IMPL(tcp_pump100_client) { + tcp_pump(100); + return 0; +} + + +BENCHMARK_IMPL(tcp_pump1_client) { + tcp_pump(1); + return 0; +} + + +BENCHMARK_IMPL(pipe_pump100_client) { + pipe_pump(100); + return 0; +} + + +BENCHMARK_IMPL(pipe_pump1_client) { + pipe_pump(1); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-sizes.c b/3rdparty/libuv/test/benchmark-sizes.c new file mode 100644 index 00000000000..9bf42f91537 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-sizes.c @@ -0,0 +1,46 @@ +/* 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 "task.h" +#include "uv.h" + + +BENCHMARK_IMPL(sizes) { + fprintf(stderr, "uv_shutdown_t: %u bytes\n", (unsigned int) sizeof(uv_shutdown_t)); + fprintf(stderr, "uv_write_t: %u bytes\n", (unsigned int) sizeof(uv_write_t)); + fprintf(stderr, "uv_connect_t: %u bytes\n", (unsigned int) sizeof(uv_connect_t)); + fprintf(stderr, "uv_udp_send_t: %u bytes\n", (unsigned int) sizeof(uv_udp_send_t)); + fprintf(stderr, "uv_tcp_t: %u bytes\n", (unsigned int) sizeof(uv_tcp_t)); + fprintf(stderr, "uv_pipe_t: %u bytes\n", (unsigned int) sizeof(uv_pipe_t)); + fprintf(stderr, "uv_tty_t: %u bytes\n", (unsigned int) sizeof(uv_tty_t)); + fprintf(stderr, "uv_prepare_t: %u bytes\n", (unsigned int) sizeof(uv_prepare_t)); + fprintf(stderr, "uv_check_t: %u bytes\n", (unsigned int) sizeof(uv_check_t)); + fprintf(stderr, "uv_idle_t: %u bytes\n", (unsigned int) sizeof(uv_idle_t)); + fprintf(stderr, "uv_async_t: %u bytes\n", (unsigned int) sizeof(uv_async_t)); + fprintf(stderr, "uv_timer_t: %u bytes\n", (unsigned int) sizeof(uv_timer_t)); + fprintf(stderr, "uv_fs_poll_t: %u bytes\n", (unsigned int) sizeof(uv_fs_poll_t)); + fprintf(stderr, "uv_fs_event_t: %u bytes\n", (unsigned int) sizeof(uv_fs_event_t)); + fprintf(stderr, "uv_process_t: %u bytes\n", (unsigned int) sizeof(uv_process_t)); + fprintf(stderr, "uv_poll_t: %u bytes\n", (unsigned int) sizeof(uv_poll_t)); + fprintf(stderr, "uv_loop_t: %u bytes\n", (unsigned int) sizeof(uv_loop_t)); + fflush(stderr); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-spawn.c b/3rdparty/libuv/test/benchmark-spawn.c new file mode 100644 index 00000000000..ed9ad608f37 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-spawn.c @@ -0,0 +1,164 @@ +/* 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. + */ + +/* This benchmark spawns itself 1000 times. */ + +#include "task.h" +#include "uv.h" + +static uv_loop_t* loop; + +static int N = 1000; +static int done; + +static uv_process_t process; +static uv_process_options_t options; +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; +static uv_pipe_t out; + +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + +static int process_open; +static int pipe_open; + + +static void spawn(void); + + +static void maybe_spawn(void) { + if (process_open == 0 && pipe_open == 0) { + done++; + if (done < N) { + spawn(); + } + } +} + + +static void process_close_cb(uv_handle_t* handle) { + ASSERT(process_open == 1); + process_open = 0; + maybe_spawn(); +} + + +static void exit_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + ASSERT(exit_status == 42); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, process_close_cb); +} + + +static void on_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = output + output_used; + buf->len = OUTPUT_SIZE - output_used; +} + + +static void pipe_close_cb(uv_handle_t* pipe) { + ASSERT(pipe_open == 1); + pipe_open = 0; + maybe_spawn(); +} + + +static void on_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) { + if (nread > 0) { + ASSERT(pipe_open == 1); + output_used += nread; + } else if (nread < 0) { + if (nread == UV_EOF) { + uv_close((uv_handle_t*)pipe, pipe_close_cb); + } + } +} + + +static void spawn(void) { + uv_stdio_container_t stdio[2]; + int r; + + ASSERT(process_open == 0); + ASSERT(pipe_open == 0); + + args[0] = exepath; + args[1] = "spawn_helper"; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; + + uv_pipe_init(loop, &out, 0); + + options.stdio = stdio; + options.stdio_count = 2; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + + r = uv_spawn(loop, &process, &options); + ASSERT(r == 0); + + process_open = 1; + pipe_open = 1; + output_used = 0; + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); +} + + +BENCHMARK_IMPL(spawn) { + int r; + static int64_t start_time, end_time; + + loop = uv_default_loop(); + + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + + uv_update_time(loop); + start_time = uv_now(loop); + + spawn(); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + uv_update_time(loop); + end_time = uv_now(loop); + + fprintf(stderr, "spawn: %.0f spawns/s\n", + (double) N / (double) (end_time - start_time) * 1000.0); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-tcp-write-batch.c b/3rdparty/libuv/test/benchmark-tcp-write-batch.c new file mode 100644 index 00000000000..96921b70db5 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-tcp-write-batch.c @@ -0,0 +1,144 @@ +/* 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> + +#define WRITE_REQ_DATA "Hello, world." +#define NUM_WRITE_REQS (1000 * 1000) + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req; + + +static write_req* write_reqs; +static uv_tcp_t tcp_client; +static uv_connect_t connect_req; +static uv_shutdown_t shutdown_req; + +static int shutdown_cb_called = 0; +static int connect_cb_called = 0; +static int write_cb_called = 0; +static int close_cb_called = 0; + +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void shutdown_cb(uv_shutdown_t* req, int status); +static void close_cb(uv_handle_t* handle); + + +static void connect_cb(uv_connect_t* req, int status) { + write_req* w; + int i; + int r; + + ASSERT(req->handle == (uv_stream_t*)&tcp_client); + + for (i = 0; i < NUM_WRITE_REQS; i++) { + w = &write_reqs[i]; + r = uv_write(&w->req, req->handle, &w->buf, 1, write_cb); + ASSERT(r == 0); + } + + r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); + ASSERT(r == 0); + + connect_cb_called++; +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + write_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*)&tcp_client); + ASSERT(req->handle->write_queue_size == 0); + + uv_close((uv_handle_t*)req->handle, close_cb); + free(write_reqs); + + shutdown_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*)&tcp_client); + close_cb_called++; +} + + +BENCHMARK_IMPL(tcp_write_batch) { + struct sockaddr_in addr; + uv_loop_t* loop; + uint64_t start; + uint64_t stop; + int i; + int r; + + write_reqs = malloc(sizeof(*write_reqs) * NUM_WRITE_REQS); + ASSERT(write_reqs != NULL); + + /* Prepare the data to write out. */ + for (i = 0; i < NUM_WRITE_REQS; i++) { + write_reqs[i].buf = uv_buf_init(WRITE_REQ_DATA, + sizeof(WRITE_REQ_DATA) - 1); + } + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(loop, &tcp_client); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + start = uv_hrtime(); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + stop = uv_hrtime(); + + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == NUM_WRITE_REQS); + ASSERT(shutdown_cb_called == 1); + ASSERT(close_cb_called == 1); + + printf("%ld write requests in %.2fs.\n", + (long)NUM_WRITE_REQS, + (stop - start) / 1e9); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-thread.c b/3rdparty/libuv/test/benchmark-thread.c new file mode 100644 index 00000000000..b37a7fd6d01 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-thread.c @@ -0,0 +1,64 @@ +/* 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> + +#define NUM_THREADS (20 * 1000) + +static volatile int num_threads; + + +static void thread_entry(void* arg) { + ASSERT(arg == (void *) 42); + num_threads++; + /* FIXME write barrier? */ +} + + +BENCHMARK_IMPL(thread_create) { + uint64_t start_time; + double duration; + uv_thread_t tid; + int i, r; + + start_time = uv_hrtime(); + + for (i = 0; i < NUM_THREADS; i++) { + r = uv_thread_create(&tid, thread_entry, (void *) 42); + ASSERT(r == 0); + + r = uv_thread_join(&tid); + ASSERT(r == 0); + } + + duration = (uv_hrtime() - start_time) / 1e9; + + ASSERT(num_threads == NUM_THREADS); + + printf("%d threads created in %.2f seconds (%.0f/s)\n", + NUM_THREADS, duration, NUM_THREADS / duration); + + return 0; +} diff --git a/3rdparty/libuv/test/benchmark-udp-pummel.c b/3rdparty/libuv/test/benchmark-udp-pummel.c new file mode 100644 index 00000000000..68a2373d781 --- /dev/null +++ b/3rdparty/libuv/test/benchmark-udp-pummel.c @@ -0,0 +1,243 @@ +/* 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 "task.h" +#include "uv.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define EXPECTED "RANG TANG DING DONG I AM THE JAPANESE SANDMAN" + +#define TEST_DURATION 5000 /* ms */ + +#define BASE_PORT 12345 + +struct sender_state { + struct sockaddr_in addr; + uv_udp_send_t send_req; + uv_udp_t udp_handle; +}; + +struct receiver_state { + struct sockaddr_in addr; + uv_udp_t udp_handle; +}; + +/* not used in timed mode */ +static unsigned int packet_counter = (unsigned int) 1e6; + +static int n_senders_; +static int n_receivers_; +static uv_buf_t bufs[5]; +static struct sender_state senders[1024]; +static struct receiver_state receivers[1024]; + +static unsigned int send_cb_called; +static unsigned int recv_cb_called; +static unsigned int close_cb_called; +static int timed; +static int exiting; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void send_cb(uv_udp_send_t* req, int status) { + struct sender_state* s; + + ASSERT(req != NULL); + + if (status != 0) { + ASSERT(status == UV_ECANCELED); + return; + } + + if (exiting) + return; + + s = container_of(req, struct sender_state, send_req); + ASSERT(req->handle == &s->udp_handle); + + if (timed) + goto send; + + if (packet_counter == 0) { + uv_close((uv_handle_t*)&s->udp_handle, NULL); + return; + } + + packet_counter--; + +send: + ASSERT(0 == uv_udp_send(&s->send_req, + &s->udp_handle, + bufs, + ARRAY_SIZE(bufs), + (const struct sockaddr*) &s->addr, + send_cb)); + send_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + if (nread == 0) + return; + + if (nread < 0) { + ASSERT(nread == UV_ECANCELED); + return; + } + + ASSERT(addr->sa_family == AF_INET); + ASSERT(!memcmp(buf->base, EXPECTED, nread)); + + recv_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void timeout_cb(uv_timer_t* timer) { + int i; + + exiting = 1; + + for (i = 0; i < n_senders_; i++) + uv_close((uv_handle_t*)&senders[i].udp_handle, close_cb); + + for (i = 0; i < n_receivers_; i++) + uv_close((uv_handle_t*)&receivers[i].udp_handle, close_cb); +} + + +static int pummel(unsigned int n_senders, + unsigned int n_receivers, + unsigned long timeout) { + uv_timer_t timer_handle; + uint64_t duration; + uv_loop_t* loop; + unsigned int i; + + ASSERT(n_senders <= ARRAY_SIZE(senders)); + ASSERT(n_receivers <= ARRAY_SIZE(receivers)); + + loop = uv_default_loop(); + + n_senders_ = n_senders; + n_receivers_ = n_receivers; + + if (timeout) { + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timeout_cb, timeout, 0)); + /* Timer should not keep loop alive. */ + uv_unref((uv_handle_t*)&timer_handle); + timed = 1; + } + + for (i = 0; i < n_receivers; i++) { + struct receiver_state* s = receivers + i; + struct sockaddr_in addr; + ASSERT(0 == uv_ip4_addr("0.0.0.0", BASE_PORT + i, &addr)); + ASSERT(0 == uv_udp_init(loop, &s->udp_handle)); + ASSERT(0 == uv_udp_bind(&s->udp_handle, (const struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_udp_recv_start(&s->udp_handle, alloc_cb, recv_cb)); + uv_unref((uv_handle_t*)&s->udp_handle); + } + + bufs[0] = uv_buf_init(EXPECTED + 0, 10); + bufs[1] = uv_buf_init(EXPECTED + 10, 10); + bufs[2] = uv_buf_init(EXPECTED + 20, 10); + bufs[3] = uv_buf_init(EXPECTED + 30, 10); + bufs[4] = uv_buf_init(EXPECTED + 40, 5); + + for (i = 0; i < n_senders; i++) { + struct sender_state* s = senders + i; + ASSERT(0 == uv_ip4_addr("127.0.0.1", + BASE_PORT + (i % n_receivers), + &s->addr)); + ASSERT(0 == uv_udp_init(loop, &s->udp_handle)); + ASSERT(0 == uv_udp_send(&s->send_req, + &s->udp_handle, + bufs, + ARRAY_SIZE(bufs), + (const struct sockaddr*) &s->addr, + send_cb)); + } + + duration = uv_hrtime(); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + duration = uv_hrtime() - duration; + /* convert from nanoseconds to milliseconds */ + duration = duration / (uint64_t) 1e6; + + printf("udp_pummel_%dv%d: %.0f/s received, %.0f/s sent. " + "%u received, %u sent in %.1f seconds.\n", + n_receivers, + n_senders, + recv_cb_called / (duration / 1000.0), + send_cb_called / (duration / 1000.0), + recv_cb_called, + send_cb_called, + duration / 1000.0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#define X(a, b) \ + BENCHMARK_IMPL(udp_pummel_##a##v##b) { \ + return pummel(a, b, 0); \ + } \ + BENCHMARK_IMPL(udp_timed_pummel_##a##v##b) { \ + return pummel(a, b, TEST_DURATION); \ + } + +X(1, 1) +X(1, 10) +X(1, 100) +X(1, 1000) +X(10, 10) +X(10, 100) +X(10, 1000) +X(100, 10) +X(100, 100) +X(100, 1000) +X(1000, 1000) + +#undef X diff --git a/3rdparty/libuv/test/blackhole-server.c b/3rdparty/libuv/test/blackhole-server.c new file mode 100644 index 00000000000..ad878b35c61 --- /dev/null +++ b/3rdparty/libuv/test/blackhole-server.c @@ -0,0 +1,121 @@ +/* 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> + +typedef struct { + uv_tcp_t handle; + uv_shutdown_t shutdown_req; +} conn_rec; + +static uv_tcp_t tcp_server; + +static void connection_cb(uv_stream_t* stream, int status); +static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); +static void shutdown_cb(uv_shutdown_t* req, int status); +static void close_cb(uv_handle_t* handle); + + +static void connection_cb(uv_stream_t* stream, int status) { + conn_rec* conn; + int r; + + ASSERT(status == 0); + ASSERT(stream == (uv_stream_t*)&tcp_server); + + conn = malloc(sizeof *conn); + ASSERT(conn != NULL); + + r = uv_tcp_init(stream->loop, &conn->handle); + ASSERT(r == 0); + + r = uv_accept(stream, (uv_stream_t*)&conn->handle); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&conn->handle, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + conn_rec* conn; + int r; + + if (nread >= 0) + return; + + ASSERT(nread == UV_EOF); + + conn = container_of(stream, conn_rec, handle); + + r = uv_shutdown(&conn->shutdown_req, stream, shutdown_cb); + ASSERT(r == 0); +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + conn_rec* conn = container_of(req, conn_rec, shutdown_req); + uv_close((uv_handle_t*)&conn->handle, close_cb); +} + + +static void close_cb(uv_handle_t* handle) { + conn_rec* conn = container_of(handle, conn_rec, handle); + free(conn); +} + + +HELPER_IMPL(tcp4_blackhole_server) { + struct sockaddr_in addr; + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(loop, &tcp_server); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, 128, connection_cb); + ASSERT(r == 0); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(0 && "Blackhole server dropped out of event loop."); + + return 0; +} diff --git a/3rdparty/libuv/test/dns-server.c b/3rdparty/libuv/test/dns-server.c new file mode 100644 index 00000000000..80052c70398 --- /dev/null +++ b/3rdparty/libuv/test/dns-server.c @@ -0,0 +1,340 @@ +/* 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 <string.h> + + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req_t; + + +/* used to track multiple DNS requests received */ +typedef struct { + char* prevbuf_ptr; + int prevbuf_pos; + int prevbuf_rem; +} dnsstate; + + +/* modify handle to append dnsstate */ +typedef struct { + uv_tcp_t handle; + dnsstate state; +} dnshandle; + + +static uv_loop_t* loop; + + +static uv_tcp_t server; + + +static void after_write(uv_write_t* req, int status); +static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf); +static void on_close(uv_handle_t* peer); +static void on_connection(uv_stream_t*, int status); + +#define WRITE_BUF_LEN (64*1024) +#define DNSREC_LEN (4) + +#define LEN_OFFSET 0 +#define QUERYID_OFFSET 2 + +static unsigned char DNSRsp[] = { + 0, 43, 0, 0, 0x81, 0x80, 0, 1, 0, 1, 0, 0, 0, 0 +}; + +static unsigned char qrecord[] = { + 5, 'e', 'c', 'h', 'o', 's', 3, 's', 'r', 'v', 0, 0, 1, 0, 1 +}; + +static unsigned char arecord[] = { + 0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 5, 0xbd, 0, 4, 10, 0, 1, 1 +}; + + +static void after_write(uv_write_t* req, int status) { + write_req_t* wr; + + if (status) { + fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); + ASSERT(0); + } + + wr = (write_req_t*) req; + + /* Free the read/write buffer and the request */ + free(wr->buf.base); + free(wr); +} + + +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, on_close); + free(req); +} + + +static void addrsp(write_req_t* wr, char* hdr) { + char * dnsrsp; + short int rsplen; + short int* reclen; + + rsplen = sizeof(DNSRsp) + sizeof(qrecord) + sizeof(arecord); + + ASSERT (rsplen + wr->buf.len < WRITE_BUF_LEN); + + dnsrsp = wr->buf.base + wr->buf.len; + + /* copy stock response */ + memcpy(dnsrsp, DNSRsp, sizeof(DNSRsp)); + memcpy(dnsrsp + sizeof(DNSRsp), qrecord, sizeof(qrecord)); + memcpy(dnsrsp + sizeof(DNSRsp) + sizeof(qrecord), arecord, sizeof(arecord)); + + /* overwrite with network order length and id from request header */ + reclen = (short int*)dnsrsp; + *reclen = htons(rsplen-2); + dnsrsp[QUERYID_OFFSET] = hdr[QUERYID_OFFSET]; + dnsrsp[QUERYID_OFFSET+1] = hdr[QUERYID_OFFSET+1]; + + wr->buf.len += rsplen; +} + +static void process_req(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + write_req_t* wr; + dnshandle* dns = (dnshandle*)handle; + char hdrbuf[DNSREC_LEN]; + int hdrbuf_remaining = DNSREC_LEN; + int rec_remaining = 0; + int readbuf_remaining; + char* dnsreq; + char* hdrstart; + int usingprev = 0; + + wr = (write_req_t*) malloc(sizeof *wr); + wr->buf.base = (char*)malloc(WRITE_BUF_LEN); + wr->buf.len = 0; + + if (dns->state.prevbuf_ptr != NULL) { + dnsreq = dns->state.prevbuf_ptr + dns->state.prevbuf_pos; + readbuf_remaining = dns->state.prevbuf_rem; + usingprev = 1; + } else { + dnsreq = buf->base; + readbuf_remaining = nread; + } + hdrstart = dnsreq; + + while (dnsreq != NULL) { + /* something to process */ + while (readbuf_remaining > 0) { + /* something to process in current buffer */ + if (hdrbuf_remaining > 0) { + /* process len and id */ + if (readbuf_remaining < hdrbuf_remaining) { + /* too little to get request header. save for next buffer */ + memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], + dnsreq, + readbuf_remaining); + hdrbuf_remaining = DNSREC_LEN - readbuf_remaining; + break; + } else { + /* save header */ + memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], + dnsreq, + hdrbuf_remaining); + dnsreq += hdrbuf_remaining; + readbuf_remaining -= hdrbuf_remaining; + hdrbuf_remaining = 0; + + /* get record length */ + rec_remaining = (unsigned) hdrbuf[0] * 256 + (unsigned) hdrbuf[1]; + rec_remaining -= (DNSREC_LEN - 2); + } + } + + if (rec_remaining <= readbuf_remaining) { + /* prepare reply */ + addrsp(wr, hdrbuf); + + /* move to next record */ + dnsreq += rec_remaining; + hdrstart = dnsreq; + readbuf_remaining -= rec_remaining; + rec_remaining = 0; + hdrbuf_remaining = DNSREC_LEN; + } else { + /* otherwise this buffer is done. */ + rec_remaining -= readbuf_remaining; + break; + } + } + + /* If we had to use bytes from prev buffer, start processing the current + * one. + */ + if (usingprev == 1) { + /* free previous buffer */ + free(dns->state.prevbuf_ptr); + dnsreq = buf->base; + readbuf_remaining = nread; + usingprev = 0; + } else { + dnsreq = NULL; + } + } + + /* send write buffer */ + if (wr->buf.len > 0) { + if (uv_write((uv_write_t*) &wr->req, handle, &wr->buf, 1, after_write)) { + FATAL("uv_write failed"); + } + } + + if (readbuf_remaining > 0) { + /* save start of record position, so we can continue on next read */ + dns->state.prevbuf_ptr = buf->base; + dns->state.prevbuf_pos = hdrstart - buf->base; + dns->state.prevbuf_rem = nread - dns->state.prevbuf_pos; + } else { + /* nothing left in this buffer */ + dns->state.prevbuf_ptr = NULL; + dns->state.prevbuf_pos = 0; + dns->state.prevbuf_rem = 0; + free(buf->base); + } +} + +static void after_read(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + uv_shutdown_t* req; + + if (nread < 0) { + /* Error or EOF */ + ASSERT(nread == UV_EOF); + + if (buf->base) { + free(buf->base); + } + + req = malloc(sizeof *req); + uv_shutdown(req, handle, after_shutdown); + + return; + } + + if (nread == 0) { + /* Everything OK, but nothing read. */ + free(buf->base); + return; + } + /* process requests and send responses */ + process_req(handle, nread, buf); +} + + +static void on_close(uv_handle_t* peer) { + free(peer); +} + + +static void buf_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + + +static void on_connection(uv_stream_t* server, int status) { + dnshandle* handle; + int r; + + ASSERT(status == 0); + + handle = (dnshandle*) malloc(sizeof *handle); + ASSERT(handle != NULL); + + /* initialize read buffer state */ + handle->state.prevbuf_ptr = 0; + handle->state.prevbuf_pos = 0; + handle->state.prevbuf_rem = 0; + + r = uv_tcp_init(loop, (uv_tcp_t*)handle); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)handle); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)handle, buf_alloc, after_read); + ASSERT(r == 0); +} + + +static int dns_start(int port) { + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); + + r = uv_tcp_init(loop, &server); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Bind error\n"); + return 1; + } + + r = uv_listen((uv_stream_t*)&server, 128, on_connection); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Listen error\n"); + return 1; + } + + return 0; +} + + +HELPER_IMPL(dns_server) { + loop = uv_default_loop(); + + if (dns_start(TEST_PORT_2)) + return 1; + + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} diff --git a/3rdparty/libuv/test/echo-server.c b/3rdparty/libuv/test/echo-server.c new file mode 100644 index 00000000000..bfed67675dd --- /dev/null +++ b/3rdparty/libuv/test/echo-server.c @@ -0,0 +1,378 @@ +/* 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> + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req_t; + +static uv_loop_t* loop; + +static int server_closed; +static stream_type serverType; +static uv_tcp_t tcpServer; +static uv_udp_t udpServer; +static uv_pipe_t pipeServer; +static uv_handle_t* server; + +static void after_write(uv_write_t* req, int status); +static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf); +static void on_close(uv_handle_t* peer); +static void on_server_close(uv_handle_t* handle); +static void on_connection(uv_stream_t*, int status); + + +static void after_write(uv_write_t* req, int status) { + write_req_t* wr; + + /* Free the read/write buffer and the request */ + wr = (write_req_t*) req; + free(wr->buf.base); + free(wr); + + if (status == 0) + return; + + fprintf(stderr, + "uv_write error: %s - %s\n", + uv_err_name(status), + uv_strerror(status)); +} + + +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, on_close); + free(req); +} + + +static void after_read(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + int i; + write_req_t *wr; + uv_shutdown_t* sreq; + + if (nread < 0) { + /* Error or EOF */ + ASSERT(nread == UV_EOF); + + free(buf->base); + sreq = malloc(sizeof* sreq); + ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown)); + return; + } + + if (nread == 0) { + /* Everything OK, but nothing read. */ + free(buf->base); + return; + } + + /* + * Scan for the letter Q which signals that we should quit the server. + * If we get QS it means close the stream. + */ + if (!server_closed) { + for (i = 0; i < nread; i++) { + if (buf->base[i] == 'Q') { + if (i + 1 < nread && buf->base[i + 1] == 'S') { + free(buf->base); + uv_close((uv_handle_t*)handle, on_close); + return; + } else { + uv_close(server, on_server_close); + server_closed = 1; + } + } + } + } + + wr = (write_req_t*) malloc(sizeof *wr); + ASSERT(wr != NULL); + wr->buf = uv_buf_init(buf->base, nread); + + if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) { + FATAL("uv_write failed"); + } +} + + +static void on_close(uv_handle_t* peer) { + free(peer); +} + + +static void echo_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + + +static void on_connection(uv_stream_t* server, int status) { + uv_stream_t* stream; + int r; + + if (status != 0) { + fprintf(stderr, "Connect error %s\n", uv_err_name(status)); + } + ASSERT(status == 0); + + switch (serverType) { + case TCP: + stream = malloc(sizeof(uv_tcp_t)); + ASSERT(stream != NULL); + r = uv_tcp_init(loop, (uv_tcp_t*)stream); + ASSERT(r == 0); + break; + + case PIPE: + stream = malloc(sizeof(uv_pipe_t)); + ASSERT(stream != NULL); + r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); + ASSERT(r == 0); + break; + + default: + ASSERT(0 && "Bad serverType"); + abort(); + } + + /* associate server with stream */ + stream->data = server; + + r = uv_accept(server, stream); + ASSERT(r == 0); + + r = uv_read_start(stream, echo_alloc, after_read); + ASSERT(r == 0); +} + + +static void on_server_close(uv_handle_t* handle) { + ASSERT(handle == server); +} + + +static void on_send(uv_udp_send_t* req, int status); + + +static void on_recv(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + uv_udp_send_t* req; + uv_buf_t sndbuf; + + ASSERT(nread > 0); + ASSERT(addr->sa_family == AF_INET); + + req = malloc(sizeof(*req)); + ASSERT(req != NULL); + + sndbuf = *rcvbuf; + ASSERT(0 == uv_udp_send(req, handle, &sndbuf, 1, addr, on_send)); +} + + +static void on_send(uv_udp_send_t* req, int status) { + ASSERT(status == 0); + free(req); +} + + +static int tcp4_echo_start(int port) { + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); + + server = (uv_handle_t*)&tcpServer; + serverType = TCP; + + r = uv_tcp_init(loop, &tcpServer); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Bind error\n"); + return 1; + } + + r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Listen error %s\n", uv_err_name(r)); + return 1; + } + + return 0; +} + + +static int tcp6_echo_start(int port) { + struct sockaddr_in6 addr6; + int r; + + ASSERT(0 == uv_ip6_addr("::1", port, &addr6)); + + server = (uv_handle_t*)&tcpServer; + serverType = TCP; + + r = uv_tcp_init(loop, &tcpServer); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + /* IPv6 is optional as not all platforms support it */ + r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr6, 0); + if (r) { + /* show message but return OK */ + fprintf(stderr, "IPv6 not supported\n"); + return 0; + } + + r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Listen error\n"); + return 1; + } + + return 0; +} + + +static int udp4_echo_start(int port) { + int r; + + server = (uv_handle_t*)&udpServer; + serverType = UDP; + + r = uv_udp_init(loop, &udpServer); + if (r) { + fprintf(stderr, "uv_udp_init: %s\n", uv_strerror(r)); + return 1; + } + + r = uv_udp_recv_start(&udpServer, echo_alloc, on_recv); + if (r) { + fprintf(stderr, "uv_udp_recv_start: %s\n", uv_strerror(r)); + return 1; + } + + return 0; +} + + +static int pipe_echo_start(char* pipeName) { + int r; + +#ifndef _WIN32 + { + uv_fs_t req; + uv_fs_unlink(NULL, &req, pipeName, NULL); + uv_fs_req_cleanup(&req); + } +#endif + + server = (uv_handle_t*)&pipeServer; + serverType = PIPE; + + r = uv_pipe_init(loop, &pipeServer, 0); + if (r) { + fprintf(stderr, "uv_pipe_init: %s\n", uv_strerror(r)); + return 1; + } + + r = uv_pipe_bind(&pipeServer, pipeName); + if (r) { + fprintf(stderr, "uv_pipe_bind: %s\n", uv_strerror(r)); + return 1; + } + + r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection); + if (r) { + fprintf(stderr, "uv_pipe_listen: %s\n", uv_strerror(r)); + return 1; + } + + return 0; +} + + +HELPER_IMPL(tcp4_echo_server) { + loop = uv_default_loop(); + + if (tcp4_echo_start(TEST_PORT)) + return 1; + + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} + + +HELPER_IMPL(tcp6_echo_server) { + loop = uv_default_loop(); + + if (tcp6_echo_start(TEST_PORT)) + return 1; + + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} + + +HELPER_IMPL(pipe_echo_server) { + loop = uv_default_loop(); + + if (pipe_echo_start(TEST_PIPENAME)) + return 1; + + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} + + +HELPER_IMPL(udp4_echo_server) { + loop = uv_default_loop(); + + if (udp4_echo_start(TEST_PORT)) + return 1; + + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} diff --git a/3rdparty/libuv/test/fixtures/empty_file b/3rdparty/libuv/test/fixtures/empty_file new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/3rdparty/libuv/test/fixtures/empty_file diff --git a/3rdparty/libuv/test/fixtures/load_error.node b/3rdparty/libuv/test/fixtures/load_error.node new file mode 100644 index 00000000000..323fae03f46 --- /dev/null +++ b/3rdparty/libuv/test/fixtures/load_error.node @@ -0,0 +1 @@ +foobar diff --git a/3rdparty/libuv/test/run-benchmarks.c b/3rdparty/libuv/test/run-benchmarks.c new file mode 100644 index 00000000000..6e42623d54c --- /dev/null +++ b/3rdparty/libuv/test/run-benchmarks.c @@ -0,0 +1,65 @@ +/* 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 <stdio.h> +#include <string.h> + +#include "runner.h" +#include "task.h" + +/* Actual benchmarks and helpers are defined in benchmark-list.h */ +#include "benchmark-list.h" + + +static int maybe_run_test(int argc, char **argv); + + +int main(int argc, char **argv) { + if (platform_init(argc, argv)) + return EXIT_FAILURE; + + switch (argc) { + case 1: return run_tests(1); + case 2: return maybe_run_test(argc, argv); + case 3: return run_test_part(argv[1], argv[2]); + default: + fprintf(stderr, "Too many arguments.\n"); + fflush(stderr); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +static int maybe_run_test(int argc, char **argv) { + if (strcmp(argv[1], "--list") == 0) { + print_tests(stdout); + return 0; + } + + if (strcmp(argv[1], "spawn_helper") == 0) { + printf("hello world\n"); + return 42; + } + + return run_test(argv[1], 1, 1); +} diff --git a/3rdparty/libuv/test/run-tests.c b/3rdparty/libuv/test/run-tests.c new file mode 100644 index 00000000000..b4be01f6f95 --- /dev/null +++ b/3rdparty/libuv/test/run-tests.c @@ -0,0 +1,181 @@ +/* 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> +#include <stdio.h> +#include <string.h> + +#ifdef _WIN32 +# include <io.h> +#else +# include <unistd.h> +#endif + +#include "uv.h" +#include "runner.h" +#include "task.h" + +/* Actual tests and helpers are defined in test-list.h */ +#include "test-list.h" + +int ipc_helper(int listen_after_write); +int ipc_helper_tcp_connection(void); +int ipc_send_recv_helper(void); +int ipc_helper_bind_twice(void); +int stdio_over_pipes_helper(void); +int spawn_stdin_stdout(void); + +static int maybe_run_test(int argc, char **argv); + + +int main(int argc, char **argv) { + if (platform_init(argc, argv)) + return EXIT_FAILURE; + + argv = uv_setup_args(argc, argv); + + switch (argc) { + case 1: return run_tests(0); + case 2: return maybe_run_test(argc, argv); + case 3: return run_test_part(argv[1], argv[2]); + default: + fprintf(stderr, "Too many arguments.\n"); + fflush(stderr); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +static int maybe_run_test(int argc, char **argv) { + if (strcmp(argv[1], "--list") == 0) { + print_tests(stdout); + return 0; + } + + if (strcmp(argv[1], "ipc_helper_listen_before_write") == 0) { + return ipc_helper(0); + } + + if (strcmp(argv[1], "ipc_helper_listen_after_write") == 0) { + return ipc_helper(1); + } + + if (strcmp(argv[1], "ipc_send_recv_helper") == 0) { + return ipc_send_recv_helper(); + } + + if (strcmp(argv[1], "ipc_helper_tcp_connection") == 0) { + return ipc_helper_tcp_connection(); + } + + if (strcmp(argv[1], "ipc_helper_bind_twice") == 0) { + return ipc_helper_bind_twice(); + } + + if (strcmp(argv[1], "stdio_over_pipes_helper") == 0) { + return stdio_over_pipes_helper(); + } + + if (strcmp(argv[1], "spawn_helper1") == 0) { + return 1; + } + + if (strcmp(argv[1], "spawn_helper2") == 0) { + printf("hello world\n"); + return 1; + } + + if (strcmp(argv[1], "spawn_helper3") == 0) { + char buffer[256]; + ASSERT(buffer == fgets(buffer, sizeof(buffer) - 1, stdin)); + buffer[sizeof(buffer) - 1] = '\0'; + fputs(buffer, stdout); + return 1; + } + + if (strcmp(argv[1], "spawn_helper4") == 0) { + /* Never surrender, never return! */ + while (1) uv_sleep(10000); + } + + if (strcmp(argv[1], "spawn_helper5") == 0) { + const char out[] = "fourth stdio!\n"; +#ifdef _WIN32 + DWORD bytes; + WriteFile((HANDLE) _get_osfhandle(3), out, sizeof(out) - 1, &bytes, NULL); +#else + { + ssize_t r; + + do + r = write(3, out, sizeof(out) - 1); + while (r == -1 && errno == EINTR); + + fsync(3); + } +#endif + return 1; + } + + if (strcmp(argv[1], "spawn_helper6") == 0) { + int r; + + r = fprintf(stdout, "hello world\n"); + ASSERT(r > 0); + + r = fprintf(stderr, "hello errworld\n"); + ASSERT(r > 0); + + return 1; + } + + if (strcmp(argv[1], "spawn_helper7") == 0) { + int r; + char *test; + /* Test if the test value from the parent is still set */ + test = getenv("ENV_TEST"); + ASSERT(test != NULL); + + r = fprintf(stdout, "%s", test); + ASSERT(r > 0); + + return 1; + } + +#ifndef _WIN32 + if (strcmp(argv[1], "spawn_helper8") == 0) { + int fd; + ASSERT(sizeof(fd) == read(0, &fd, sizeof(fd))); + ASSERT(fd > 2); + ASSERT(-1 == write(fd, "x", 1)); + + return 1; + } +#endif /* !_WIN32 */ + + if (strcmp(argv[1], "spawn_helper9") == 0) { + return spawn_stdin_stdout(); + } + + return run_test(argv[1], 0, 1); +} diff --git a/3rdparty/libuv/test/runner-unix.c b/3rdparty/libuv/test/runner-unix.c new file mode 100644 index 00000000000..2264d1e89d5 --- /dev/null +++ b/3rdparty/libuv/test/runner-unix.c @@ -0,0 +1,400 @@ +/* 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 "runner-unix.h" +#include "runner.h" + +#include <limits.h> +#include <stdint.h> /* uintptr_t */ + +#include <errno.h> +#include <unistd.h> /* readlink, usleep */ +#include <string.h> /* strdup */ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <assert.h> + +#include <sys/select.h> +#include <sys/time.h> +#include <pthread.h> + + +/* Do platform-specific initialization. */ +int platform_init(int argc, char **argv) { + const char* tap; + + tap = getenv("UV_TAP_OUTPUT"); + tap_output = (tap != NULL && atoi(tap) > 0); + + /* Disable stdio output buffering. */ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + signal(SIGPIPE, SIG_IGN); + + if (realpath(argv[0], executable_path) == NULL) { + perror("realpath"); + return -1; + } + + return 0; +} + + +/* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ +/* Make sure that all stdio output of the processes is buffered up. */ +int process_start(char* name, char* part, process_info_t* p, int is_helper) { + FILE* stdout_file; + const char* arg; + char* args[16]; + int n; + pid_t pid; + + stdout_file = tmpfile(); + if (!stdout_file) { + perror("tmpfile"); + return -1; + } + + p->terminated = 0; + p->status = 0; + + pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + + if (pid == 0) { + /* child */ + arg = getenv("UV_USE_VALGRIND"); + n = 0; + + /* Disable valgrind for helpers, it complains about helpers leaking memory. + * They're killed after the test and as such never get a chance to clean up. + */ + if (is_helper == 0 && arg != NULL && atoi(arg) != 0) { + args[n++] = "valgrind"; + args[n++] = "--quiet"; + args[n++] = "--leak-check=full"; + args[n++] = "--show-reachable=yes"; + args[n++] = "--error-exitcode=125"; + } + + args[n++] = executable_path; + args[n++] = name; + args[n++] = part; + args[n++] = NULL; + + dup2(fileno(stdout_file), STDOUT_FILENO); + dup2(fileno(stdout_file), STDERR_FILENO); + execvp(args[0], args); + perror("execvp()"); + _exit(127); + } + + /* parent */ + p->pid = pid; + p->name = strdup(name); + p->stdout_file = stdout_file; + + return 0; +} + + +typedef struct { + int pipe[2]; + process_info_t* vec; + int n; +} dowait_args; + + +/* This function is run inside a pthread. We do this so that we can possibly + * timeout. + */ +static void* dowait(void* data) { + dowait_args* args = data; + + int i, r; + process_info_t* p; + + for (i = 0; i < args->n; i++) { + p = (process_info_t*)(args->vec + i * sizeof(process_info_t)); + if (p->terminated) continue; + r = waitpid(p->pid, &p->status, 0); + if (r < 0) { + perror("waitpid"); + return NULL; + } + p->terminated = 1; + } + + if (args->pipe[1] >= 0) { + /* Write a character to the main thread to notify it about this. */ + ssize_t r; + + do + r = write(args->pipe[1], "", 1); + while (r == -1 && errno == EINTR); + } + + return NULL; +} + + +/* Wait for all `n` processes in `vec` to terminate. */ +/* Time out after `timeout` msec, or never if timeout == -1 */ +/* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */ +int process_wait(process_info_t* vec, int n, int timeout) { + int i; + int r; + int retval; + process_info_t* p; + dowait_args args; + pthread_t tid; + pthread_attr_t attr; + unsigned int elapsed_ms; + struct timeval timebase; + struct timeval tv; + fd_set fds; + + args.vec = vec; + args.n = n; + args.pipe[0] = -1; + args.pipe[1] = -1; + + /* The simple case is where there is no timeout */ + if (timeout == -1) { + dowait(&args); + return 0; + } + + /* Hard case. Do the wait with a timeout. + * + * Assumption: we are the only ones making this call right now. Otherwise + * we'd need to lock vec. + */ + + r = pipe((int*)&(args.pipe)); + if (r) { + perror("pipe()"); + return -1; + } + + if (pthread_attr_init(&attr)) + abort(); + + if (pthread_attr_setstacksize(&attr, 256 * 1024)) + abort(); + + r = pthread_create(&tid, &attr, dowait, &args); + + if (pthread_attr_destroy(&attr)) + abort(); + + if (r) { + perror("pthread_create()"); + retval = -1; + goto terminate; + } + + if (gettimeofday(&timebase, NULL)) + abort(); + + tv = timebase; + for (;;) { + /* Check that gettimeofday() doesn't jump back in time. */ + assert(tv.tv_sec == timebase.tv_sec || + (tv.tv_sec == timebase.tv_sec && tv.tv_usec >= timebase.tv_usec)); + + elapsed_ms = + (tv.tv_sec - timebase.tv_sec) * 1000 + + (tv.tv_usec / 1000) - + (timebase.tv_usec / 1000); + + r = 0; /* Timeout. */ + if (elapsed_ms >= (unsigned) timeout) + break; + + tv.tv_sec = (timeout - elapsed_ms) / 1000; + tv.tv_usec = (timeout - elapsed_ms) % 1000 * 1000; + + FD_ZERO(&fds); + FD_SET(args.pipe[0], &fds); + + r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv); + if (!(r == -1 && errno == EINTR)) + break; + + if (gettimeofday(&tv, NULL)) + abort(); + } + + if (r == -1) { + perror("select()"); + retval = -1; + + } else if (r) { + /* The thread completed successfully. */ + retval = 0; + + } else { + /* Timeout. Kill all the children. */ + for (i = 0; i < n; i++) { + p = (process_info_t*)(vec + i * sizeof(process_info_t)); + kill(p->pid, SIGTERM); + } + retval = -2; + } + + if (pthread_join(tid, NULL)) + abort(); + +terminate: + close(args.pipe[0]); + close(args.pipe[1]); + return retval; +} + + +/* Returns the number of bytes in the stdio output buffer for process `p`. */ +long int process_output_size(process_info_t *p) { + /* Size of the p->stdout_file */ + struct stat buf; + + int r = fstat(fileno(p->stdout_file), &buf); + if (r < 0) { + return -1; + } + + return (long)buf.st_size; +} + + +/* Copy the contents of the stdio output buffer to `fd`. */ +int process_copy_output(process_info_t *p, int fd) { + ssize_t nwritten; + char buf[1024]; + int r; + + r = fseek(p->stdout_file, 0, SEEK_SET); + if (r < 0) { + perror("fseek"); + return -1; + } + + /* TODO: what if the line is longer than buf */ + while (fgets(buf, sizeof(buf), p->stdout_file) != NULL) { + /* TODO: what if write doesn't write the whole buffer... */ + nwritten = 0; + + if (tap_output) + nwritten += write(fd, "#", 1); + + nwritten += write(fd, buf, strlen(buf)); + + if (nwritten < 0) { + perror("write"); + return -1; + } + } + + if (ferror(p->stdout_file)) { + perror("read"); + return -1; + } + + return 0; +} + + +/* Copy the last line of the stdio output buffer to `buffer` */ +int process_read_last_line(process_info_t *p, + char* buffer, + size_t buffer_len) { + char* ptr; + + int r = fseek(p->stdout_file, 0, SEEK_SET); + if (r < 0) { + perror("fseek"); + return -1; + } + + buffer[0] = '\0'; + + while (fgets(buffer, buffer_len, p->stdout_file) != NULL) { + for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++); + *ptr = '\0'; + } + + if (ferror(p->stdout_file)) { + perror("read"); + buffer[0] = '\0'; + return -1; + } + return 0; +} + + +/* Return the name that was specified when `p` was started by process_start */ +char* process_get_name(process_info_t *p) { + return p->name; +} + + +/* Terminate process `p`. */ +int process_terminate(process_info_t *p) { + return kill(p->pid, SIGTERM); +} + + +/* Return the exit code of process p. */ +/* On error, return -1. */ +int process_reap(process_info_t *p) { + if (WIFEXITED(p->status)) { + return WEXITSTATUS(p->status); + } else { + return p->status; /* ? */ + } +} + + +/* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ +void process_cleanup(process_info_t *p) { + fclose(p->stdout_file); + free(p->name); +} + + +/* Move the console cursor one line up and back to the first column. */ +void rewind_cursor(void) { + fprintf(stderr, "\033[2K\r"); +} + + +/* Pause the calling thread for a number of milliseconds. */ +void uv_sleep(int msec) { + usleep(msec * 1000); +} diff --git a/3rdparty/libuv/test/runner-unix.h b/3rdparty/libuv/test/runner-unix.h new file mode 100644 index 00000000000..e21847f92c0 --- /dev/null +++ b/3rdparty/libuv/test/runner-unix.h @@ -0,0 +1,36 @@ +/* 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. + */ + +#ifndef TEST_RUNNER_UNIX_H +#define TEST_RUNNER_UNIX_H + +#include <sys/types.h> +#include <stdio.h> /* FILE */ + +typedef struct { + FILE* stdout_file; + pid_t pid; + char* name; + int status; + int terminated; +} process_info_t; + +#endif /* TEST_RUNNER_UNIX_H */ diff --git a/3rdparty/libuv/test/runner-win.c b/3rdparty/libuv/test/runner-win.c new file mode 100644 index 00000000000..97ef7599eb8 --- /dev/null +++ b/3rdparty/libuv/test/runner-win.c @@ -0,0 +1,371 @@ +/* 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 <fcntl.h> +#include <io.h> +#include <malloc.h> +#include <stdio.h> +#include <process.h> +#if !defined(__MINGW32__) +# include <crtdbg.h> +#endif + + +#include "task.h" +#include "runner.h" + + +/* + * Define the stuff that MinGW doesn't have + */ +#ifndef GetFileSizeEx + WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, + PLARGE_INTEGER lpFileSize); +#endif + + +/* Do platform-specific initialization. */ +int platform_init(int argc, char **argv) { + const char* tap; + + tap = getenv("UV_TAP_OUTPUT"); + tap_output = (tap != NULL && atoi(tap) > 0); + + /* Disable the "application crashed" popup. */ + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | + SEM_NOOPENFILEERRORBOX); +#if !defined(__MINGW32__) + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); + _setmode(2, _O_BINARY); + + /* Disable stdio output buffering. */ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + strcpy(executable_path, argv[0]); + + return 0; +} + + +int process_start(char *name, char *part, process_info_t *p, int is_helper) { + HANDLE file = INVALID_HANDLE_VALUE; + HANDLE nul = INVALID_HANDLE_VALUE; + WCHAR path[MAX_PATH], filename[MAX_PATH]; + WCHAR image[MAX_PATH + 1]; + WCHAR args[MAX_PATH * 2]; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + DWORD result; + + if (GetTempPathW(sizeof(path) / sizeof(WCHAR), (WCHAR*)&path) == 0) + goto error; + if (GetTempFileNameW((WCHAR*)&path, L"uv", 0, (WCHAR*)&filename) == 0) + goto error; + + file = CreateFileW((WCHAR*)filename, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (file == INVALID_HANDLE_VALUE) + goto error; + + if (!SetHandleInformation(file, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) + goto error; + + nul = CreateFileA("nul", + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (nul == INVALID_HANDLE_VALUE) + goto error; + + if (!SetHandleInformation(nul, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) + goto error; + + result = GetModuleFileNameW(NULL, + (WCHAR*) &image, + sizeof(image) / sizeof(WCHAR)); + if (result == 0 || result == sizeof(image)) + goto error; + + if (part) { + if (_snwprintf((WCHAR*)args, + sizeof(args) / sizeof(WCHAR), + L"\"%s\" %S %S", + image, + name, + part) < 0) { + goto error; + } + } else { + if (_snwprintf((WCHAR*)args, + sizeof(args) / sizeof(WCHAR), + L"\"%s\" %S", + image, + name) < 0) { + goto error; + } + } + + memset((void*)&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = nul; + si.hStdOutput = file; + si.hStdError = file; + + if (!CreateProcessW(image, args, NULL, NULL, TRUE, + 0, NULL, NULL, &si, &pi)) + goto error; + + CloseHandle(pi.hThread); + + SetHandleInformation(nul, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(file, HANDLE_FLAG_INHERIT, 0); + + p->stdio_in = nul; + p->stdio_out = file; + p->process = pi.hProcess; + p->name = part; + + return 0; + +error: + if (file != INVALID_HANDLE_VALUE) + CloseHandle(file); + if (nul != INVALID_HANDLE_VALUE) + CloseHandle(nul); + + return -1; +} + + +/* Timeout is is msecs. Set timeout < 0 to never time out. */ +/* Returns 0 when all processes are terminated, -2 on timeout. */ +int process_wait(process_info_t *vec, int n, int timeout) { + int i; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + DWORD timeout_api, result; + + /* If there's nothing to wait for, return immediately. */ + if (n == 0) + return 0; + + ASSERT(n <= MAXIMUM_WAIT_OBJECTS); + + for (i = 0; i < n; i++) + handles[i] = vec[i].process; + + if (timeout >= 0) { + timeout_api = (DWORD)timeout; + } else { + timeout_api = INFINITE; + } + + result = WaitForMultipleObjects(n, handles, TRUE, timeout_api); + + if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n) { + /* All processes are terminated. */ + return 0; + } + if (result == WAIT_TIMEOUT) { + return -2; + } + return -1; +} + + +long int process_output_size(process_info_t *p) { + LARGE_INTEGER size; + if (!GetFileSizeEx(p->stdio_out, &size)) + return -1; + return (long int)size.QuadPart; +} + + +int process_copy_output(process_info_t *p, int fd) { + DWORD read; + char buf[1024]; + char *line, *start; + + if (SetFilePointer(p->stdio_out, + 0, + 0, + FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + return -1; + } + + if (tap_output) + write(fd, "#", 1); + + while (ReadFile(p->stdio_out, (void*)&buf, sizeof(buf), &read, NULL) && + read > 0) { + if (tap_output) { + start = buf; + + while ((line = strchr(start, '\n')) != NULL) { + write(fd, start, line - start + 1); + write(fd, "#", 1); + start = line + 1; + } + + if (start < buf + read) + write(fd, start, buf + read - start); + } else { + write(fd, buf, read); + } + } + + if (tap_output) + write(fd, "\n", 1); + + if (GetLastError() != ERROR_HANDLE_EOF) + return -1; + + return 0; +} + + +int process_read_last_line(process_info_t *p, + char * buffer, + size_t buffer_len) { + DWORD size; + DWORD read; + DWORD start; + OVERLAPPED overlapped; + + ASSERT(buffer_len > 0); + + size = GetFileSize(p->stdio_out, NULL); + if (size == INVALID_FILE_SIZE) + return -1; + + if (size == 0) { + buffer[0] = '\0'; + return 1; + } + + memset(&overlapped, 0, sizeof overlapped); + if (size >= buffer_len) + overlapped.Offset = size - buffer_len - 1; + + if (!ReadFile(p->stdio_out, buffer, buffer_len - 1, &read, &overlapped)) + return -1; + + for (start = read - 1; start >= 0; start--) { + if (buffer[start] == '\n' || buffer[start] == '\r') + break; + } + + if (start > 0) + memmove(buffer, buffer + start, read - start); + + buffer[read - start] = '\0'; + + return 0; +} + + +char* process_get_name(process_info_t *p) { + return p->name; +} + + +int process_terminate(process_info_t *p) { + if (!TerminateProcess(p->process, 1)) + return -1; + return 0; +} + + +int process_reap(process_info_t *p) { + DWORD exitCode; + if (!GetExitCodeProcess(p->process, &exitCode)) + return -1; + return (int)exitCode; +} + + +void process_cleanup(process_info_t *p) { + CloseHandle(p->process); + CloseHandle(p->stdio_in); + CloseHandle(p->stdio_out); +} + + +static int clear_line() { + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + COORD coord; + DWORD written; + + handle = (HANDLE)_get_osfhandle(fileno(stderr)); + if (handle == INVALID_HANDLE_VALUE) + return -1; + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return -1; + + coord = info.dwCursorPosition; + if (coord.Y <= 0) + return -1; + + coord.X = 0; + + if (!SetConsoleCursorPosition(handle, coord)) + return -1; + + if (!FillConsoleOutputCharacterW(handle, + 0x20, + info.dwSize.X, + coord, + &written)) { + return -1; + } + + return 0; +} + + +void rewind_cursor() { + if (clear_line() == -1) { + /* If clear_line fails (stdout is not a console), print a newline. */ + fprintf(stderr, "\n"); + } +} + + +/* Pause the calling thread for a number of milliseconds. */ +void uv_sleep(int msec) { + Sleep(msec); +} diff --git a/3rdparty/libuv/test/runner-win.h b/3rdparty/libuv/test/runner-win.h new file mode 100644 index 00000000000..8cc4c16eb22 --- /dev/null +++ b/3rdparty/libuv/test/runner-win.h @@ -0,0 +1,39 @@ +/* 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. + */ + +/* Don't complain about write(), fileno() etc. being deprecated. */ +#pragma warning(disable : 4996) + + +#include <winsock2.h> +#include <windows.h> +#include <stdio.h> + +#if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 +extern int snprintf(char*, size_t, const char*, ...); +#endif + +typedef struct { + HANDLE process; + HANDLE stdio_in; + HANDLE stdio_out; + char *name; +} process_info_t; diff --git a/3rdparty/libuv/test/runner.c b/3rdparty/libuv/test/runner.c new file mode 100644 index 00000000000..c616d176445 --- /dev/null +++ b/3rdparty/libuv/test/runner.c @@ -0,0 +1,466 @@ +/* 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 <stdio.h> +#include <string.h> + +#include "runner.h" +#include "task.h" +#include "uv.h" + +char executable_path[sizeof(executable_path)]; + +int tap_output = 0; + + +static void log_progress(int total, + int passed, + int failed, + int todos, + int skipped, + const char* name) { + int progress; + + if (total == 0) + total = 1; + + progress = 100 * (passed + failed + skipped + todos) / total; + fprintf(stderr, "[%% %3d|+ %3d|- %3d|T %3d|S %3d]: %s", + progress, + passed, + failed, + todos, + skipped, + name); + fflush(stderr); +} + + +const char* fmt(double d) { + static char buf[1024]; + static char* p; + uint64_t v; + + if (p == NULL) + p = buf; + + p += 31; + + if (p >= buf + sizeof(buf)) + return "<buffer too small>"; + + v = (uint64_t) d; + +#if 0 /* works but we don't care about fractional precision */ + if (d - v >= 0.01) { + *--p = '0' + (uint64_t) (d * 100) % 10; + *--p = '0' + (uint64_t) (d * 10) % 10; + *--p = '.'; + } +#endif + + if (v == 0) + *--p = '0'; + + while (v) { + if (v) *--p = '0' + (v % 10), v /= 10; + if (v) *--p = '0' + (v % 10), v /= 10; + if (v) *--p = '0' + (v % 10), v /= 10; + if (v) *--p = ','; + } + + return p; +} + + +int run_tests(int benchmark_output) { + int total; + int passed; + int failed; + int todos; + int skipped; + int current; + int test_result; + task_entry_t* task; + + /* Count the number of tests. */ + total = 0; + for (task = TASKS; task->main; task++) { + if (!task->is_helper) { + total++; + } + } + + if (tap_output) { + fprintf(stderr, "1..%d\n", total); + fflush(stderr); + } + + /* Run all tests. */ + passed = 0; + failed = 0; + todos = 0; + skipped = 0; + current = 1; + for (task = TASKS; task->main; task++) { + if (task->is_helper) { + continue; + } + + if (!tap_output) + rewind_cursor(); + + if (!benchmark_output && !tap_output) { + log_progress(total, passed, failed, todos, skipped, task->task_name); + } + + test_result = run_test(task->task_name, benchmark_output, current); + switch (test_result) { + case TEST_OK: passed++; break; + case TEST_TODO: todos++; break; + case TEST_SKIP: skipped++; break; + default: failed++; + } + current++; + } + + if (!tap_output) + rewind_cursor(); + + if (!benchmark_output && !tap_output) { + log_progress(total, passed, failed, todos, skipped, "Done.\n"); + } + + return failed; +} + + +void log_tap_result(int test_count, + const char* test, + int status, + process_info_t* process) { + const char* result; + const char* directive; + char reason[1024]; + + switch (status) { + case TEST_OK: + result = "ok"; + directive = ""; + break; + case TEST_TODO: + result = "not ok"; + directive = " # TODO "; + break; + case TEST_SKIP: + result = "ok"; + directive = " # SKIP "; + break; + default: + result = "not ok"; + directive = ""; + } + + if ((status == TEST_SKIP || status == TEST_TODO) && + process_output_size(process) > 0) { + process_read_last_line(process, reason, sizeof reason); + } else { + reason[0] = '\0'; + } + + fprintf(stderr, "%s %d - %s%s%s\n", result, test_count, test, directive, reason); + fflush(stderr); +} + + +int run_test(const char* test, + int benchmark_output, + int test_count) { + char errmsg[1024] = "no error"; + process_info_t processes[1024]; + process_info_t *main_proc; + task_entry_t* task; + int process_count; + int result; + int status; + int i; + + status = 255; + main_proc = NULL; + process_count = 0; + +#ifndef _WIN32 + /* Clean up stale socket from previous run. */ + remove(TEST_PIPENAME); + remove(TEST_PIPENAME_2); + remove(TEST_PIPENAME_3); +#endif + + /* If it's a helper the user asks for, start it directly. */ + for (task = TASKS; task->main; task++) { + if (task->is_helper && strcmp(test, task->process_name) == 0) { + return task->main(); + } + } + + /* Start the helpers first. */ + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) != 0) { + continue; + } + + /* Skip the test itself. */ + if (!task->is_helper) { + continue; + } + + if (process_start(task->task_name, + task->process_name, + &processes[process_count], + 1 /* is_helper */) == -1) { + snprintf(errmsg, + sizeof errmsg, + "Process `%s` failed to start.", + task->process_name); + goto out; + } + + process_count++; + } + + /* Give the helpers time to settle. Race-y, fix this. */ + uv_sleep(250); + + /* Now start the test itself. */ + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) != 0) { + continue; + } + + if (task->is_helper) { + continue; + } + + if (process_start(task->task_name, + task->process_name, + &processes[process_count], + 0 /* !is_helper */) == -1) { + snprintf(errmsg, + sizeof errmsg, + "Process `%s` failed to start.", + task->process_name); + goto out; + } + + main_proc = &processes[process_count]; + process_count++; + break; + } + + if (main_proc == NULL) { + snprintf(errmsg, + sizeof errmsg, + "No test with that name: %s", + test); + goto out; + } + + result = process_wait(main_proc, 1, task->timeout); + if (result == -1) { + FATAL("process_wait failed"); + } else if (result == -2) { + /* Don't have to clean up the process, process_wait() has killed it. */ + snprintf(errmsg, + sizeof errmsg, + "timeout"); + goto out; + } + + status = process_reap(main_proc); + if (status != TEST_OK) { + snprintf(errmsg, + sizeof errmsg, + "exit code %d", + status); + goto out; + } + + if (benchmark_output) { + /* Give the helpers time to clean up their act. */ + uv_sleep(1000); + } + +out: + /* Reap running processes except the main process, it's already dead. */ + for (i = 0; i < process_count - 1; i++) { + process_terminate(&processes[i]); + } + + if (process_count > 0 && + process_wait(processes, process_count - 1, -1) < 0) { + FATAL("process_wait failed"); + } + + if (tap_output) + log_tap_result(test_count, test, status, &processes[i]); + + /* Show error and output from processes if the test failed. */ + if (status != 0 || task->show_output) { + if (tap_output) { + fprintf(stderr, "#"); + } else if (status == TEST_TODO) { + fprintf(stderr, "\n`%s` todo\n", test); + } else if (status == TEST_SKIP) { + fprintf(stderr, "\n`%s` skipped\n", test); + } else if (status != 0) { + fprintf(stderr, "\n`%s` failed: %s\n", test, errmsg); + } else { + fprintf(stderr, "\n"); + } + fflush(stderr); + + for (i = 0; i < process_count; i++) { + switch (process_output_size(&processes[i])) { + case -1: + fprintf(stderr, "Output from process `%s`: (unavailable)\n", + process_get_name(&processes[i])); + fflush(stderr); + break; + + case 0: + fprintf(stderr, "Output from process `%s`: (no output)\n", + process_get_name(&processes[i])); + fflush(stderr); + break; + + default: + fprintf(stderr, "Output from process `%s`:\n", process_get_name(&processes[i])); + fflush(stderr); + process_copy_output(&processes[i], fileno(stderr)); + break; + } + } + + if (!tap_output) { + fprintf(stderr, "=============================================================\n"); + } + + /* In benchmark mode show concise output from the main process. */ + } else if (benchmark_output) { + switch (process_output_size(main_proc)) { + case -1: + fprintf(stderr, "%s: (unavailable)\n", test); + fflush(stderr); + break; + + case 0: + fprintf(stderr, "%s: (no output)\n", test); + fflush(stderr); + break; + + default: + for (i = 0; i < process_count; i++) { + process_copy_output(&processes[i], fileno(stderr)); + } + break; + } + } + + /* Clean up all process handles. */ + for (i = 0; i < process_count; i++) { + process_cleanup(&processes[i]); + } + + return status; +} + + +/* Returns the status code of the task part + * or 255 if no matching task was not found. + */ +int run_test_part(const char* test, const char* part) { + task_entry_t* task; + int r; + + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) == 0 && + strcmp(part, task->process_name) == 0) { + r = task->main(); + return r; + } + } + + fprintf(stderr, "No test part with that name: %s:%s\n", test, part); + fflush(stderr); + return 255; +} + + +static int compare_task(const void* va, const void* vb) { + const task_entry_t* a = va; + const task_entry_t* b = vb; + return strcmp(a->task_name, b->task_name); +} + + +static int find_helpers(const task_entry_t* task, + const task_entry_t** helpers) { + const task_entry_t* helper; + int n_helpers; + + for (n_helpers = 0, helper = TASKS; helper->main; helper++) { + if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) { + *helpers++ = helper; + n_helpers++; + } + } + + return n_helpers; +} + + +void print_tests(FILE* stream) { + const task_entry_t* helpers[1024]; + const task_entry_t* task; + int n_helpers; + int n_tasks; + int i; + + for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++); + qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task); + + for (task = TASKS; task->main; task++) { + if (task->is_helper) { + continue; + } + + n_helpers = find_helpers(task, helpers); + if (n_helpers) { + printf("%-25s (helpers:", task->task_name); + for (i = 0; i < n_helpers; i++) { + printf(" %s", helpers[i]->process_name); + } + printf(")\n"); + } else { + printf("%s\n", task->task_name); + } + } +} diff --git a/3rdparty/libuv/test/runner.h b/3rdparty/libuv/test/runner.h new file mode 100644 index 00000000000..78f3c880a98 --- /dev/null +++ b/3rdparty/libuv/test/runner.h @@ -0,0 +1,178 @@ +/* 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. + */ + +#ifndef RUNNER_H_ +#define RUNNER_H_ + +#include <limits.h> /* PATH_MAX */ +#include <stdio.h> /* FILE */ + + +/* + * The maximum number of processes (main + helpers) that a test / benchmark + * can have. + */ +#define MAX_PROCESSES 8 + + +/* + * Struct to store both tests and to define helper processes for tasks. + */ +typedef struct { + char *task_name; + char *process_name; + int (*main)(void); + int is_helper; + int show_output; + + /* + * The time in milliseconds after which a single test or benchmark times out. + */ + int timeout; +} task_entry_t, bench_entry_t; + + +/* + * Macros used by test-list.h and benchmark-list.h. + */ +#define TASK_LIST_START \ + task_entry_t TASKS[] = { + +#define TASK_LIST_END \ + { 0, 0, 0, 0, 0, 0 } \ + }; + +#define TEST_DECLARE(name) \ + int run_test_##name(void); + +#define TEST_ENTRY(name) \ + { #name, #name, &run_test_##name, 0, 0, 5000 }, + +#define TEST_ENTRY_CUSTOM(name, is_helper, show_output, timeout) \ + { #name, #name, &run_test_##name, is_helper, show_output, timeout }, + +#define BENCHMARK_DECLARE(name) \ + int run_benchmark_##name(void); + +#define BENCHMARK_ENTRY(name) \ + { #name, #name, &run_benchmark_##name, 0, 0, 60000 }, + +#define HELPER_DECLARE(name) \ + int run_helper_##name(void); + +#define HELPER_ENTRY(task_name, name) \ + { #task_name, #name, &run_helper_##name, 1, 0, 0 }, + +#define TEST_HELPER HELPER_ENTRY +#define BENCHMARK_HELPER HELPER_ENTRY + +#ifdef PATH_MAX +extern char executable_path[PATH_MAX]; +#else +extern char executable_path[4096]; +#endif + +/* + * Include platform-dependent definitions + */ +#ifdef _WIN32 +# include "runner-win.h" +#else +# include "runner-unix.h" +#endif + + +/* The array that is filled by test-list.h or benchmark-list.h */ +extern task_entry_t TASKS[]; + +/* + * Run all tests. + */ +int run_tests(int benchmark_output); + +/* + * Run a single test. Starts up any helpers. + */ +int run_test(const char* test, + int benchmark_output, + int test_count); + +/* + * Run a test part, i.e. the test or one of its helpers. + */ +int run_test_part(const char* test, const char* part); + + +/* + * Print tests in sorted order to `stream`. Used by `./run-tests --list`. + */ +void print_tests(FILE* stream); + + +/* + * Stuff that should be implemented by test-runner-<platform>.h + * All functions return 0 on success, -1 on failure, unless specified + * otherwise. + */ + +/* Do platform-specific initialization. */ +int platform_init(int argc, char** argv); + +/* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ +/* Make sure that all stdio output of the processes is buffered up. */ +int process_start(char *name, char* part, process_info_t *p, int is_helper); + +/* Wait for all `n` processes in `vec` to terminate. */ +/* Time out after `timeout` msec, or never if timeout == -1 */ +/* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */ +int process_wait(process_info_t *vec, int n, int timeout); + +/* Returns the number of bytes in the stdio output buffer for process `p`. */ +long int process_output_size(process_info_t *p); + +/* Copy the contents of the stdio output buffer to `fd`. */ +int process_copy_output(process_info_t *p, int fd); + +/* Copy the last line of the stdio output buffer to `buffer` */ +int process_read_last_line(process_info_t *p, + char * buffer, + size_t buffer_len); + +/* Return the name that was specified when `p` was started by process_start */ +char* process_get_name(process_info_t *p); + +/* Terminate process `p`. */ +int process_terminate(process_info_t *p); + +/* Return the exit code of process p. */ +/* On error, return -1. */ +int process_reap(process_info_t *p); + +/* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ +void process_cleanup(process_info_t *p); + +/* Move the console cursor one line up and back to the first column. */ +void rewind_cursor(void); + +/* trigger output as tap */ +extern int tap_output; + +#endif /* RUNNER_H_ */ diff --git a/3rdparty/libuv/test/task.h b/3rdparty/libuv/test/task.h new file mode 100644 index 00000000000..d18c1daa364 --- /dev/null +++ b/3rdparty/libuv/test/task.h @@ -0,0 +1,220 @@ +/* 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. + */ + +#ifndef TASK_H_ +#define TASK_H_ + +#include "uv.h" + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> + +#if defined(_MSC_VER) && _MSC_VER < 1600 +# include "stdint-msvc2008.h" +#else +# include <stdint.h> +#endif + +#if !defined(_WIN32) +# include <sys/time.h> +# include <sys/resource.h> /* setrlimit() */ +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wvariadic-macros" +# pragma clang diagnostic ignored "-Wc99-extensions" +#endif + +#define TEST_PORT 9123 +#define TEST_PORT_2 9124 + +#ifdef _WIN32 +# define TEST_PIPENAME "\\\\?\\pipe\\uv-test" +# define TEST_PIPENAME_2 "\\\\?\\pipe\\uv-test2" +# define TEST_PIPENAME_3 "\\\\?\\pipe\\uv-test3" +#else +# define TEST_PIPENAME "/tmp/uv-test-sock" +# define TEST_PIPENAME_2 "/tmp/uv-test-sock2" +# define TEST_PIPENAME_3 "/tmp/uv-test-sock3" +#endif + +#ifdef _WIN32 +# include <io.h> +# ifndef S_IRUSR +# define S_IRUSR _S_IREAD +# endif +# ifndef S_IWUSR +# define S_IWUSR _S_IWRITE +# endif +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) + +typedef enum { + TCP = 0, + UDP, + PIPE +} stream_type; + +/* Die with fatal error. */ +#define FATAL(msg) \ + do { \ + fprintf(stderr, \ + "Fatal error in %s on line %d: %s\n", \ + __FILE__, \ + __LINE__, \ + msg); \ + fflush(stderr); \ + abort(); \ + } while (0) + +/* Have our own assert, so we are sure it does not get optimized away in + * a release build. + */ +#define ASSERT(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: %s\n", \ + __FILE__, \ + __LINE__, \ + #expr); \ + abort(); \ + } \ + } while (0) + +/* This macro cleans up the main loop. This is used to avoid valgrind + * warnings about memory being "leaked" by the main event loop. + */ +#define MAKE_VALGRIND_HAPPY() \ + do { \ + close_loop(uv_default_loop()); \ + uv_loop_delete(uv_default_loop()); \ + } while (0) + +/* Just sugar for wrapping the main() for a task or helper. */ +#define TEST_IMPL(name) \ + int run_test_##name(void); \ + int run_test_##name(void) + +#define BENCHMARK_IMPL(name) \ + int run_benchmark_##name(void); \ + int run_benchmark_##name(void) + +#define HELPER_IMPL(name) \ + int run_helper_##name(void); \ + int run_helper_##name(void) + +/* Pause the calling thread for a number of milliseconds. */ +void uv_sleep(int msec); + +/* Format big numbers nicely. WARNING: leaks memory. */ +const char* fmt(double d); + +/* Reserved test exit codes. */ +enum test_status { + TEST_OK = 0, + TEST_TODO, + TEST_SKIP +}; + +#define RETURN_OK() \ + do { \ + return TEST_OK; \ + } while (0) + +#define RETURN_TODO(explanation) \ + do { \ + fprintf(stderr, "%s\n", explanation); \ + fflush(stderr); \ + return TEST_TODO; \ + } while (0) + +#define RETURN_SKIP(explanation) \ + do { \ + fprintf(stderr, "%s\n", explanation); \ + fflush(stderr); \ + return TEST_SKIP; \ + } while (0) + +#if !defined(_WIN32) + +# define TEST_FILE_LIMIT(num) \ + do { \ + struct rlimit lim; \ + lim.rlim_cur = (num); \ + lim.rlim_max = lim.rlim_cur; \ + if (setrlimit(RLIMIT_NOFILE, &lim)) \ + RETURN_SKIP("File descriptor limit too low."); \ + } while (0) + +#else /* defined(_WIN32) */ + +# define TEST_FILE_LIMIT(num) do {} while (0) + +#endif + +#if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 +extern int snprintf(char*, size_t, const char*, ...); +#endif + +#if defined(__clang__) || \ + defined(__GNUC__) || \ + defined(__INTEL_COMPILER) || \ + defined(__SUNPRO_C) +# define UNUSED __attribute__((unused)) +#else +# define UNUSED +#endif + +/* Fully close a loop */ +static void close_walk_cb(uv_handle_t* handle, void* arg) { + if (!uv_is_closing(handle)) + uv_close(handle, NULL); +} + +UNUSED static void close_loop(uv_loop_t* loop) { + uv_walk(loop, close_walk_cb, NULL); + uv_run(loop, UV_RUN_DEFAULT); +} + +UNUSED static int can_ipv6(void) { + uv_interface_address_t* addr; + int supported; + int count; + int i; + + if (uv_interface_addresses(&addr, &count)) + return 1; /* Assume IPv6 support on failure. */ + + supported = 0; + for (i = 0; supported == 0 && i < count; i += 1) + supported = (AF_INET6 == addr[i].address.address6.sin6_family); + + uv_free_interface_addresses(addr, count); + return supported; +} + +#endif /* TASK_H_ */ diff --git a/3rdparty/libuv/test/test-active.c b/3rdparty/libuv/test/test-active.c new file mode 100644 index 00000000000..b17bd176018 --- /dev/null +++ b/3rdparty/libuv/test/test-active.c @@ -0,0 +1,84 @@ +/* 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 close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(0 && "timer_cb should not have been called"); +} + + +TEST_IMPL(active) { + int r; + uv_timer_t timer; + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + /* uv_is_active() and uv_is_closing() should always return either 0 or 1. */ + ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + + r = uv_timer_start(&timer, timer_cb, 1000, 0); + ASSERT(r == 0); + + ASSERT(1 == uv_is_active((uv_handle_t*) &timer)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + + r = uv_timer_stop(&timer); + ASSERT(r == 0); + + ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + + r = uv_timer_start(&timer, timer_cb, 1000, 0); + ASSERT(r == 0); + + ASSERT(1 == uv_is_active((uv_handle_t*) &timer)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + + uv_close((uv_handle_t*) &timer, close_cb); + + ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); + ASSERT(1 == uv_is_closing((uv_handle_t*) &timer)); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-async-null-cb.c b/3rdparty/libuv/test/test-async-null-cb.c new file mode 100644 index 00000000000..757944a2762 --- /dev/null +++ b/3rdparty/libuv/test/test-async-null-cb.c @@ -0,0 +1,55 @@ +/* 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_async_t async_handle; +static uv_check_t check_handle; +static int check_cb_called; +static uv_thread_t thread; + + +static void thread_cb(void* dummy) { + (void) &dummy; + uv_async_send(&async_handle); +} + + +static void check_cb(uv_check_t* handle) { + ASSERT(check_cb_called == 0); + uv_close((uv_handle_t*) &async_handle, NULL); + uv_close((uv_handle_t*) &check_handle, NULL); + check_cb_called++; +} + + +TEST_IMPL(async_null_cb) { + ASSERT(0 == uv_async_init(uv_default_loop(), &async_handle, NULL)); + ASSERT(0 == uv_check_init(uv_default_loop(), &check_handle)); + ASSERT(0 == uv_check_start(&check_handle, check_cb)); + ASSERT(0 == uv_thread_create(&thread, thread_cb, NULL)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(0 == uv_thread_join(&thread)); + ASSERT(1 == check_cb_called); + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-async.c b/3rdparty/libuv/test/test-async.c new file mode 100644 index 00000000000..6f5351bf158 --- /dev/null +++ b/3rdparty/libuv/test/test-async.c @@ -0,0 +1,134 @@ +/* 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 uv_thread_t thread; +static uv_mutex_t mutex; + +static uv_prepare_t prepare; +static uv_async_t async; + +static volatile int async_cb_called; +static int prepare_cb_called; +static int close_cb_called; + + +static void thread_cb(void *arg) { + int n; + int r; + + for (;;) { + uv_mutex_lock(&mutex); + n = async_cb_called; + uv_mutex_unlock(&mutex); + + if (n == 3) { + break; + } + + r = uv_async_send(&async); + ASSERT(r == 0); + + /* Work around a bug in Valgrind. + * + * Valgrind runs threads not in parallel but sequentially, i.e. one after + * the other. It also doesn't preempt them, instead it depends on threads + * yielding voluntarily by making a syscall. + * + * That never happens here: the pipe that is associated with the async + * handle is written to once but that's too early for Valgrind's scheduler + * to kick in. Afterwards, the thread busy-loops, starving the main thread. + * Therefore, we yield. + * + * This behavior has been observed with Valgrind 3.7.0 and 3.9.0. + */ + uv_sleep(0); + } +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void async_cb(uv_async_t* handle) { + int n; + + ASSERT(handle == &async); + + uv_mutex_lock(&mutex); + n = ++async_cb_called; + uv_mutex_unlock(&mutex); + + if (n == 3) { + uv_close((uv_handle_t*)&async, close_cb); + uv_close((uv_handle_t*)&prepare, close_cb); + } +} + + +static void prepare_cb(uv_prepare_t* handle) { + int r; + + ASSERT(handle == &prepare); + + if (prepare_cb_called++) + return; + + r = uv_thread_create(&thread, thread_cb, NULL); + ASSERT(r == 0); + uv_mutex_unlock(&mutex); +} + + +TEST_IMPL(async) { + int r; + + r = uv_mutex_init(&mutex); + ASSERT(r == 0); + uv_mutex_lock(&mutex); + + r = uv_prepare_init(uv_default_loop(), &prepare); + ASSERT(r == 0); + r = uv_prepare_start(&prepare, prepare_cb); + ASSERT(r == 0); + + r = uv_async_init(uv_default_loop(), &async, async_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(prepare_cb_called > 0); + ASSERT(async_cb_called == 3); + ASSERT(close_cb_called == 2); + + ASSERT(0 == uv_thread_join(&thread)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-barrier.c b/3rdparty/libuv/test/test-barrier.c new file mode 100644 index 00000000000..dfd2dbdef1b --- /dev/null +++ b/3rdparty/libuv/test/test-barrier.c @@ -0,0 +1,106 @@ +/* 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 <string.h> +#include <errno.h> + +typedef struct { + uv_barrier_t barrier; + int delay; + volatile int posted; + int main_barrier_wait_rval; + int worker_barrier_wait_rval; +} worker_config; + + +static void worker(void* arg) { + worker_config* c = arg; + + if (c->delay) + uv_sleep(c->delay); + + c->worker_barrier_wait_rval = uv_barrier_wait(&c->barrier); +} + + +TEST_IMPL(barrier_1) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + + ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_sleep(100); + wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); + + ASSERT(0 == uv_thread_join(&thread)); + uv_barrier_destroy(&wc.barrier); + + ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); + + return 0; +} + + +TEST_IMPL(barrier_2) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); + + ASSERT(0 == uv_thread_join(&thread)); + uv_barrier_destroy(&wc.barrier); + + ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); + + return 0; +} + + +TEST_IMPL(barrier_3) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + + ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); + + ASSERT(0 == uv_thread_join(&thread)); + uv_barrier_destroy(&wc.barrier); + + ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); + + return 0; +} diff --git a/3rdparty/libuv/test/test-callback-order.c b/3rdparty/libuv/test/test-callback-order.c new file mode 100644 index 00000000000..8bc2c4f75de --- /dev/null +++ b/3rdparty/libuv/test/test-callback-order.c @@ -0,0 +1,77 @@ +/* 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 int idle_cb_called; +static int timer_cb_called; + +static uv_idle_t idle_handle; +static uv_timer_t timer_handle; + + +/* idle_cb should run before timer_cb */ +static void idle_cb(uv_idle_t* handle) { + ASSERT(idle_cb_called == 0); + ASSERT(timer_cb_called == 0); + uv_idle_stop(handle); + idle_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(idle_cb_called == 1); + ASSERT(timer_cb_called == 0); + uv_timer_stop(handle); + timer_cb_called++; +} + + +static void next_tick(uv_idle_t* handle) { + uv_loop_t* loop = handle->loop; + uv_idle_stop(handle); + uv_idle_init(loop, &idle_handle); + uv_idle_start(&idle_handle, idle_cb); + uv_timer_init(loop, &timer_handle); + uv_timer_start(&timer_handle, timer_cb, 0, 0); +} + + +TEST_IMPL(callback_order) { + uv_loop_t* loop; + uv_idle_t idle; + + loop = uv_default_loop(); + uv_idle_init(loop, &idle); + uv_idle_start(&idle, next_tick); + + ASSERT(idle_cb_called == 0); + ASSERT(timer_cb_called == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(idle_cb_called == 1); + ASSERT(timer_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-callback-stack.c b/3rdparty/libuv/test/test-callback-stack.c new file mode 100644 index 00000000000..8855c0841b3 --- /dev/null +++ b/3rdparty/libuv/test/test-callback-stack.c @@ -0,0 +1,205 @@ +/* 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. + */ + +/* + * TODO: Add explanation of why we want on_close to be called from fresh + * stack. + */ + +#include "uv.h" +#include "task.h" + + +static const char MESSAGE[] = "Failure is for the weak. Everyone dies alone."; + +static uv_tcp_t client; +static uv_timer_t timer; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; + +static int nested = 0; +static int close_cb_called = 0; +static int connect_cb_called = 0; +static int write_cb_called = 0; +static int timer_cb_called = 0; +static int bytes_received = 0; +static int shutdown_cb_called = 0; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->len = size; + buf->base = malloc(size); + ASSERT(buf->base != NULL); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(nested == 0 && "close_cb must be called from a fresh stack"); + + close_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(status == 0); + ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack"); + + shutdown_cb_called++; +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + ASSERT(nested == 0 && "read_cb must be called from a fresh stack"); + + printf("Read. nread == %d\n", (int)nread); + free(buf->base); + + if (nread == 0) { + return; + + } else if (nread < 0) { + ASSERT(nread == UV_EOF); + + nested++; + uv_close((uv_handle_t*)tcp, close_cb); + nested--; + + return; + } + + bytes_received += nread; + + /* We call shutdown here because when bytes_received == sizeof MESSAGE */ + /* there will be no more data sent nor received, so here it would be */ + /* possible for a backend to to call shutdown_cb immediately and *not* */ + /* from a fresh stack. */ + if (bytes_received == sizeof MESSAGE) { + nested++; + + puts("Shutdown"); + + if (uv_shutdown(&shutdown_req, (uv_stream_t*)tcp, shutdown_cb)) { + FATAL("uv_shutdown failed"); + } + nested--; + } +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer); + ASSERT(nested == 0 && "timer_cb must be called from a fresh stack"); + + puts("Timeout complete. Now read data..."); + + nested++; + if (uv_read_start((uv_stream_t*)&client, alloc_cb, read_cb)) { + FATAL("uv_read_start failed"); + } + nested--; + + timer_cb_called++; + + uv_close((uv_handle_t*)handle, close_cb); +} + + +static void write_cb(uv_write_t* req, int status) { + int r; + + ASSERT(status == 0); + ASSERT(nested == 0 && "write_cb must be called from a fresh stack"); + + puts("Data written. 500ms timeout..."); + + /* After the data has been sent, we're going to wait for a while, then */ + /* start reading. This makes us certain that the message has been echoed */ + /* back to our receive buffer when we start reading. This maximizes the */ + /* temptation for the backend to use dirty stack for calling read_cb. */ + nested++; + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + r = uv_timer_start(&timer, timer_cb, 500, 0); + ASSERT(r == 0); + nested--; + + write_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t buf; + + puts("Connected. Write some data to echo server..."); + + ASSERT(status == 0); + ASSERT(nested == 0 && "connect_cb must be called from a fresh stack"); + + nested++; + + buf.base = (char*) &MESSAGE; + buf.len = sizeof MESSAGE; + + if (uv_write(&write_req, (uv_stream_t*)req->handle, &buf, 1, write_cb)) { + FATAL("uv_write failed"); + } + + nested--; + + connect_cb_called++; +} + + +TEST_IMPL(callback_stack) { + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + if (uv_tcp_init(uv_default_loop(), &client)) { + FATAL("uv_tcp_init failed"); + } + + puts("Connecting..."); + + nested++; + + if (uv_tcp_connect(&connect_req, + &client, + (const struct sockaddr*) &addr, + connect_cb)) { + FATAL("uv_tcp_connect failed"); + } + nested--; + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(nested == 0); + ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once"); + ASSERT(write_cb_called == 1 && "write_cb must be called exactly once"); + ASSERT(timer_cb_called == 1 && "timer_cb must be called exactly once"); + ASSERT(bytes_received == sizeof MESSAGE); + ASSERT(shutdown_cb_called == 1 && "shutdown_cb must be called exactly once"); + ASSERT(close_cb_called == 2 && "close_cb must be called exactly twice"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-close-fd.c b/3rdparty/libuv/test/test-close-fd.c new file mode 100644 index 00000000000..93a7bd7c021 --- /dev/null +++ b/3rdparty/libuv/test/test-close-fd.c @@ -0,0 +1,76 @@ +/* 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. + */ + +#if !defined(_WIN32) + +#include "uv.h" +#include "task.h" +#include <fcntl.h> +#include <unistd.h> + +static unsigned int read_cb_called; + +static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { + static char slab[1]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { + switch (++read_cb_called) { + case 1: + ASSERT(nread == 1); + uv_read_stop(handle); + break; + case 2: + ASSERT(nread == UV_EOF); + uv_close((uv_handle_t *) handle, NULL); + break; + default: + ASSERT(!"read_cb_called > 2"); + } +} + +TEST_IMPL(close_fd) { + uv_pipe_t pipe_handle; + int fd[2]; + + ASSERT(0 == pipe(fd)); + ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */ + ASSERT(1 == write(fd[1], "", 1)); + ASSERT(0 == close(fd[1])); + fd[1] = -1; + ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(1 == read_cb_called); + ASSERT(0 == uv_is_active((const uv_handle_t *) &pipe_handle)); + ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(2 == read_cb_called); + ASSERT(0 != uv_is_closing((const uv_handle_t *) &pipe_handle)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* !defined(_WIN32) */ diff --git a/3rdparty/libuv/test/test-close-order.c b/3rdparty/libuv/test/test-close-order.c new file mode 100644 index 00000000000..2b24f6d6579 --- /dev/null +++ b/3rdparty/libuv/test/test-close-order.c @@ -0,0 +1,80 @@ +/* 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 int check_cb_called; +static int timer_cb_called; +static int close_cb_called; + +static uv_check_t check_handle; +static uv_timer_t timer_handle1; +static uv_timer_t timer_handle2; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +/* check_cb should run before any close_cb */ +static void check_cb(uv_check_t* handle) { + ASSERT(check_cb_called == 0); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 0); + uv_close((uv_handle_t*) handle, close_cb); + uv_close((uv_handle_t*) &timer_handle2, close_cb); + check_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + uv_close((uv_handle_t*) handle, close_cb); + timer_cb_called++; +} + + +TEST_IMPL(close_order) { + uv_loop_t* loop; + loop = uv_default_loop(); + + uv_check_init(loop, &check_handle); + uv_check_start(&check_handle, check_cb); + uv_timer_init(loop, &timer_handle1); + uv_timer_start(&timer_handle1, timer_cb, 0, 0); + uv_timer_init(loop, &timer_handle2); + uv_timer_start(&timer_handle2, timer_cb, 100000, 0); + + ASSERT(check_cb_called == 0); + ASSERT(close_cb_called == 0); + ASSERT(timer_cb_called == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(check_cb_called == 1); + ASSERT(close_cb_called == 3); + ASSERT(timer_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-condvar.c b/3rdparty/libuv/test/test-condvar.c new file mode 100644 index 00000000000..dbacdba384d --- /dev/null +++ b/3rdparty/libuv/test/test-condvar.c @@ -0,0 +1,173 @@ +/* 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 <string.h> +#include <errno.h> + +typedef struct { + uv_mutex_t mutex; + uv_cond_t cond; + int delay; + int use_broadcast; + volatile int posted; +} worker_config; + + +static void worker(void* arg) { + worker_config* c = arg; + + if (c->delay) + uv_sleep(c->delay); + + uv_mutex_lock(&c->mutex); + ASSERT(c->posted == 0); + c->posted = 1; + if (c->use_broadcast) + uv_cond_broadcast(&c->cond); + else + uv_cond_signal(&c->cond); + uv_mutex_unlock(&c->mutex); +} + + +TEST_IMPL(condvar_1) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + uv_sleep(100); + uv_cond_wait(&wc.cond, &wc.mutex); + ASSERT(wc.posted == 1); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_2) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + uv_cond_wait(&wc.cond, &wc.mutex); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_3) { + uv_thread_t thread; + worker_config wc; + int r; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(50 * 1e6)); + ASSERT(r == UV_ETIMEDOUT); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_4) { + uv_thread_t thread; + worker_config wc; + int r; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(150 * 1e6)); + ASSERT(r == 0); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_5) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.use_broadcast = 1; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + uv_sleep(100); + uv_cond_wait(&wc.cond, &wc.mutex); + ASSERT(wc.posted == 1); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} diff --git a/3rdparty/libuv/test/test-connection-fail.c b/3rdparty/libuv/test/test-connection-fail.c new file mode 100644 index 00000000000..328bff46e7d --- /dev/null +++ b/3rdparty/libuv/test/test-connection-fail.c @@ -0,0 +1,151 @@ +/* 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 <stdlib.h> +#include <stdio.h> + + +static uv_tcp_t tcp; +static uv_connect_t req; +static int connect_cb_calls; +static int close_cb_calls; + +static uv_timer_t timer; +static int timer_close_cb_calls; +static int timer_cb_calls; + + +static void on_close(uv_handle_t* handle) { + close_cb_calls++; +} + + +static void timer_close_cb(uv_handle_t* handle) { + timer_close_cb_calls++; +} + + +static void timer_cb(uv_timer_t* handle) { + timer_cb_calls++; + + /* + * These are the important asserts. The connection callback has been made, + * but libuv hasn't automatically closed the socket. The user must + * uv_close the handle manually. + */ + ASSERT(close_cb_calls == 0); + ASSERT(connect_cb_calls == 1); + + /* Close the tcp handle. */ + uv_close((uv_handle_t*)&tcp, on_close); + + /* Close the timer. */ + uv_close((uv_handle_t*)handle, timer_close_cb); +} + + +static void on_connect_with_close(uv_connect_t *req, int status) { + ASSERT((uv_stream_t*) &tcp == req->handle); + ASSERT(status == UV_ECONNREFUSED); + connect_cb_calls++; + + ASSERT(close_cb_calls == 0); + uv_close((uv_handle_t*)req->handle, on_close); +} + + +static void on_connect_without_close(uv_connect_t *req, int status) { + ASSERT(status == UV_ECONNREFUSED); + connect_cb_calls++; + + uv_timer_start(&timer, timer_cb, 100, 0); + + ASSERT(close_cb_calls == 0); +} + + +static void connection_fail(uv_connect_cb connect_cb) { + struct sockaddr_in client_addr, server_addr; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); + + /* There should be no servers listening on this port. */ + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + + /* Try to connect to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(uv_default_loop(), &tcp); + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + ASSERT(0 == uv_tcp_bind(&tcp, (const struct sockaddr*) &client_addr, 0)); + + r = uv_tcp_connect(&req, + &tcp, + (const struct sockaddr*) &server_addr, + connect_cb); + ASSERT(!r); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(connect_cb_calls == 1); + ASSERT(close_cb_calls == 1); +} + + +/* + * This test attempts to connect to a port where no server is running. We + * expect an error. + */ +TEST_IMPL(connection_fail) { + connection_fail(on_connect_with_close); + + ASSERT(timer_close_cb_calls == 0); + ASSERT(timer_cb_calls == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +/* + * This test is the same as the first except it check that the close + * callback of the tcp handle hasn't been made after the failed connection + * attempt. + */ +TEST_IMPL(connection_fail_doesnt_auto_close) { + int r; + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + connection_fail(on_connect_without_close); + + ASSERT(timer_close_cb_calls == 1); + ASSERT(timer_cb_calls == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-cwd-and-chdir.c b/3rdparty/libuv/test/test-cwd-and-chdir.c new file mode 100644 index 00000000000..1e95043c177 --- /dev/null +++ b/3rdparty/libuv/test/test-cwd-and-chdir.c @@ -0,0 +1,51 @@ +/* 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 <string.h> + +#define PATHMAX 1024 +extern char executable_path[]; + +TEST_IMPL(cwd_and_chdir) { + char buffer_orig[PATHMAX]; + char buffer_new[PATHMAX]; + size_t size1; + size_t size2; + int err; + + size1 = sizeof buffer_orig; + err = uv_cwd(buffer_orig, &size1); + ASSERT(err == 0); + + err = uv_chdir(buffer_orig); + ASSERT(err == 0); + + size2 = sizeof buffer_new; + err = uv_cwd(buffer_new, &size2); + ASSERT(err == 0); + + ASSERT(size1 == size2); + ASSERT(strcmp(buffer_orig, buffer_new) == 0); + + return 0; +} diff --git a/3rdparty/libuv/test/test-default-loop-close.c b/3rdparty/libuv/test/test-default-loop-close.c new file mode 100644 index 00000000000..fd11cfa8c12 --- /dev/null +++ b/3rdparty/libuv/test/test-default-loop-close.c @@ -0,0 +1,59 @@ +/* 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 int timer_cb_called; + + +static void timer_cb(uv_timer_t* timer) { + timer_cb_called++; + uv_close((uv_handle_t*) timer, NULL); +} + + +TEST_IMPL(default_loop_close) { + uv_loop_t* loop; + uv_timer_t timer_handle; + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(1 == timer_cb_called); + ASSERT(0 == uv_loop_close(loop)); + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(2 == timer_cb_called); + ASSERT(0 == uv_loop_close(loop)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-delayed-accept.c b/3rdparty/libuv/test/test-delayed-accept.c new file mode 100644 index 00000000000..4a7998909c3 --- /dev/null +++ b/3rdparty/libuv/test/test-delayed-accept.c @@ -0,0 +1,189 @@ +/* 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 connection_cb_called = 0; +static int do_accept_called = 0; +static int close_cb_called = 0; +static int connect_cb_called = 0; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + + free(handle); + + close_cb_called++; +} + + +static void do_accept(uv_timer_t* timer_handle) { + uv_tcp_t* server; + uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); + int r; + + ASSERT(timer_handle != NULL); + ASSERT(accepted_handle != NULL); + + r = uv_tcp_init(uv_default_loop(), accepted_handle); + ASSERT(r == 0); + + server = (uv_tcp_t*)timer_handle->data; + r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle); + ASSERT(r == 0); + + do_accept_called++; + + /* Immediately close the accepted handle. */ + uv_close((uv_handle_t*)accepted_handle, close_cb); + + /* After accepting the two clients close the server handle */ + if (do_accept_called == 2) { + uv_close((uv_handle_t*)server, close_cb); + } + + /* Dispose the timer. */ + uv_close((uv_handle_t*)timer_handle, close_cb); +} + + +static void connection_cb(uv_stream_t* tcp, int status) { + int r; + uv_timer_t* timer_handle; + + ASSERT(status == 0); + + timer_handle = (uv_timer_t*)malloc(sizeof *timer_handle); + ASSERT(timer_handle != NULL); + + /* Accept the client after 1 second */ + r = uv_timer_init(uv_default_loop(), timer_handle); + ASSERT(r == 0); + + timer_handle->data = tcp; + + r = uv_timer_start(timer_handle, do_accept, 1000, 0); + ASSERT(r == 0); + + connection_cb_called++; +} + + +static void start_server(void) { + struct sockaddr_in addr; + uv_tcp_t* server = (uv_tcp_t*)malloc(sizeof *server); + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT(server != NULL); + + r = uv_tcp_init(uv_default_loop(), server); + ASSERT(r == 0); + r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)server, 128, connection_cb); + ASSERT(r == 0); +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + /* The server will not send anything, it should close gracefully. */ + + if (buf->base) { + free(buf->base); + } + + if (nread >= 0) { + ASSERT(nread == 0); + } else { + ASSERT(tcp != NULL); + ASSERT(nread == UV_EOF); + uv_close((uv_handle_t*)tcp, close_cb); + } +} + + +static void connect_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(req != NULL); + ASSERT(status == 0); + + /* Not that the server will send anything, but otherwise we'll never know */ + /* when the server closes the connection. */ + r = uv_read_start((uv_stream_t*)(req->handle), alloc_cb, read_cb); + ASSERT(r == 0); + + connect_cb_called++; + + free(req); +} + + +static void client_connect(void) { + struct sockaddr_in addr; + uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof *client); + uv_connect_t* connect_req = malloc(sizeof *connect_req); + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT(client != NULL); + ASSERT(connect_req != NULL); + + r = uv_tcp_init(uv_default_loop(), client); + ASSERT(r == 0); + + r = uv_tcp_connect(connect_req, + client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); +} + + + +TEST_IMPL(delayed_accept) { + start_server(); + + client_connect(); + client_connect(); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(connection_cb_called == 2); + ASSERT(do_accept_called == 2); + ASSERT(connect_cb_called == 2); + ASSERT(close_cb_called == 7); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-dlerror.c b/3rdparty/libuv/test/test-dlerror.c new file mode 100644 index 00000000000..091200edbed --- /dev/null +++ b/3rdparty/libuv/test/test-dlerror.c @@ -0,0 +1,55 @@ +/* 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 <string.h> + + +TEST_IMPL(dlerror) { + const char* path = "test/fixtures/load_error.node"; + const char* dlerror_no_error = "no error"; + const char* msg; + uv_lib_t lib; + int r; + + lib.errmsg = NULL; + lib.handle = NULL; + msg = uv_dlerror(&lib); + ASSERT(msg != NULL); + ASSERT(strstr(msg, dlerror_no_error) != NULL); + + r = uv_dlopen(path, &lib); + ASSERT(r == -1); + + msg = uv_dlerror(&lib); + ASSERT(msg != NULL); + ASSERT(strstr(msg, dlerror_no_error) == NULL); + + /* Should return the same error twice in a row. */ + msg = uv_dlerror(&lib); + ASSERT(msg != NULL); + ASSERT(strstr(msg, dlerror_no_error) == NULL); + + uv_dlclose(&lib); + + return 0; +} diff --git a/3rdparty/libuv/test/test-embed.c b/3rdparty/libuv/test/test-embed.c new file mode 100644 index 00000000000..06137456f8b --- /dev/null +++ b/3rdparty/libuv/test/test-embed.c @@ -0,0 +1,138 @@ +/* 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 defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__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) + { + struct epoll_event ev; + r = epoll_wait(fd, &ev, 1, 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) { + uv_run(uv_default_loop(), UV_RUN_ONCE); + + uv_sem_post(&embed_sem); +} + + +static void embed_timer_cb(uv_timer_t* timer) { + 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; + + ASSERT(0 == uv_loop_init(&external)); + + 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_RUN_DEFAULT); + + uv_thread_join(&embed_thread); + uv_loop_close(&external); + + ASSERT(embed_timer_called == 1); +#endif + + return 0; +} diff --git a/3rdparty/libuv/test/test-emfile.c b/3rdparty/libuv/test/test-emfile.c new file mode 100644 index 00000000000..dd35f785b46 --- /dev/null +++ b/3rdparty/libuv/test/test-emfile.c @@ -0,0 +1,110 @@ +/* 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. + */ + +#if !defined(_WIN32) + +#include "uv.h" +#include "task.h" + +#include <errno.h> +#include <sys/resource.h> +#include <unistd.h> + +static void connection_cb(uv_stream_t* server_handle, int status); +static void connect_cb(uv_connect_t* req, int status); + +static const int maxfd = 31; +static unsigned connect_cb_called; +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; + + +TEST_IMPL(emfile) { + struct sockaddr_in addr; + struct rlimit limits; + uv_connect_t connect_req; + uv_loop_t* loop; + int first_fd; + + /* Lower the file descriptor limit and use up all fds save one. */ + limits.rlim_cur = limits.rlim_max = maxfd + 1; + if (setrlimit(RLIMIT_NOFILE, &limits)) { + ASSERT(errno == EPERM); /* Valgrind blocks the setrlimit() call. */ + RETURN_SKIP("setrlimit(RLIMIT_NOFILE) failed, running under valgrind?"); + } + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 8, connection_cb)); + + /* Remember the first one so we can clean up afterwards. */ + do + first_fd = dup(0); + while (first_fd == -1 && errno == EINTR); + ASSERT(first_fd > 0); + + while (dup(0) != -1 || errno == EINTR); + ASSERT(errno == EMFILE); + close(maxfd); + + /* Now connect and use up the last available file descriptor. The EMFILE + * handling logic in src/unix/stream.c should ensure that connect_cb() runs + * whereas connection_cb() should *not* run. + */ + ASSERT(0 == uv_tcp_connect(&connect_req, + &client_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(1 == connect_cb_called); + + /* Close the dups again. Ignore errors in the unlikely event that the + * file descriptors were not contiguous. + */ + while (first_fd < maxfd) { + close(first_fd); + first_fd += 1; + } + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void connection_cb(uv_stream_t* server_handle, int status) { + ASSERT(0 && "connection_cb should not be called."); +} + + +static void connect_cb(uv_connect_t* req, int status) { + /* |status| should equal 0 because the connection should have been accepted, + * it's just that the server immediately closes it again. + */ + ASSERT(0 == status); + connect_cb_called += 1; + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); +} + +#endif /* !defined(_WIN32) */ diff --git a/3rdparty/libuv/test/test-error.c b/3rdparty/libuv/test/test-error.c new file mode 100644 index 00000000000..eb337e66f33 --- /dev/null +++ b/3rdparty/libuv/test/test-error.c @@ -0,0 +1,50 @@ +/* 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 <string.h> + + +/* + * Synthetic errors (errors that originate from within libuv, not the system) + * should produce sensible error messages when run through uv_strerror(). + * + * See https://github.com/joyent/libuv/issues/210 + */ +TEST_IMPL(error_message) { + /* Cop out. Can't do proper checks on systems with + * i18n-ized error messages... + */ + if (strcmp(uv_strerror(0), "Success") != 0) { + printf("i18n error messages detected, skipping test.\n"); + return 0; + } + + ASSERT(strstr(uv_strerror(UV_EINVAL), "Success") == NULL); + ASSERT(strcmp(uv_strerror(1337), "Unknown error") == 0); + ASSERT(strcmp(uv_strerror(-1337), "Unknown error") == 0); + + return 0; +} diff --git a/3rdparty/libuv/test/test-fail-always.c b/3rdparty/libuv/test/test-fail-always.c new file mode 100644 index 00000000000..0008459eac7 --- /dev/null +++ b/3rdparty/libuv/test/test-fail-always.c @@ -0,0 +1,29 @@ +/* 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 "task.h" + + +TEST_IMPL(fail_always) { + /* This test always fails. It is used to test the test runner. */ + FATAL("Yes, it always fails"); + return 2; +} diff --git a/3rdparty/libuv/test/test-fs-event.c b/3rdparty/libuv/test/test-fs-event.c new file mode 100644 index 00000000000..e02ff2fda5e --- /dev/null +++ b/3rdparty/libuv/test/test-fs-event.c @@ -0,0 +1,907 @@ +/* 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 <string.h> +#include <fcntl.h> + +#ifndef HAVE_KQUEUE +# if defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) +# define HAVE_KQUEUE 1 +# endif +#endif + +static uv_fs_event_t fs_event; +static const char file_prefix[] = "fsevent-"; +static const int fs_event_file_count = 16; +#if defined(__APPLE__) || defined(_WIN32) +static const char file_prefix_in_subdir[] = "subdir"; +#endif +static uv_timer_t timer; +static int timer_cb_called; +static int close_cb_called; +static int fs_event_created; +static int fs_event_removed; +static int fs_event_cb_called; +#if defined(PATH_MAX) +static char fs_event_filename[PATH_MAX]; +#else +static char fs_event_filename[1024]; +#endif /* defined(PATH_MAX) */ +static int timer_cb_touch_called; + +static void create_dir(const char* name) { + int r; + uv_fs_t req; + r = uv_fs_mkdir(NULL, &req, name, 0755, NULL); + ASSERT(r == 0 || r == UV_EEXIST); + uv_fs_req_cleanup(&req); +} + +static void create_file(const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + file = r; + uv_fs_req_cleanup(&req); + r = uv_fs_close(NULL, &req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void touch_file(const char* name) { + int r; + uv_file file; + uv_fs_t req; + uv_buf_t buf; + + r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL); + ASSERT(r >= 0); + file = r; + uv_fs_req_cleanup(&req); + + buf = uv_buf_init("foo", 4); + r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_close(NULL, &req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + +static void fail_cb(uv_fs_event_t* handle, + const char* path, + int events, + int status) { + ASSERT(0 && "fail_cb called"); +} + +static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_RENAME); + #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) + ASSERT(strcmp(filename, "file1") == 0); + #else + ASSERT(filename == NULL || strcmp(filename, "file1") == 0); + #endif + ASSERT(0 == uv_fs_event_stop(handle)); + uv_close((uv_handle_t*)handle, close_cb); +} + +static const char* fs_event_get_filename(int i) { + snprintf(fs_event_filename, + sizeof(fs_event_filename), + "watch_dir/%s%d", + file_prefix, + i); + return fs_event_filename; +} + +static void fs_event_create_files(uv_timer_t* handle) { + /* Make sure we're not attempting to create files we do not intend */ + ASSERT(fs_event_created < fs_event_file_count); + + /* Create the file */ + create_file(fs_event_get_filename(fs_event_created)); + + if (++fs_event_created < fs_event_file_count) { + /* Create another file on a different event loop tick. We do it this way + * to avoid fs events coalescing into one fs event. */ + ASSERT(0 == uv_timer_start(&timer, fs_event_create_files, 1, 0)); + } +} + +static void fs_event_unlink_files(uv_timer_t* handle) { + int r; + int i; + + /* NOTE: handle might be NULL if invoked not as timer callback */ + if (handle == NULL) { + /* Unlink all files */ + for (i = 0; i < 16; i++) { + r = remove(fs_event_get_filename(i)); + if (handle != NULL) + ASSERT(r == 0); + } + } else { + /* Make sure we're not attempting to remove files we do not intend */ + ASSERT(fs_event_removed < fs_event_file_count); + + /* Remove the file */ + ASSERT(0 == remove(fs_event_get_filename(fs_event_removed))); + + if (++fs_event_removed < fs_event_file_count) { + /* Remove another file on a different event loop tick. We do it this way + * to avoid fs events coalescing into one fs event. */ + ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); + } + } +} + +static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + fs_event_cb_called++; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE || UV_RENAME); + #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) + ASSERT(strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); + #else + ASSERT(filename == NULL || + strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); + #endif + + if (fs_event_created + fs_event_removed == fs_event_file_count) { + /* Once we've processed all create events, delete all files */ + ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); + } else if (fs_event_cb_called == 2 * fs_event_file_count) { + /* Once we've processed all create and delete events, stop watching */ + uv_close((uv_handle_t*) &timer, close_cb); + uv_close((uv_handle_t*) handle, close_cb); + } +} + +#if defined(__APPLE__) || defined(_WIN32) +static const char* fs_event_get_filename_in_subdir(int i) { + snprintf(fs_event_filename, + sizeof(fs_event_filename), + "watch_dir/subdir/%s%d", + file_prefix, + i); + return fs_event_filename; +} + +static void fs_event_create_files_in_subdir(uv_timer_t* handle) { + /* Make sure we're not attempting to create files we do not intend */ + ASSERT(fs_event_created < fs_event_file_count); + + /* Create the file */ + create_file(fs_event_get_filename_in_subdir(fs_event_created)); + + if (++fs_event_created < fs_event_file_count) { + /* Create another file on a different event loop tick. We do it this way + * to avoid fs events coalescing into one fs event. */ + ASSERT(0 == uv_timer_start(&timer, fs_event_create_files_in_subdir, 1, 0)); + } +} + +static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) { + int r; + int i; + + /* NOTE: handle might be NULL if invoked not as timer callback */ + if (handle == NULL) { + /* Unlink all files */ + for (i = 0; i < 16; i++) { + r = remove(fs_event_get_filename_in_subdir(i)); + if (handle != NULL) + ASSERT(r == 0); + } + } else { + /* Make sure we're not attempting to remove files we do not intend */ + ASSERT(fs_event_removed < fs_event_file_count); + + /* Remove the file */ + ASSERT(0 == remove(fs_event_get_filename_in_subdir(fs_event_removed))); + + if (++fs_event_removed < fs_event_file_count) { + /* Remove another file on a different event loop tick. We do it this way + * to avoid fs events coalescing into one fs event. */ + ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0)); + } + } +} + +static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + fs_event_cb_called++; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE || UV_RENAME); + #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) + ASSERT(strncmp(filename, + file_prefix_in_subdir, + sizeof(file_prefix_in_subdir) - 1) == 0); + #else + ASSERT(filename == NULL || + strncmp(filename, + file_prefix_in_subdir, + sizeof(file_prefix_in_subdir) - 1) == 0); + #endif + + if (fs_event_created + fs_event_removed == fs_event_file_count) { + /* Once we've processed all create events, delete all files */ + ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0)); + } else if (fs_event_cb_called == 2 * fs_event_file_count) { + /* Once we've processed all create and delete events, stop watching */ + uv_close((uv_handle_t*) &timer, close_cb); + uv_close((uv_handle_t*) handle, close_cb); + } +} +#endif + +static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) + ASSERT(strcmp(filename, "file2") == 0); + #else + ASSERT(filename == NULL || strcmp(filename, "file2") == 0); + #endif + ASSERT(0 == uv_fs_event_stop(handle)); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_close_handle(uv_timer_t* timer) { + uv_handle_t* handle; + + ASSERT(timer != NULL); + handle = timer->data; + + uv_close((uv_handle_t*)timer, NULL); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, + const char* filename, int events, int status) { + ASSERT(fs_event_cb_called == 0); + ++fs_event_cb_called; + + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) + ASSERT(strcmp(filename, "watch_file") == 0); + #else + ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); + #endif + + /* Regression test for SunOS: touch should generate just one event. */ + { + static uv_timer_t timer; + uv_timer_init(handle->loop, &timer); + timer.data = handle; + uv_timer_start(&timer, timer_cb_close_handle, 250, 0); + } +} + +static void timer_cb_file(uv_timer_t* handle) { + ++timer_cb_called; + + if (timer_cb_called == 1) { + touch_file("watch_dir/file1"); + } else { + touch_file("watch_dir/file2"); + uv_close((uv_handle_t*)handle, close_cb); + } +} + +static void timer_cb_touch(uv_timer_t* timer) { + uv_close((uv_handle_t*)timer, NULL); + touch_file("watch_file"); + timer_cb_touch_called++; +} + +static void timer_cb_watch_twice(uv_timer_t* handle) { + uv_fs_event_t* handles = handle->data; + uv_close((uv_handle_t*) (handles + 0), NULL); + uv_close((uv_handle_t*) (handles + 1), NULL); + uv_close((uv_handle_t*) handle, NULL); +} + +TEST_IMPL(fs_event_watch_dir) { + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + fs_event_unlink_files(NULL); + remove("watch_dir/file2"); + remove("watch_dir/file1"); + remove("watch_dir/"); + create_dir("watch_dir"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0); + ASSERT(r == 0); + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + r = uv_timer_start(&timer, fs_event_create_files, 100, 0); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + fs_event_unlink_files(NULL); + remove("watch_dir/file2"); + remove("watch_dir/file1"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_event_watch_dir_recursive) { +#if defined(__APPLE__) || defined(_WIN32) + uv_loop_t* loop; + int r; + + /* Setup */ + loop = uv_default_loop(); + fs_event_unlink_files(NULL); + remove("watch_dir/file2"); + remove("watch_dir/file1"); + remove("watch_dir/subdir"); + remove("watch_dir/"); + create_dir("watch_dir"); + create_dir("watch_dir/subdir"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file_in_subdir, "watch_dir", UV_FS_EVENT_RECURSIVE); + ASSERT(r == 0); + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + fs_event_unlink_files_in_subdir(NULL); + remove("watch_dir/file2"); + remove("watch_dir/file1"); + remove("watch_dir/subdir"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +#else + RETURN_SKIP("Recursive directory watching not supported on this platform."); +#endif +} + + +TEST_IMPL(fs_event_watch_file) { + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + remove("watch_dir/file2"); + remove("watch_dir/file1"); + remove("watch_dir/"); + create_dir("watch_dir"); + create_file("watch_dir/file1"); + create_file("watch_dir/file2"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0); + ASSERT(r == 0); + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + r = uv_timer_start(&timer, timer_cb_file, 100, 100); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 2); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + remove("watch_dir/file2"); + remove("watch_dir/file1"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_event_watch_file_twice) { + const char path[] = "test/fixtures/empty_file"; + uv_fs_event_t watchers[2]; + uv_timer_t timer; + uv_loop_t* loop; + + loop = uv_default_loop(); + timer.data = watchers; + + ASSERT(0 == uv_fs_event_init(loop, watchers + 0)); + ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0)); + ASSERT(0 == uv_fs_event_init(loop, watchers + 1)); + ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0)); + ASSERT(0 == uv_timer_init(loop, &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_event_watch_file_current_dir) { + uv_timer_t timer; + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + /* Setup */ + remove("watch_file"); + create_file("watch_file"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, + fs_event_cb_file_current_dir, + "watch_file", + 0); + ASSERT(r == 0); + + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb_touch, 10, 0); + ASSERT(r == 0); + + ASSERT(timer_cb_touch_called == 0); + ASSERT(fs_event_cb_called == 0); + ASSERT(close_cb_called == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(timer_cb_touch_called == 1); + ASSERT(fs_event_cb_called == 1); + ASSERT(close_cb_called == 1); + + /* Cleanup */ + remove("watch_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_event_no_callback_after_close) { + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + remove("watch_dir/file1"); + remove("watch_dir/"); + create_dir("watch_dir"); + create_file("watch_dir/file1"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, + fs_event_cb_file, + "watch_dir/file1", + 0); + ASSERT(r == 0); + + + uv_close((uv_handle_t*)&fs_event, close_cb); + touch_file("watch_dir/file1"); + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(fs_event_cb_called == 0); + ASSERT(close_cb_called == 1); + + /* Cleanup */ + remove("watch_dir/file1"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_event_no_callback_on_close) { + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + remove("watch_dir/file1"); + remove("watch_dir/"); + create_dir("watch_dir"); + create_file("watch_dir/file1"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, + fs_event_cb_file, + "watch_dir/file1", + 0); + ASSERT(r == 0); + + uv_close((uv_handle_t*)&fs_event, close_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(fs_event_cb_called == 0); + ASSERT(close_cb_called == 1); + + /* Cleanup */ + remove("watch_dir/file1"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void fs_event_fail(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ASSERT(0 && "should never be called"); +} + + +static void timer_cb(uv_timer_t* handle) { + int r; + + r = uv_fs_event_init(handle->loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0); + ASSERT(r == 0); + + uv_close((uv_handle_t*)&fs_event, close_cb); + uv_close((uv_handle_t*)handle, close_cb); +} + + +TEST_IMPL(fs_event_immediate_close) { + uv_timer_t timer; + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 1, 0); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_event_close_with_pending_event) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + create_dir("watch_dir"); + create_file("watch_dir/file"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0); + ASSERT(r == 0); + + /* Generate an fs event. */ + touch_file("watch_dir/file"); + + uv_close((uv_handle_t*)&fs_event, close_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + /* Clean up */ + remove("watch_dir/file"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#if defined(HAVE_KQUEUE) + +/* kqueue doesn't register fs events if you don't have an active watcher. + * The file descriptor needs to be part of the kqueue set of interest and + * that's not the case until we actually enter the event loop. + */ +TEST_IMPL(fs_event_close_in_callback) { + fprintf(stderr, "Skipping test, doesn't work with kqueue.\n"); + return 0; +} + +#else /* !HAVE_KQUEUE */ + +static void fs_event_cb_close(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ASSERT(status == 0); + + ASSERT(fs_event_cb_called < 3); + ++fs_event_cb_called; + + if (fs_event_cb_called == 3) { + uv_close((uv_handle_t*) handle, close_cb); + } +} + + +TEST_IMPL(fs_event_close_in_callback) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + create_dir("watch_dir"); + create_file("watch_dir/file1"); + create_file("watch_dir/file2"); + create_file("watch_dir/file3"); + create_file("watch_dir/file4"); + create_file("watch_dir/file5"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0); + ASSERT(r == 0); + + /* Generate a couple of fs events. */ + touch_file("watch_dir/file1"); + touch_file("watch_dir/file2"); + touch_file("watch_dir/file3"); + touch_file("watch_dir/file4"); + touch_file("watch_dir/file5"); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + ASSERT(fs_event_cb_called == 3); + + /* Clean up */ + remove("watch_dir/file1"); + remove("watch_dir/file2"); + remove("watch_dir/file3"); + remove("watch_dir/file4"); + remove("watch_dir/file5"); + remove("watch_dir/"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* HAVE_KQUEUE */ + +TEST_IMPL(fs_event_start_and_close) { + uv_loop_t* loop; + uv_fs_event_t fs_event1; + uv_fs_event_t fs_event2; + int r; + + loop = uv_default_loop(); + + create_dir("watch_dir"); + + r = uv_fs_event_init(loop, &fs_event1); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0); + ASSERT(r == 0); + + r = uv_fs_event_init(loop, &fs_event2); + ASSERT(r == 0); + r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0); + ASSERT(r == 0); + + uv_close((uv_handle_t*) &fs_event2, close_cb); + uv_close((uv_handle_t*) &fs_event1, close_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + + remove("watch_dir/"); + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_event_getpath) { + uv_loop_t* loop = uv_default_loop(); + int r; + char buf[1024]; + size_t len; + + create_dir("watch_dir"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT(r == 0); + len = sizeof buf; + r = uv_fs_event_getpath(&fs_event, buf, &len); + ASSERT(r == UV_EINVAL); + r = uv_fs_event_start(&fs_event, fail_cb, "watch_dir", 0); + ASSERT(r == 0); + len = sizeof buf; + r = uv_fs_event_getpath(&fs_event, buf, &len); + ASSERT(r == 0); + ASSERT(buf[len - 1] != 0); + ASSERT(memcmp(buf, "watch_dir", len) == 0); + r = uv_fs_event_stop(&fs_event); + ASSERT(r == 0); + uv_close((uv_handle_t*) &fs_event, close_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + remove("watch_dir/"); + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#if defined(__APPLE__) + +static int fs_event_error_reported; + +static void fs_event_error_report_cb(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + if (status != 0) + fs_event_error_reported = status; +} + +static void timer_cb_nop(uv_timer_t* handle) { + ++timer_cb_called; + uv_close((uv_handle_t*) handle, close_cb); +} + +static void fs_event_error_report_close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; + + /* handle is allocated on-stack, no need to free it */ +} + + +TEST_IMPL(fs_event_error_reporting) { + unsigned int i; + uv_loop_t loops[1024]; + uv_fs_event_t events[ARRAY_SIZE(loops)]; + uv_loop_t* loop; + uv_fs_event_t* event; + + TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3); + + remove("watch_dir/"); + create_dir("watch_dir"); + + /* Create a lot of loops, and start FSEventStream in each of them. + * Eventually, this should create enough streams to make FSEventStreamStart() + * fail. + */ + for (i = 0; i < ARRAY_SIZE(loops); i++) { + loop = &loops[i]; + ASSERT(0 == uv_loop_init(loop)); + event = &events[i]; + + timer_cb_called = 0; + close_cb_called = 0; + ASSERT(0 == uv_fs_event_init(loop, event)); + ASSERT(0 == uv_fs_event_start(event, + fs_event_error_report_cb, + "watch_dir", + 0)); + uv_unref((uv_handle_t*) event); + + /* Let loop run for some time */ + ASSERT(0 == uv_timer_init(loop, &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0)); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(1 == timer_cb_called); + ASSERT(1 == close_cb_called); + if (fs_event_error_reported != 0) + break; + } + + /* At least one loop should fail */ + ASSERT(fs_event_error_reported == UV_EMFILE); + + /* Stop and close all events, and destroy loops */ + do { + loop = &loops[i]; + event = &events[i]; + + ASSERT(0 == uv_fs_event_stop(event)); + uv_ref((uv_handle_t*) event); + uv_close((uv_handle_t*) event, fs_event_error_report_close_cb); + + close_cb_called = 0; + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(close_cb_called == 1); + + uv_loop_close(loop); + } while (i-- != 0); + + remove("watch_dir/"); + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#else /* !defined(__APPLE__) */ + +TEST_IMPL(fs_event_error_reporting) { + /* No-op, needed only for FSEvents backend */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* defined(__APPLE__) */ diff --git a/3rdparty/libuv/test/test-fs-poll.c b/3rdparty/libuv/test/test-fs-poll.c new file mode 100644 index 00000000000..dbc1515b0b1 --- /dev/null +++ b/3rdparty/libuv/test/test-fs-poll.c @@ -0,0 +1,186 @@ +/* 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 <string.h> + +#define FIXTURE "testfile" + +static void timer_cb(uv_timer_t* handle); +static void close_cb(uv_handle_t* handle); +static void poll_cb(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr); + +static void poll_cb_fail(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr); + +static uv_fs_poll_t poll_handle; +static uv_timer_t timer_handle; +static uv_loop_t* loop; + +static int poll_cb_called; +static int timer_cb_called; +static int close_cb_called; + + +static void touch_file(const char* path) { + static int count; + FILE* fp; + int i; + + ASSERT((fp = fopen(FIXTURE, "w+"))); + + /* Need to change the file size because the poller may not pick up + * sub-second mtime changes. + */ + i = ++count; + + while (i--) + fputc('*', fp); + + fclose(fp); +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + touch_file(FIXTURE); + timer_cb_called++; +} + + +static void poll_cb_fail(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr) { + ASSERT(0 && "fail_cb called"); +} + + +static void poll_cb(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr) { + uv_stat_t zero_statbuf; + + memset(&zero_statbuf, 0, sizeof(zero_statbuf)); + + ASSERT(handle == &poll_handle); + ASSERT(1 == uv_is_active((uv_handle_t*) handle)); + ASSERT(prev != NULL); + ASSERT(curr != NULL); + + switch (poll_cb_called++) { + case 0: + ASSERT(status == UV_ENOENT); + ASSERT(0 == memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 == memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + touch_file(FIXTURE); + break; + + case 1: + ASSERT(status == 0); + ASSERT(0 == memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 20, 0)); + break; + + case 2: + ASSERT(status == 0); + ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 200, 0)); + break; + + case 3: + ASSERT(status == 0); + ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + remove(FIXTURE); + break; + + case 4: + ASSERT(status == UV_ENOENT); + ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT(0 == memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + uv_close((uv_handle_t*)handle, close_cb); + break; + + default: + ASSERT(0); + } +} + + +TEST_IMPL(fs_poll) { + loop = uv_default_loop(); + + remove(FIXTURE); + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_fs_poll_init(loop, &poll_handle)); + ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb, FIXTURE, 100)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(poll_cb_called == 5); + ASSERT(timer_cb_called == 2); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_poll_getpath) { + char buf[1024]; + size_t len; + loop = uv_default_loop(); + + remove(FIXTURE); + + ASSERT(0 == uv_fs_poll_init(loop, &poll_handle)); + len = sizeof buf; + ASSERT(UV_EINVAL == uv_fs_poll_getpath(&poll_handle, buf, &len)); + ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); + len = sizeof buf; + ASSERT(0 == uv_fs_poll_getpath(&poll_handle, buf, &len)); + ASSERT(buf[len - 1] != 0); + ASSERT(0 == memcmp(buf, FIXTURE, len)); + + uv_close((uv_handle_t*) &poll_handle, close_cb); + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-fs.c b/3rdparty/libuv/test/test-fs.c new file mode 100644 index 00000000000..cf37ac4909c --- /dev/null +++ b/3rdparty/libuv/test/test-fs.c @@ -0,0 +1,2664 @@ +/* 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 <errno.h> +#include <string.h> /* memset */ +#include <fcntl.h> +#include <sys/stat.h> + +/* FIXME we shouldn't need to branch in this file */ +#if defined(__unix__) || defined(__POSIX__) || \ + defined(__APPLE__) || defined(_AIX) +#include <unistd.h> /* unlink, rmdir, etc. */ +#else +# include <direct.h> +# include <io.h> +# define unlink _unlink +# define rmdir _rmdir +# define open _open +# define write _write +# define close _close +# ifndef stat +# define stat _stati64 +# endif +# ifndef lseek +# define lseek _lseek +# endif +#endif + +#define TOO_LONG_NAME_LENGTH 65536 +#define PATHMAX 1024 + +typedef struct { + const char* path; + double atime; + double mtime; +} utime_check_t; + + +static int dummy_cb_count; +static int close_cb_count; +static int create_cb_count; +static int open_cb_count; +static int read_cb_count; +static int write_cb_count; +static int unlink_cb_count; +static int mkdir_cb_count; +static int mkdtemp_cb_count; +static int rmdir_cb_count; +static int scandir_cb_count; +static int stat_cb_count; +static int rename_cb_count; +static int fsync_cb_count; +static int fdatasync_cb_count; +static int ftruncate_cb_count; +static int sendfile_cb_count; +static int fstat_cb_count; +static int access_cb_count; +static int chmod_cb_count; +static int fchmod_cb_count; +static int chown_cb_count; +static int fchown_cb_count; +static int link_cb_count; +static int symlink_cb_count; +static int readlink_cb_count; +static int realpath_cb_count; +static int utime_cb_count; +static int futime_cb_count; + +static uv_loop_t* loop; + +static uv_fs_t open_req1; +static uv_fs_t open_req2; +static uv_fs_t read_req; +static uv_fs_t write_req; +static uv_fs_t unlink_req; +static uv_fs_t close_req; +static uv_fs_t mkdir_req; +static uv_fs_t mkdtemp_req1; +static uv_fs_t mkdtemp_req2; +static uv_fs_t rmdir_req; +static uv_fs_t scandir_req; +static uv_fs_t stat_req; +static uv_fs_t rename_req; +static uv_fs_t fsync_req; +static uv_fs_t fdatasync_req; +static uv_fs_t ftruncate_req; +static uv_fs_t sendfile_req; +static uv_fs_t utime_req; +static uv_fs_t futime_req; + +static char buf[32]; +static char buf2[32]; +static char test_buf[] = "test-buffer\n"; +static char test_buf2[] = "second-buffer\n"; +static uv_buf_t iov; + +static void check_permission(const char* filename, unsigned int mode) { + int r; + uv_fs_t req; + uv_stat_t* s; + + r = uv_fs_stat(NULL, &req, filename, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + + s = &req.statbuf; +#ifdef _WIN32 + /* + * On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit, + * so only testing for the specified flags. + */ + ASSERT((s->st_mode & 0777) & mode); +#else + ASSERT((s->st_mode & 0777) == mode); +#endif + + uv_fs_req_cleanup(&req); +} + + +static void dummy_cb(uv_fs_t* req) { + (void) req; + dummy_cb_count++; +} + + +static void link_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_LINK); + ASSERT(req->result == 0); + link_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void symlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_SYMLINK); + ASSERT(req->result == 0); + symlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void readlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_READLINK); + ASSERT(req->result == 0); + ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0); + readlink_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void realpath_cb(uv_fs_t* req) { + char test_file_abs_buf[PATHMAX]; + size_t test_file_abs_size = sizeof(test_file_abs_buf); + ASSERT(req->fs_type == UV_FS_REALPATH); +#ifdef _WIN32 + /* + * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() + */ + if (req->result == UV_ENOSYS) { + realpath_cb_count++; + uv_fs_req_cleanup(req); + return; + } +#endif + ASSERT(req->result == 0); + + uv_cwd(test_file_abs_buf, &test_file_abs_size); +#ifdef _WIN32 + strcat(test_file_abs_buf, "\\test_file"); + ASSERT(stricmp(req->ptr, test_file_abs_buf) == 0); +#else + strcat(test_file_abs_buf, "/test_file"); + ASSERT(strcmp(req->ptr, test_file_abs_buf) == 0); +#endif + realpath_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void access_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_ACCESS); + access_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void fchmod_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_FCHMOD); + ASSERT(req->result == 0); + fchmod_cb_count++; + uv_fs_req_cleanup(req); + check_permission("test_file", *(int*)req->data); +} + + +static void chmod_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHMOD); + ASSERT(req->result == 0); + chmod_cb_count++; + uv_fs_req_cleanup(req); + check_permission("test_file", *(int*)req->data); +} + + +static void fchown_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_FCHOWN); + ASSERT(req->result == 0); + fchown_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void chown_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHOWN); + ASSERT(req->result == 0); + chown_cb_count++; + uv_fs_req_cleanup(req); +} + +static void chown_root_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHOWN); +#ifdef _WIN32 + /* On windows, chown is a no-op and always succeeds. */ + ASSERT(req->result == 0); +#else + /* On unix, chown'ing the root directory is not allowed - + * unless you're root, of course. + */ + if (geteuid() == 0) + ASSERT(req->result == 0); + else + ASSERT(req->result == UV_EPERM); +#endif + chown_cb_count++; + uv_fs_req_cleanup(req); +} + +static void unlink_cb(uv_fs_t* req) { + ASSERT(req == &unlink_req); + ASSERT(req->fs_type == UV_FS_UNLINK); + ASSERT(req->result == 0); + unlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void fstat_cb(uv_fs_t* req) { + uv_stat_t* s = req->ptr; + ASSERT(req->fs_type == UV_FS_FSTAT); + ASSERT(req->result == 0); + ASSERT(s->st_size == sizeof(test_buf)); + uv_fs_req_cleanup(req); + fstat_cb_count++; +} + + +static void close_cb(uv_fs_t* req) { + int r; + ASSERT(req == &close_req); + ASSERT(req->fs_type == UV_FS_CLOSE); + ASSERT(req->result == 0); + close_cb_count++; + uv_fs_req_cleanup(req); + if (close_cb_count == 3) { + r = uv_fs_unlink(loop, &unlink_req, "test_file2", unlink_cb); + ASSERT(r == 0); + } +} + + +static void ftruncate_cb(uv_fs_t* req) { + int r; + ASSERT(req == &ftruncate_req); + ASSERT(req->fs_type == UV_FS_FTRUNCATE); + ASSERT(req->result == 0); + ftruncate_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); + ASSERT(r == 0); +} + + +static void read_cb(uv_fs_t* req) { + int r; + ASSERT(req == &read_req); + ASSERT(req->fs_type == UV_FS_READ); + ASSERT(req->result >= 0); /* FIXME(bnoordhuis) Check if requested size? */ + read_cb_count++; + uv_fs_req_cleanup(req); + if (read_cb_count == 1) { + ASSERT(strcmp(buf, test_buf) == 0); + r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, + ftruncate_cb); + } else { + ASSERT(strcmp(buf, "test-bu") == 0); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); + } + ASSERT(r == 0); +} + + +static void open_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + if (req->result < 0) { + fprintf(stderr, "async open error: %d\n", (int) req->result); + ASSERT(0); + } + open_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_file2\0", 11) == 0); + uv_fs_req_cleanup(req); + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, + read_cb); + ASSERT(r == 0); +} + + +static void open_cb_simple(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_OPEN); + if (req->result < 0) { + fprintf(stderr, "async open error: %d\n", (int) req->result); + ASSERT(0); + } + open_cb_count++; + ASSERT(req->path); + uv_fs_req_cleanup(req); +} + + +static void fsync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fsync_req); + ASSERT(req->fs_type == UV_FS_FSYNC); + ASSERT(req->result == 0); + fsync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); + ASSERT(r == 0); +} + + +static void fdatasync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fdatasync_req); + ASSERT(req->fs_type == UV_FS_FDATASYNC); + ASSERT(req->result == 0); + fdatasync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fsync(loop, &fsync_req, open_req1.result, fsync_cb); + ASSERT(r == 0); +} + + +static void write_cb(uv_fs_t* req) { + int r; + ASSERT(req == &write_req); + ASSERT(req->fs_type == UV_FS_WRITE); + ASSERT(req->result >= 0); /* FIXME(bnoordhuis) Check if requested size? */ + write_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); + ASSERT(r == 0); +} + + +static void create_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result >= 0); + create_cb_count++; + uv_fs_req_cleanup(req); + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(loop, &write_req, req->result, &iov, 1, -1, write_cb); + ASSERT(r == 0); +} + + +static void rename_cb(uv_fs_t* req) { + ASSERT(req == &rename_req); + ASSERT(req->fs_type == UV_FS_RENAME); + ASSERT(req->result == 0); + rename_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void mkdir_cb(uv_fs_t* req) { + ASSERT(req == &mkdir_req); + ASSERT(req->fs_type == UV_FS_MKDIR); + ASSERT(req->result == 0); + mkdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); +} + + +static void check_mkdtemp_result(uv_fs_t* req) { + int r; + + ASSERT(req->fs_type == UV_FS_MKDTEMP); + ASSERT(req->result == 0); + ASSERT(req->path); + ASSERT(strlen(req->path) == 15); + ASSERT(memcmp(req->path, "test_dir_", 9) == 0); + ASSERT(memcmp(req->path + 9, "XXXXXX", 6) != 0); + check_permission(req->path, 0700); + + /* Check if req->path is actually a directory */ + r = uv_fs_stat(NULL, &stat_req, req->path, NULL); + ASSERT(r == 0); + ASSERT(((uv_stat_t*)stat_req.ptr)->st_mode & S_IFDIR); + uv_fs_req_cleanup(&stat_req); +} + + +static void mkdtemp_cb(uv_fs_t* req) { + ASSERT(req == &mkdtemp_req1); + check_mkdtemp_result(req); + mkdtemp_cb_count++; +} + + +static void rmdir_cb(uv_fs_t* req) { + ASSERT(req == &rmdir_req); + ASSERT(req->fs_type == UV_FS_RMDIR); + ASSERT(req->result == 0); + rmdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); +} + + +static void assert_is_file_type(uv_dirent_t dent) { +#ifdef HAVE_DIRENT_TYPES + /* + * For Apple and Windows, we know getdents is expected to work but for other + * environments, the filesystem dictates whether or not getdents supports + * returning the file type. + * + * See: + * http://man7.org/linux/man-pages/man2/getdents.2.html + * https://github.com/libuv/libuv/issues/501 + */ + #if defined(__APPLE__) || defined(_WIN32) + ASSERT(dent.type == UV_DIRENT_FILE); + #else + ASSERT(dent.type == UV_DIRENT_FILE || dent.type == UV_DIRENT_UNKNOWN); + #endif +#else + ASSERT(dent.type == UV_DIRENT_UNKNOWN); +#endif +} + + +static void scandir_cb(uv_fs_t* req) { + uv_dirent_t dent; + ASSERT(req == &scandir_req); + ASSERT(req->fs_type == UV_FS_SCANDIR); + ASSERT(req->result == 2); + ASSERT(req->ptr); + + while (UV_EOF != uv_fs_scandir_next(req, &dent)) { + ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); + assert_is_file_type(dent); + } + scandir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void empty_scandir_cb(uv_fs_t* req) { + uv_dirent_t dent; + + ASSERT(req == &scandir_req); + ASSERT(req->fs_type == UV_FS_SCANDIR); + ASSERT(req->result == 0); + ASSERT(req->ptr == NULL); + ASSERT(UV_EOF == uv_fs_scandir_next(req, &dent)); + uv_fs_req_cleanup(req); + scandir_cb_count++; +} + + +static void file_scandir_cb(uv_fs_t* req) { + ASSERT(req == &scandir_req); + ASSERT(req->fs_type == UV_FS_SCANDIR); + ASSERT(req->result == UV_ENOTDIR); + ASSERT(req->ptr == NULL); + uv_fs_req_cleanup(req); + scandir_cb_count++; +} + + +static void stat_cb(uv_fs_t* req) { + ASSERT(req == &stat_req); + ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT); + ASSERT(req->result == 0); + ASSERT(req->ptr); + stat_cb_count++; + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void sendfile_cb(uv_fs_t* req) { + ASSERT(req == &sendfile_req); + ASSERT(req->fs_type == UV_FS_SENDFILE); + ASSERT(req->result == 65546); + sendfile_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void open_noent_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result == UV_ENOENT); + open_cb_count++; + uv_fs_req_cleanup(req); +} + +static void open_nametoolong_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result == UV_ENAMETOOLONG); + open_cb_count++; + uv_fs_req_cleanup(req); +} + +static void open_loop_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result == UV_ELOOP); + open_cb_count++; + uv_fs_req_cleanup(req); +} + + +TEST_IMPL(fs_file_noent) { + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, "does_not_exist", O_RDONLY, 0, NULL); + ASSERT(r == UV_ENOENT); + ASSERT(req.result == UV_ENOENT); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, open_noent_cb); + ASSERT(r == 0); + + ASSERT(open_cb_count == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(open_cb_count == 1); + + /* TODO add EACCES test */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_file_nametoolong) { + uv_fs_t req; + int r; + char name[TOO_LONG_NAME_LENGTH + 1]; + + loop = uv_default_loop(); + + memset(name, 'a', TOO_LONG_NAME_LENGTH); + name[TOO_LONG_NAME_LENGTH] = 0; + + r = uv_fs_open(NULL, &req, name, O_RDONLY, 0, NULL); + ASSERT(r == UV_ENAMETOOLONG); + ASSERT(req.result == UV_ENAMETOOLONG); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, name, O_RDONLY, 0, open_nametoolong_cb); + ASSERT(r == 0); + + ASSERT(open_cb_count == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(open_cb_count == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(fs_file_loop) { + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + unlink("test_symlink"); + r = uv_fs_symlink(NULL, &req, "test_symlink", "test_symlink", 0, NULL); +#ifdef _WIN32 + /* + * Windows XP and Server 2003 don't support symlinks; we'll get UV_ENOTSUP. + * Starting with vista they are supported, but only when elevated, otherwise + * we'll see UV_EPERM. + */ + if (r == UV_ENOTSUP || r == UV_EPERM) + return 0; +#endif + ASSERT(r == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(NULL, &req, "test_symlink", O_RDONLY, 0, NULL); + ASSERT(r == UV_ELOOP); + ASSERT(req.result == UV_ELOOP); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_symlink", O_RDONLY, 0, open_loop_cb); + ASSERT(r == 0); + + ASSERT(open_cb_count == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(open_cb_count == 1); + + unlink("test_symlink"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +static void check_utime(const char* path, double atime, double mtime) { + uv_stat_t* s; + uv_fs_t req; + int r; + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + + ASSERT(req.result == 0); + s = &req.statbuf; + + ASSERT(s->st_atim.tv_sec == atime); + ASSERT(s->st_mtim.tv_sec == mtime); + + uv_fs_req_cleanup(&req); +} + + +static void utime_cb(uv_fs_t* req) { + utime_check_t* c; + + ASSERT(req == &utime_req); + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_UTIME); + + c = req->data; + check_utime(c->path, c->atime, c->mtime); + + uv_fs_req_cleanup(req); + utime_cb_count++; +} + + +static void futime_cb(uv_fs_t* req) { + utime_check_t* c; + + ASSERT(req == &futime_req); + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_FUTIME); + + c = req->data; + check_utime(c->path, c->atime, c->mtime); + + uv_fs_req_cleanup(req); + futime_cb_count++; +} + + +TEST_IMPL(fs_file_async) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR, create_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(fsync_cb_count == 1); + ASSERT(fdatasync_cb_count == 1); + ASSERT(close_cb_count == 1); + + r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", rename_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(close_cb_count == 1); + ASSERT(rename_cb_count == 1); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDWR, 0, open_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(open_cb_count == 1); + ASSERT(read_cb_count == 1); + ASSERT(close_cb_count == 2); + ASSERT(rename_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, open_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(open_cb_count == 2); + ASSERT(read_cb_count == 2); + ASSERT(close_cb_count == 3); + ASSERT(rename_cb_count == 1); + ASSERT(unlink_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file2"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_file_sync) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(write_req.result >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(read_req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_ftruncate(NULL, &ftruncate_req, open_req1.result, 7, NULL); + ASSERT(r == 0); + ASSERT(ftruncate_req.result == 0); + uv_fs_req_cleanup(&ftruncate_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_rename(NULL, &rename_req, "test_file", "test_file2", NULL); + ASSERT(r == 0); + ASSERT(rename_req.result == 0); + uv_fs_req_cleanup(&rename_req); + + r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(read_req.result >= 0); + ASSERT(strcmp(buf, "test-bu") == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_unlink(NULL, &unlink_req, "test_file2", NULL); + ASSERT(r == 0); + ASSERT(unlink_req.result == 0); + uv_fs_req_cleanup(&unlink_req); + + /* Cleanup */ + unlink("test_file"); + unlink("test_file2"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_file_write_null_buffer) { + int r; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(NULL, 0); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 0); + ASSERT(write_req.result == 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_async_dir) { + int r; + uv_dirent_t dent; + + /* Setup */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + + loop = uv_default_loop(); + + r = uv_fs_mkdir(loop, &mkdir_req, "test_dir", 0755, mkdir_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(mkdir_cb_count == 1); + + /* Create 2 files synchronously. */ + r = uv_fs_open(NULL, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_scandir(loop, &scandir_req, "test_dir", 0, scandir_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(scandir_cb_count == 1); + + /* sync uv_fs_scandir */ + r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); + ASSERT(r == 2); + ASSERT(scandir_req.result == 2); + ASSERT(scandir_req.ptr); + while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { + ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); + assert_is_file_type(dent); + } + uv_fs_req_cleanup(&scandir_req); + ASSERT(!scandir_req.ptr); + + r = uv_fs_stat(loop, &stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + + r = uv_fs_stat(loop, &stat_req, "test_dir/", stat_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + + r = uv_fs_lstat(loop, &stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + + r = uv_fs_lstat(loop, &stat_req, "test_dir/", stat_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(stat_cb_count == 4); + + r = uv_fs_unlink(loop, &unlink_req, "test_dir/file1", unlink_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(unlink_cb_count == 1); + + r = uv_fs_unlink(loop, &unlink_req, "test_dir/file2", unlink_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(unlink_cb_count == 2); + + r = uv_fs_rmdir(loop, &rmdir_req, "test_dir", rmdir_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(rmdir_cb_count == 1); + + /* Cleanup */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_async_sendfile) { + int f, r; + struct stat s1, s2; + + loop = uv_default_loop(); + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + f = open("test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); + ASSERT(f != -1); + + r = write(f, "begin\n", 6); + ASSERT(r == 6); + + r = lseek(f, 65536, SEEK_CUR); + ASSERT(r == 65542); + + r = write(f, "end\n", 4); + ASSERT(r != -1); + + r = close(f); + ASSERT(r == 0); + + /* Test starts here. */ + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_open(NULL, &open_req2, "test_file2", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req2.result >= 0); + uv_fs_req_cleanup(&open_req2); + + r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, + 0, 131072, sendfile_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(sendfile_cb_count == 1); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + r = uv_fs_close(NULL, &close_req, open_req2.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + stat("test_file", &s1); + stat("test_file2", &s2); + ASSERT(65546 == s2.st_size && s1.st_size == s2.st_size); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file2"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_mkdtemp) { + int r; + const char* path_template = "test_dir_XXXXXX"; + + loop = uv_default_loop(); + + r = uv_fs_mkdtemp(loop, &mkdtemp_req1, path_template, mkdtemp_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(mkdtemp_cb_count == 1); + + /* sync mkdtemp */ + r = uv_fs_mkdtemp(NULL, &mkdtemp_req2, path_template, NULL); + ASSERT(r == 0); + check_mkdtemp_result(&mkdtemp_req2); + + /* mkdtemp return different values on subsequent calls */ + ASSERT(strcmp(mkdtemp_req1.path, mkdtemp_req2.path) != 0); + + /* Cleanup */ + rmdir(mkdtemp_req1.path); + rmdir(mkdtemp_req2.path); + uv_fs_req_cleanup(&mkdtemp_req1); + uv_fs_req_cleanup(&mkdtemp_req2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_fstat) { + int r; + uv_fs_t req; + uv_file file; + uv_stat_t* s; +#ifndef _WIN32 + struct stat t; +#endif + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + r = uv_fs_fstat(NULL, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + s = req.ptr; + ASSERT(s->st_size == sizeof(test_buf)); + +#ifndef _WIN32 + r = fstat(file, &t); + ASSERT(r == 0); + + ASSERT(s->st_dev == (uint64_t) t.st_dev); + ASSERT(s->st_mode == (uint64_t) t.st_mode); + ASSERT(s->st_nlink == (uint64_t) t.st_nlink); + ASSERT(s->st_uid == (uint64_t) t.st_uid); + ASSERT(s->st_gid == (uint64_t) t.st_gid); + ASSERT(s->st_rdev == (uint64_t) t.st_rdev); + ASSERT(s->st_ino == (uint64_t) t.st_ino); + ASSERT(s->st_size == (uint64_t) t.st_size); + ASSERT(s->st_blksize == (uint64_t) t.st_blksize); + ASSERT(s->st_blocks == (uint64_t) t.st_blocks); +#if defined(__APPLE__) + ASSERT(s->st_atim.tv_sec == t.st_atimespec.tv_sec); + ASSERT(s->st_atim.tv_nsec == t.st_atimespec.tv_nsec); + ASSERT(s->st_mtim.tv_sec == t.st_mtimespec.tv_sec); + ASSERT(s->st_mtim.tv_nsec == t.st_mtimespec.tv_nsec); + ASSERT(s->st_ctim.tv_sec == t.st_ctimespec.tv_sec); + ASSERT(s->st_ctim.tv_nsec == t.st_ctimespec.tv_nsec); + ASSERT(s->st_birthtim.tv_sec == t.st_birthtimespec.tv_sec); + ASSERT(s->st_birthtim.tv_nsec == t.st_birthtimespec.tv_nsec); + ASSERT(s->st_flags == t.st_flags); + ASSERT(s->st_gen == t.st_gen); +#elif defined(_AIX) + ASSERT(s->st_atim.tv_sec == t.st_atime); + ASSERT(s->st_atim.tv_nsec == 0); + ASSERT(s->st_mtim.tv_sec == t.st_mtime); + ASSERT(s->st_mtim.tv_nsec == 0); + ASSERT(s->st_ctim.tv_sec == t.st_ctime); + ASSERT(s->st_ctim.tv_nsec == 0); +#elif defined(__sun) || \ + defined(_BSD_SOURCE) || \ + defined(_SVID_SOURCE) || \ + defined(_XOPEN_SOURCE) || \ + defined(_DEFAULT_SOURCE) + ASSERT(s->st_atim.tv_sec == t.st_atim.tv_sec); + ASSERT(s->st_atim.tv_nsec == t.st_atim.tv_nsec); + ASSERT(s->st_mtim.tv_sec == t.st_mtim.tv_sec); + ASSERT(s->st_mtim.tv_nsec == t.st_mtim.tv_nsec); + ASSERT(s->st_ctim.tv_sec == t.st_ctim.tv_sec); + ASSERT(s->st_ctim.tv_nsec == t.st_ctim.tv_nsec); +# if defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) + ASSERT(s->st_birthtim.tv_sec == t.st_birthtim.tv_sec); + ASSERT(s->st_birthtim.tv_nsec == t.st_birthtim.tv_nsec); + ASSERT(s->st_flags == t.st_flags); + ASSERT(s->st_gen == t.st_gen); +# endif +#else + ASSERT(s->st_atim.tv_sec == t.st_atime); + ASSERT(s->st_atim.tv_nsec == 0); + ASSERT(s->st_mtim.tv_sec == t.st_mtime); + ASSERT(s->st_mtim.tv_nsec == 0); + ASSERT(s->st_ctim.tv_sec == t.st_ctime); + ASSERT(s->st_ctim.tv_nsec == 0); +#endif +#endif + + uv_fs_req_cleanup(&req); + + /* Now do the uv_fs_fstat call asynchronously */ + r = uv_fs_fstat(loop, &req, file, fstat_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(fstat_cb_count == 1); + + + r = uv_fs_close(NULL, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_access) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + rmdir("test_dir"); + + loop = uv_default_loop(); + + /* File should not exist */ + r = uv_fs_access(NULL, &req, "test_file", F_OK, NULL); + ASSERT(r < 0); + ASSERT(req.result < 0); + uv_fs_req_cleanup(&req); + + /* File should not exist */ + r = uv_fs_access(loop, &req, "test_file", F_OK, access_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(access_cb_count == 1); + access_cb_count = 0; /* reset for the next test */ + + /* Create file */ + r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + /* File should exist */ + r = uv_fs_access(NULL, &req, "test_file", F_OK, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* File should exist */ + r = uv_fs_access(loop, &req, "test_file", F_OK, access_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(access_cb_count == 1); + access_cb_count = 0; /* reset for the next test */ + + /* Close file */ + r = uv_fs_close(NULL, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* Directory access */ + r = uv_fs_mkdir(NULL, &req, "test_dir", 0777, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_access(NULL, &req, "test_dir", W_OK, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + unlink("test_file"); + rmdir("test_dir"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_chmod) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + +#ifndef _WIN32 + /* Make the file write-only */ + r = uv_fs_chmod(NULL, &req, "test_file", 0200, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0200); +#endif + + /* Make the file read-only */ + r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0400); + + /* Make the file read+write with sync uv_fs_fchmod */ + r = uv_fs_fchmod(NULL, &req, file, 0600, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0600); + +#ifndef _WIN32 + /* async chmod */ + { + static int mode = 0200; + req.data = &mode; + } + r = uv_fs_chmod(loop, &req, "test_file", 0200, chmod_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(chmod_cb_count == 1); + chmod_cb_count = 0; /* reset for the next test */ +#endif + + /* async chmod */ + { + static int mode = 0400; + req.data = &mode; + } + r = uv_fs_chmod(loop, &req, "test_file", 0400, chmod_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(chmod_cb_count == 1); + + /* async fchmod */ + { + static int mode = 0600; + req.data = &mode; + } + r = uv_fs_fchmod(loop, &req, file, 0600, fchmod_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(fchmod_cb_count == 1); + + close(file); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_unlink_readonly) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, + &req, + "test_file", + O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* Make the file read-only */ + r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0400); + + /* Try to unlink the file */ + r = uv_fs_unlink(NULL, &req, "test_file", NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + uv_fs_chmod(NULL, &req, "test_file", 0600, NULL); + uv_fs_req_cleanup(&req); + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_chown) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + /* sync chown */ + r = uv_fs_chown(NULL, &req, "test_file", -1, -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* sync fchown */ + r = uv_fs_fchown(NULL, &req, file, -1, -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* async chown */ + r = uv_fs_chown(loop, &req, "test_file", -1, -1, chown_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(chown_cb_count == 1); + + /* chown to root (fail) */ + chown_cb_count = 0; + r = uv_fs_chown(loop, &req, "test_file", 0, 0, chown_root_cb); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(chown_cb_count == 1); + + /* async fchown */ + r = uv_fs_fchown(loop, &req, file, -1, -1, fchown_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(fchown_cb_count == 1); + + close(file); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_link) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync link */ + r = uv_fs_link(NULL, &req, "test_file", "test_file_link", NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(NULL, &req, "test_file_link", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* async link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(link_cb_count == 1); + + r = uv_fs_open(NULL, &req, "test_file_link2", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_readlink) { + uv_fs_t req; + + loop = uv_default_loop(); + ASSERT(0 == uv_fs_readlink(loop, &req, "no_such_file", dummy_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(dummy_cb_count == 1); + ASSERT(req.ptr == NULL); + ASSERT(req.result == UV_ENOENT); + uv_fs_req_cleanup(&req); + + ASSERT(UV_ENOENT == uv_fs_readlink(NULL, &req, "no_such_file", NULL)); + ASSERT(req.ptr == NULL); + ASSERT(req.result == UV_ENOENT); + uv_fs_req_cleanup(&req); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_realpath) { + uv_fs_t req; + + loop = uv_default_loop(); + ASSERT(0 == uv_fs_realpath(loop, &req, "no_such_file", dummy_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(dummy_cb_count == 1); + ASSERT(req.ptr == NULL); +#ifdef _WIN32 + /* + * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() + */ + if (req.result == UV_ENOSYS) { + uv_fs_req_cleanup(&req); + RETURN_SKIP("realpath is not supported on Windows XP"); + } +#endif + ASSERT(req.result == UV_ENOENT); + uv_fs_req_cleanup(&req); + + ASSERT(UV_ENOENT == uv_fs_realpath(NULL, &req, "no_such_file", NULL)); + ASSERT(req.ptr == NULL); + ASSERT(req.result == UV_ENOENT); + uv_fs_req_cleanup(&req); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_symlink) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + char test_file_abs_buf[PATHMAX]; + size_t test_file_abs_size; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink2"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2_symlink"); + test_file_abs_size = sizeof(test_file_abs_buf); +#ifdef _WIN32 + uv_cwd(test_file_abs_buf, &test_file_abs_size); + strcat(test_file_abs_buf, "\\test_file"); +#else + uv_cwd(test_file_abs_buf, &test_file_abs_size); + strcat(test_file_abs_buf, "/test_file"); +#endif + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; + uv_fs_req_cleanup(&req); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync symlink */ + r = uv_fs_symlink(NULL, &req, "test_file", "test_file_symlink", 0, NULL); +#ifdef _WIN32 + if (r < 0) { + if (r == UV_ENOTSUP) { + /* + * Windows doesn't support symlinks on older versions. + * We just pass the test and bail out early if we get ENOTSUP. + */ + return 0; + } else if (r == UV_EPERM) { + /* + * Creating a symlink is only allowed when running elevated. + * We pass the test and bail out early if we get UV_EPERM. + */ + return 0; + } + } +#endif + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(NULL, &req, "test_file_symlink", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + r = uv_fs_symlink(NULL, + &req, + "test_file_symlink", + "test_file_symlink_symlink", + 0, + NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_readlink(NULL, &req, "test_file_symlink_symlink", NULL); + ASSERT(r == 0); + ASSERT(strcmp(req.ptr, "test_file_symlink") == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_realpath(NULL, &req, "test_file_symlink_symlink", NULL); +#ifdef _WIN32 + /* + * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() + */ + if (r == UV_ENOSYS) { + uv_fs_req_cleanup(&req); + RETURN_SKIP("realpath is not supported on Windows XP"); + } +#endif + ASSERT(r == 0); +#ifdef _WIN32 + ASSERT(stricmp(req.ptr, test_file_abs_buf) == 0); +#else + ASSERT(strcmp(req.ptr, test_file_abs_buf) == 0); +#endif + uv_fs_req_cleanup(&req); + + /* async link */ + r = uv_fs_symlink(loop, + &req, + "test_file", + "test_file_symlink2", + 0, + symlink_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(symlink_cb_count == 1); + + r = uv_fs_open(NULL, &req, "test_file_symlink2", O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + r = uv_fs_symlink(NULL, + &req, + "test_file_symlink2", + "test_file_symlink2_symlink", + 0, + NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(readlink_cb_count == 1); + + r = uv_fs_realpath(loop, &req, "test_file", realpath_cb); +#ifdef _WIN32 + /* + * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() + */ + if (r == UV_ENOSYS) { + uv_fs_req_cleanup(&req); + RETURN_SKIP("realpath is not supported on Windows XP"); + } +#endif + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(realpath_cb_count == 1); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop, UV_RUN_DEFAULT); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2"); + unlink("test_file_symlink2_symlink"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_symlink_dir) { + uv_fs_t req; + int r; + char* test_dir; + uv_dirent_t dent; + static char test_dir_abs_buf[PATHMAX]; + size_t test_dir_abs_size; + + /* set-up */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + rmdir("test_dir_symlink"); + test_dir_abs_size = sizeof(test_dir_abs_buf); + + loop = uv_default_loop(); + + uv_fs_mkdir(NULL, &req, "test_dir", 0777, NULL); + uv_fs_req_cleanup(&req); + +#ifdef _WIN32 + strcpy(test_dir_abs_buf, "\\\\?\\"); + uv_cwd(test_dir_abs_buf + 4, &test_dir_abs_size); + test_dir_abs_size += 4; + strcat(test_dir_abs_buf, "\\test_dir\\"); + test_dir_abs_size += strlen("\\test_dir\\"); + test_dir = test_dir_abs_buf; +#else + uv_cwd(test_dir_abs_buf, &test_dir_abs_size); + strcat(test_dir_abs_buf, "/test_dir"); + test_dir_abs_size += strlen("/test_dir"); + test_dir = "test_dir"; +#endif + + r = uv_fs_symlink(NULL, &req, test_dir, "test_dir_symlink", + UV_FS_SYMLINK_JUNCTION, NULL); + fprintf(stderr, "r == %i\n", r); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(NULL, &req, "test_dir_symlink", NULL); + ASSERT(r == 0); + ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFDIR); + uv_fs_req_cleanup(&req); + + r = uv_fs_lstat(NULL, &req, "test_dir_symlink", NULL); + ASSERT(r == 0); + ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFLNK); +#ifdef _WIN32 + ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir + 4)); +#else + ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir)); +#endif + uv_fs_req_cleanup(&req); + + r = uv_fs_readlink(NULL, &req, "test_dir_symlink", NULL); + ASSERT(r == 0); +#ifdef _WIN32 + ASSERT(strcmp(req.ptr, test_dir + 4) == 0); +#else + ASSERT(strcmp(req.ptr, test_dir) == 0); +#endif + uv_fs_req_cleanup(&req); + + r = uv_fs_realpath(NULL, &req, "test_dir_symlink", NULL); +#ifdef _WIN32 + /* + * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() + */ + if (r == UV_ENOSYS) { + uv_fs_req_cleanup(&req); + RETURN_SKIP("realpath is not supported on Windows XP"); + } +#endif + ASSERT(r == 0); +#ifdef _WIN32 + ASSERT(strlen(req.ptr) == test_dir_abs_size - 5); + ASSERT(strnicmp(req.ptr, test_dir + 4, test_dir_abs_size - 5) == 0); +#else + ASSERT(strcmp(req.ptr, test_dir_abs_buf) == 0); +#endif + uv_fs_req_cleanup(&req); + + r = uv_fs_open(NULL, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_scandir(NULL, &scandir_req, "test_dir_symlink", 0, NULL); + ASSERT(r == 2); + ASSERT(scandir_req.result == 2); + ASSERT(scandir_req.ptr); + while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { + ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); + assert_is_file_type(dent); + } + uv_fs_req_cleanup(&scandir_req); + ASSERT(!scandir_req.ptr); + + /* unlink will remove the directory symlink */ + r = uv_fs_unlink(NULL, &req, "test_dir_symlink", NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_scandir(NULL, &scandir_req, "test_dir_symlink", 0, NULL); + ASSERT(r == UV_ENOENT); + uv_fs_req_cleanup(&scandir_req); + + r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); + ASSERT(r == 2); + ASSERT(scandir_req.result == 2); + ASSERT(scandir_req.ptr); + while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { + ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); + assert_is_file_type(dent); + } + uv_fs_req_cleanup(&scandir_req); + ASSERT(!scandir_req.ptr); + + /* clean-up */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + rmdir("test_dir_symlink"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_utime) { + utime_check_t checkme; + const char* path = "test_file"; + double atime; + double mtime; + uv_fs_t req; + int r; + + /* Setup. */ + loop = uv_default_loop(); + unlink(path); + r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + uv_fs_req_cleanup(&req); + close(r); + + atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ + + r = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(NULL, &req, path, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + check_utime(path, atime, mtime); + uv_fs_req_cleanup(&req); + + atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ + checkme.path = path; + checkme.atime = atime; + checkme.mtime = mtime; + + /* async utime */ + utime_req.data = &checkme; + r = uv_fs_utime(loop, &utime_req, path, atime, mtime, utime_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(utime_cb_count == 1); + + /* Cleanup. */ + unlink(path); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#ifdef _WIN32 +TEST_IMPL(fs_stat_root) { + int r; + uv_loop_t* loop = uv_default_loop(); + + r = uv_fs_stat(NULL, &stat_req, "\\", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(NULL, &stat_req, "..\\..\\..\\..\\..\\..\\..", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(NULL, &stat_req, "..", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(NULL, &stat_req, "..\\", NULL); + ASSERT(r == 0); + + /* stats the current directory on c: */ + r = uv_fs_stat(NULL, &stat_req, "c:", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(NULL, &stat_req, "c:\\", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(NULL, &stat_req, "\\\\?\\C:\\", NULL); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + + +TEST_IMPL(fs_futime) { + utime_check_t checkme; + const char* path = "test_file"; + double atime; + double mtime; + uv_file file; + uv_fs_t req; + int r; + + /* Setup. */ + loop = uv_default_loop(); + unlink(path); + r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + uv_fs_req_cleanup(&req); + close(r); + + atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ + + r = uv_fs_open(NULL, &req, path, O_RDWR, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + file = req.result; /* FIXME probably not how it's supposed to be used */ + uv_fs_req_cleanup(&req); + + r = uv_fs_futime(NULL, &req, file, atime, mtime, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(NULL, &req, path, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + check_utime(path, atime, mtime); + uv_fs_req_cleanup(&req); + + atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ + + checkme.atime = atime; + checkme.mtime = mtime; + checkme.path = path; + + /* async futime */ + futime_req.data = &checkme; + r = uv_fs_futime(loop, &futime_req, file, atime, mtime, futime_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(futime_cb_count == 1); + + /* Cleanup. */ + unlink(path); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_stat_missing_path) { + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + r = uv_fs_stat(NULL, &req, "non_existent_file", NULL); + ASSERT(r == UV_ENOENT); + ASSERT(req.result == UV_ENOENT); + uv_fs_req_cleanup(&req); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_scandir_empty_dir) { + const char* path; + uv_fs_t req; + uv_dirent_t dent; + int r; + + path = "./empty_dir/"; + loop = uv_default_loop(); + + uv_fs_mkdir(NULL, &req, path, 0777, NULL); + uv_fs_req_cleanup(&req); + + /* Fill the req to ensure that required fields are cleaned up */ + memset(&req, 0xdb, sizeof(req)); + + r = uv_fs_scandir(NULL, &req, path, 0, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + ASSERT(req.ptr == NULL); + ASSERT(UV_EOF == uv_fs_scandir_next(&req, &dent)); + uv_fs_req_cleanup(&req); + + r = uv_fs_scandir(loop, &scandir_req, path, 0, empty_scandir_cb); + ASSERT(r == 0); + + ASSERT(scandir_cb_count == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(scandir_cb_count == 1); + + uv_fs_rmdir(NULL, &req, path, NULL); + uv_fs_req_cleanup(&req); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_scandir_file) { + const char* path; + int r; + + path = "test/fixtures/empty_file"; + loop = uv_default_loop(); + + r = uv_fs_scandir(NULL, &scandir_req, path, 0, NULL); + ASSERT(r == UV_ENOTDIR); + uv_fs_req_cleanup(&scandir_req); + + r = uv_fs_scandir(loop, &scandir_req, path, 0, file_scandir_cb); + ASSERT(r == 0); + + ASSERT(scandir_cb_count == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(scandir_cb_count == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_open_dir) { + const char* path; + uv_fs_t req; + int r, file; + + path = "."; + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &req, path, O_RDONLY, 0, NULL); + ASSERT(r >= 0); + ASSERT(req.result >= 0); + ASSERT(req.ptr == NULL); + file = r; + uv_fs_req_cleanup(&req); + + r = uv_fs_close(NULL, &req, file, NULL); + ASSERT(r == 0); + + r = uv_fs_open(loop, &req, path, O_RDONLY, 0, open_cb_simple); + ASSERT(r == 0); + + ASSERT(open_cb_count == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(open_cb_count == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_file_open_append) { + int r; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(write_req.result >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | O_APPEND, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(write_req.result >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + printf("read = %d\n", r); + ASSERT(r == 26); + ASSERT(read_req.result == 26); + ASSERT(memcmp(buf, + "test-buffer\n\0test-buffer\n\0", + sizeof("test-buffer\n\0test-buffer\n\0") - 1) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_rename_to_existing_file) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(write_req.result >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file2", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_rename(NULL, &rename_req, "test_file", "test_file2", NULL); + ASSERT(r == 0); + ASSERT(rename_req.result == 0); + uv_fs_req_cleanup(&rename_req); + + r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(read_req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + unlink("test_file2"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_read_file_eof) { + int r; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(write_req.result >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r >= 0); + ASSERT(read_req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, + read_req.result, NULL); + ASSERT(r == 0); + ASSERT(read_req.result == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_write_multiple_bufs) { + uv_buf_t iovs[2]; + int r; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iovs[0] = uv_buf_init(test_buf, sizeof(test_buf)); + iovs[1] = uv_buf_init(test_buf2, sizeof(test_buf2)); + r = uv_fs_write(NULL, &write_req, open_req1.result, iovs, 2, 0, NULL); + ASSERT(r >= 0); + ASSERT(write_req.result >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + memset(buf2, 0, sizeof(buf2)); + /* Read the strings back to separate buffers. */ + iovs[0] = uv_buf_init(buf, sizeof(test_buf)); + iovs[1] = uv_buf_init(buf2, sizeof(test_buf2)); + r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, 2, 0, NULL); + ASSERT(r >= 0); + ASSERT(read_req.result >= 0); + ASSERT(strcmp(buf, test_buf) == 0); + ASSERT(strcmp(buf2, test_buf2) == 0); + uv_fs_req_cleanup(&read_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, + read_req.result, NULL); + ASSERT(r == 0); + ASSERT(read_req.result == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_write_alotof_bufs) { + const size_t iovcount = 54321; + uv_buf_t* iovs; + char* buffer; + size_t index; + int r; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + iovs = malloc(sizeof(*iovs) * iovcount); + ASSERT(iovs != NULL); + + r = uv_fs_open(NULL, + &open_req1, + "test_file", + O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + for (index = 0; index < iovcount; ++index) + iovs[index] = uv_buf_init(test_buf, sizeof(test_buf)); + + r = uv_fs_write(NULL, + &write_req, + open_req1.result, + iovs, + iovcount, + -1, + NULL); + ASSERT(r >= 0); + ASSERT((size_t)write_req.result == sizeof(test_buf) * iovcount); + uv_fs_req_cleanup(&write_req); + + /* Read the strings back to separate buffers. */ + buffer = malloc(sizeof(test_buf) * iovcount); + ASSERT(buffer != NULL); + + for (index = 0; index < iovcount; ++index) + iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), + sizeof(test_buf)); + + r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, iovcount, 0, NULL); + ASSERT(r >= 0); + ASSERT((size_t)read_req.result == sizeof(test_buf) * iovcount); + + for (index = 0; index < iovcount; ++index) + ASSERT(strncmp(buffer + index * sizeof(test_buf), + test_buf, + sizeof(test_buf)) == 0); + + uv_fs_req_cleanup(&read_req); + free(buffer); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, + &read_req, + open_req1.result, + &iov, + 1, + read_req.result, + NULL); + ASSERT(r == 0); + ASSERT(read_req.result == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + free(iovs); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_write_alotof_bufs_with_offset) { + const size_t iovcount = 54321; + uv_buf_t* iovs; + char* buffer; + size_t index; + int r; + int64_t offset; + char* filler = "0123456789"; + int filler_len = strlen(filler); + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + iovs = malloc(sizeof(*iovs) * iovcount); + ASSERT(iovs != NULL); + + r = uv_fs_open(NULL, + &open_req1, + "test_file", + O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(filler, filler_len); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == filler_len); + ASSERT(write_req.result == filler_len); + uv_fs_req_cleanup(&write_req); + offset = (int64_t)r; + + for (index = 0; index < iovcount; ++index) + iovs[index] = uv_buf_init(test_buf, sizeof(test_buf)); + + r = uv_fs_write(NULL, + &write_req, + open_req1.result, + iovs, + iovcount, + offset, + NULL); + ASSERT(r >= 0); + ASSERT((size_t)write_req.result == sizeof(test_buf) * iovcount); + uv_fs_req_cleanup(&write_req); + + /* Read the strings back to separate buffers. */ + buffer = malloc(sizeof(test_buf) * iovcount); + ASSERT(buffer != NULL); + + for (index = 0; index < iovcount; ++index) + iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), + sizeof(test_buf)); + + r = uv_fs_read(NULL, &read_req, open_req1.result, + iovs, iovcount, offset, NULL); + ASSERT(r >= 0); + ASSERT(read_req.result == sizeof(test_buf) * iovcount); + + for (index = 0; index < iovcount; ++index) + ASSERT(strncmp(buffer + index * sizeof(test_buf), + test_buf, + sizeof(test_buf)) == 0); + + uv_fs_req_cleanup(&read_req); + free(buffer); + + r = uv_fs_stat(NULL, &stat_req, "test_file", NULL); + ASSERT(r == 0); + ASSERT((int64_t)((uv_stat_t*)stat_req.ptr)->st_size == + offset + (int64_t)(iovcount * sizeof(test_buf))); + uv_fs_req_cleanup(&stat_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, + &read_req, + open_req1.result, + &iov, + 1, + read_req.result + offset, + NULL); + ASSERT(r == 0); + ASSERT(read_req.result == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + free(iovs); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_read_write_null_arguments) { + int r; + + r = uv_fs_read(NULL, NULL, 0, NULL, 0, -1, NULL); + ASSERT(r == UV_EINVAL); + + r = uv_fs_write(NULL, NULL, 0, NULL, 0, -1, NULL); + ASSERT(r == UV_EINVAL); + + iov = uv_buf_init(NULL, 0); + r = uv_fs_read(NULL, NULL, 0, &iov, 0, -1, NULL); + ASSERT(r == UV_EINVAL); + + iov = uv_buf_init(NULL, 0); + r = uv_fs_write(NULL, NULL, 0, &iov, 0, -1, NULL); + ASSERT(r == UV_EINVAL); + + return 0; +} diff --git a/3rdparty/libuv/test/test-get-currentexe.c b/3rdparty/libuv/test/test-get-currentexe.c new file mode 100644 index 00000000000..0e9d6965402 --- /dev/null +++ b/3rdparty/libuv/test/test-get-currentexe.c @@ -0,0 +1,86 @@ +/* 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 <string.h> + +#define PATHMAX 1024 +extern char executable_path[]; + +TEST_IMPL(get_currentexe) { + char buffer[PATHMAX]; + size_t size; + char* match; + char* path; + int r; + + size = sizeof(buffer) / sizeof(buffer[0]); + r = uv_exepath(buffer, &size); + ASSERT(!r); + + /* uv_exepath can return an absolute path on darwin, so if the test runner + * was run with a relative prefix of "./", we need to strip that prefix off + * executable_path or we'll fail. */ + if (executable_path[0] == '.' && executable_path[1] == '/') { + path = executable_path + 2; + } else { + path = executable_path; + } + + match = strstr(buffer, path); + /* Verify that the path returned from uv_exepath is a subdirectory of + * executable_path. + */ + ASSERT(match && !strcmp(match, path)); + ASSERT(size == strlen(buffer)); + + /* Negative tests */ + size = sizeof(buffer) / sizeof(buffer[0]); + r = uv_exepath(NULL, &size); + ASSERT(r == UV_EINVAL); + + r = uv_exepath(buffer, NULL); + ASSERT(r == UV_EINVAL); + + size = 0; + r = uv_exepath(buffer, &size); + ASSERT(r == UV_EINVAL); + + memset(buffer, -1, sizeof(buffer)); + + size = 1; + r = uv_exepath(buffer, &size); + ASSERT(r == 0); + ASSERT(size == 0); + ASSERT(buffer[0] == '\0'); + + memset(buffer, -1, sizeof(buffer)); + + size = 2; + r = uv_exepath(buffer, &size); + ASSERT(r == 0); + ASSERT(size == 1); + ASSERT(buffer[0] != '\0'); + ASSERT(buffer[1] == '\0'); + + return 0; +} diff --git a/3rdparty/libuv/test/test-get-loadavg.c b/3rdparty/libuv/test/test-get-loadavg.c new file mode 100644 index 00000000000..4762e47576d --- /dev/null +++ b/3rdparty/libuv/test/test-get-loadavg.c @@ -0,0 +1,35 @@ +/* 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" + +TEST_IMPL(get_loadavg) { + + double avg[3] = {-1, -1, -1}; + uv_loadavg(avg); + + ASSERT(avg[0] >= 0); + ASSERT(avg[1] >= 0); + ASSERT(avg[2] >= 0); + + return 0; +} diff --git a/3rdparty/libuv/test/test-get-memory.c b/3rdparty/libuv/test/test-get-memory.c new file mode 100644 index 00000000000..2396939bcb1 --- /dev/null +++ b/3rdparty/libuv/test/test-get-memory.c @@ -0,0 +1,38 @@ +/* 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" + +TEST_IMPL(get_memory) { + uint64_t free_mem = uv_get_free_memory(); + uint64_t total_mem = uv_get_total_memory(); + + printf("free_mem=%llu, total_mem=%llu\n", + (unsigned long long) free_mem, + (unsigned long long) total_mem); + + ASSERT(free_mem > 0); + ASSERT(total_mem > 0); + ASSERT(total_mem > free_mem); + + return 0; +} diff --git a/3rdparty/libuv/test/test-getaddrinfo.c b/3rdparty/libuv/test/test-getaddrinfo.c new file mode 100644 index 00000000000..6b644a8d442 --- /dev/null +++ b/3rdparty/libuv/test/test-getaddrinfo.c @@ -0,0 +1,184 @@ +/* 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 <stdlib.h> + +#define CONCURRENT_COUNT 10 + +static const char* name = "localhost"; + +static int getaddrinfo_cbs = 0; + +/* data used for running multiple calls concurrently */ +static uv_getaddrinfo_t* getaddrinfo_handle; +static uv_getaddrinfo_t getaddrinfo_handles[CONCURRENT_COUNT]; +static int callback_counts[CONCURRENT_COUNT]; +static int fail_cb_called; + + +static void getaddrinfo_fail_cb(uv_getaddrinfo_t* req, + int status, + struct addrinfo* res) { + ASSERT(fail_cb_called == 0); + ASSERT(status < 0); + ASSERT(res == NULL); + uv_freeaddrinfo(res); /* Should not crash. */ + fail_cb_called++; +} + + +static void getaddrinfo_basic_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res) { + ASSERT(handle == getaddrinfo_handle); + getaddrinfo_cbs++; + free(handle); + uv_freeaddrinfo(res); +} + + +static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res) { + int i; + int* data = (int*)handle->data; + + for (i = 0; i < CONCURRENT_COUNT; i++) { + if (&getaddrinfo_handles[i] == handle) { + ASSERT(i == *data); + + callback_counts[i]++; + break; + } + } + ASSERT (i < CONCURRENT_COUNT); + + free(data); + uv_freeaddrinfo(res); + + getaddrinfo_cbs++; +} + + +TEST_IMPL(getaddrinfo_fail) { + uv_getaddrinfo_t req; + + /* Use a FQDN by ending in a period */ + ASSERT(0 == uv_getaddrinfo(uv_default_loop(), + &req, + getaddrinfo_fail_cb, + "xyzzy.xyzzy.xyzzy.", + NULL, + NULL)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(fail_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getaddrinfo_fail_sync) { + uv_getaddrinfo_t req; + + /* Use a FQDN by ending in a period */ + ASSERT(0 > uv_getaddrinfo(uv_default_loop(), + &req, + NULL, + "xyzzy.xyzzy.xyzzy.", + NULL, + NULL)); + uv_freeaddrinfo(req.addrinfo); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getaddrinfo_basic) { + int r; + getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); + + r = uv_getaddrinfo(uv_default_loop(), + getaddrinfo_handle, + &getaddrinfo_basic_cb, + name, + NULL, + NULL); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(getaddrinfo_cbs == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getaddrinfo_basic_sync) { + uv_getaddrinfo_t req; + + ASSERT(0 == uv_getaddrinfo(uv_default_loop(), + &req, + NULL, + name, + NULL, + NULL)); + uv_freeaddrinfo(req.addrinfo); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getaddrinfo_concurrent) { + int i, r; + int* data; + + for (i = 0; i < CONCURRENT_COUNT; i++) { + callback_counts[i] = 0; + + data = (int*)malloc(sizeof(int)); + ASSERT(data != NULL); + *data = i; + getaddrinfo_handles[i].data = data; + + r = uv_getaddrinfo(uv_default_loop(), + &getaddrinfo_handles[i], + &getaddrinfo_cuncurrent_cb, + name, + NULL, + NULL); + ASSERT(r == 0); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + for (i = 0; i < CONCURRENT_COUNT; i++) { + ASSERT(callback_counts[i] == 1); + } + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-getnameinfo.c b/3rdparty/libuv/test/test-getnameinfo.c new file mode 100644 index 00000000000..b1391616d13 --- /dev/null +++ b/3rdparty/libuv/test/test-getnameinfo.c @@ -0,0 +1,101 @@ +/* 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 <string.h> + + +static const char* address_ip4 = "127.0.0.1"; +static const char* address_ip6 = "::1"; +static const int port = 80; + +static struct sockaddr_in addr4; +static struct sockaddr_in6 addr6; +static uv_getnameinfo_t req; + +static void getnameinfo_req(uv_getnameinfo_t* handle, + int status, + const char* hostname, + const char* service) { + ASSERT(handle != NULL); + ASSERT(status == 0); + ASSERT(hostname != NULL); + ASSERT(service != NULL); +} + + +TEST_IMPL(getnameinfo_basic_ip4) { + int r; + + r = uv_ip4_addr(address_ip4, port, &addr4); + ASSERT(r == 0); + + r = uv_getnameinfo(uv_default_loop(), + &req, + &getnameinfo_req, + (const struct sockaddr*)&addr4, + 0); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getnameinfo_basic_ip4_sync) { + ASSERT(0 == uv_ip4_addr(address_ip4, port, &addr4)); + + ASSERT(0 == uv_getnameinfo(uv_default_loop(), + &req, + NULL, + (const struct sockaddr*)&addr4, + 0)); + ASSERT(req.host[0] != '\0'); + ASSERT(req.service[0] != '\0'); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getnameinfo_basic_ip6) { + int r; + + r = uv_ip6_addr(address_ip6, port, &addr6); + ASSERT(r == 0); + + r = uv_getnameinfo(uv_default_loop(), + &req, + &getnameinfo_req, + (const struct sockaddr*)&addr6, + 0); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-getsockname.c b/3rdparty/libuv/test/test-getsockname.c new file mode 100644 index 00000000000..565c17fe50b --- /dev/null +++ b/3rdparty/libuv/test/test-getsockname.c @@ -0,0 +1,361 @@ +/* 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 <string.h> + +static const int server_port = TEST_PORT; +/* Will be updated right after making the uv_connect_call */ +static int connect_port = -1; + +static int getsocknamecount = 0; +static int getpeernamecount = 0; + +static uv_loop_t* loop; +static uv_tcp_t tcp; +static uv_udp_t udp; +static uv_connect_t connect_req; +static uv_tcp_t tcpServer; +static uv_udp_t udpServer; +static uv_udp_send_t send_req; + + +static void alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + + +static void on_close(uv_handle_t* peer) { + free(peer); + uv_close((uv_handle_t*)&tcpServer, NULL); +} + + +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, on_close); + free(req); +} + + +static void after_read(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + uv_shutdown_t* req; + int r; + + if (buf->base) { + free(buf->base); + } + + req = (uv_shutdown_t*) malloc(sizeof *req); + r = uv_shutdown(req, handle, after_shutdown); + ASSERT(r == 0); +} + + +static void check_sockname(struct sockaddr* addr, const char* compare_ip, + int compare_port, const char* context) { + struct sockaddr_in check_addr = *(struct sockaddr_in*) addr; + struct sockaddr_in compare_addr; + char check_ip[17]; + int r; + + ASSERT(0 == uv_ip4_addr(compare_ip, compare_port, &compare_addr)); + + /* Both addresses should be ipv4 */ + ASSERT(check_addr.sin_family == AF_INET); + ASSERT(compare_addr.sin_family == AF_INET); + + /* Check if the ip matches */ + ASSERT(memcmp(&check_addr.sin_addr, + &compare_addr.sin_addr, + sizeof compare_addr.sin_addr) == 0); + + /* Check if the port matches. If port == 0 anything goes. */ + ASSERT(compare_port == 0 || check_addr.sin_port == compare_addr.sin_port); + + r = uv_ip4_name(&check_addr, (char*) check_ip, sizeof check_ip); + ASSERT(r == 0); + + printf("%s: %s:%d\n", context, check_ip, ntohs(check_addr.sin_port)); +} + + +static void on_connection(uv_stream_t* server, int status) { + struct sockaddr sockname, peername; + int namelen; + uv_tcp_t* handle; + int r; + + if (status != 0) { + fprintf(stderr, "Connect error %s\n", uv_err_name(status)); + } + ASSERT(status == 0); + + handle = malloc(sizeof(*handle)); + ASSERT(handle != NULL); + + r = uv_tcp_init(loop, handle); + ASSERT(r == 0); + + /* associate server with stream */ + handle->data = server; + + r = uv_accept(server, (uv_stream_t*)handle); + ASSERT(r == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname(handle, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket"); + getsocknamecount++; + + namelen = sizeof peername; + r = uv_tcp_getpeername(handle, &peername, &namelen); + ASSERT(r == 0); + check_sockname(&peername, "127.0.0.1", connect_port, "accepted socket peer"); + getpeernamecount++; + + r = uv_read_start((uv_stream_t*)handle, alloc, after_read); + ASSERT(r == 0); +} + + +static void on_connect(uv_connect_t* req, int status) { + struct sockaddr sockname, peername; + int r, namelen; + + ASSERT(status == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "127.0.0.1", 0, "connected socket"); + getsocknamecount++; + + namelen = sizeof peername; + r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen); + ASSERT(r == 0); + check_sockname(&peername, "127.0.0.1", server_port, "connected socket peer"); + getpeernamecount++; + + uv_close((uv_handle_t*)&tcp, NULL); +} + + +static int tcp_listener(void) { + struct sockaddr_in addr; + struct sockaddr sockname, peername; + int namelen; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", server_port, &addr)); + + r = uv_tcp_init(loop, &tcpServer); + if (r) { + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0); + if (r) { + fprintf(stderr, "Bind error\n"); + return 1; + } + + r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); + if (r) { + fprintf(stderr, "Listen error\n"); + return 1; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof sockname; + r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", server_port, "server socket"); + getsocknamecount++; + + namelen = sizeof sockname; + r = uv_tcp_getpeername(&tcpServer, &peername, &namelen); + ASSERT(r == UV_ENOTCONN); + getpeernamecount++; + + return 0; +} + + +static void tcp_connector(void) { + struct sockaddr_in server_addr; + struct sockaddr sockname; + int r, namelen; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", server_port, &server_addr)); + + r = uv_tcp_init(loop, &tcp); + tcp.data = &connect_req; + ASSERT(!r); + + r = uv_tcp_connect(&connect_req, + &tcp, + (const struct sockaddr*) &server_addr, + on_connect); + ASSERT(!r); + + /* Fetch the actual port used by the connecting socket. */ + namelen = sizeof sockname; + r = uv_tcp_getsockname(&tcp, &sockname, &namelen); + ASSERT(!r); + ASSERT(sockname.sa_family == AF_INET); + connect_port = ntohs(((struct sockaddr_in*) &sockname)->sin_port); + ASSERT(connect_port > 0); +} + + +static void udp_recv(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + struct sockaddr sockname; + int namelen; + int r; + + ASSERT(nread >= 0); + free(buf->base); + + if (nread == 0) { + return; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof(sockname); + r = uv_udp_getsockname(&udp, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket"); + getsocknamecount++; + + uv_close((uv_handle_t*) &udp, NULL); + uv_close((uv_handle_t*) handle, NULL); +} + + +static void udp_send(uv_udp_send_t* req, int status) { + +} + + +static int udp_listener(void) { + struct sockaddr_in addr; + struct sockaddr sockname; + int namelen; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", server_port, &addr)); + + r = uv_udp_init(loop, &udpServer); + if (r) { + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_udp_bind(&udpServer, (const struct sockaddr*) &addr, 0); + if (r) { + fprintf(stderr, "Bind error\n"); + return 1; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof sockname; + r = uv_udp_getsockname(&udpServer, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket"); + getsocknamecount++; + + r = uv_udp_recv_start(&udpServer, alloc, udp_recv); + ASSERT(r == 0); + + return 0; +} + + +static void udp_sender(void) { + struct sockaddr_in server_addr; + uv_buf_t buf; + int r; + + r = uv_udp_init(loop, &udp); + ASSERT(!r); + + buf = uv_buf_init("PING", 4); + ASSERT(0 == uv_ip4_addr("127.0.0.1", server_port, &server_addr)); + + r = uv_udp_send(&send_req, + &udp, + &buf, + 1, + (const struct sockaddr*) &server_addr, + udp_send); + ASSERT(!r); +} + + +TEST_IMPL(getsockname_tcp) { + loop = uv_default_loop(); + + if (tcp_listener()) + return 1; + + tcp_connector(); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(getsocknamecount == 3); + ASSERT(getpeernamecount == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(getsockname_udp) { + loop = uv_default_loop(); + + if (udp_listener()) + return 1; + + udp_sender(); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(getsocknamecount == 2); + + ASSERT(udp.send_queue_size == 0); + ASSERT(udpServer.send_queue_size == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-handle-fileno.c b/3rdparty/libuv/test/test-handle-fileno.c new file mode 100644 index 00000000000..3fe933adebd --- /dev/null +++ b/3rdparty/libuv/test/test-handle-fileno.c @@ -0,0 +1,121 @@ +/* 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 int get_tty_fd(void) { + /* Make sure we have an FD that refers to a tty */ +#ifdef _WIN32 + HANDLE handle; + handle = CreateFileA("conout$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (handle == INVALID_HANDLE_VALUE) + return -1; + return _open_osfhandle((intptr_t) handle, 0); +#else /* unix */ + return open("/dev/tty", O_RDONLY, 0); +#endif +} + + +TEST_IMPL(handle_fileno) { + int r; + int tty_fd; + struct sockaddr_in addr; + uv_os_fd_t fd; + uv_tcp_t tcp; + uv_udp_t udp; + uv_pipe_t pipe; + uv_tty_t tty; + uv_idle_t idle; + uv_loop_t* loop; + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_idle_init(loop, &idle); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &idle, &fd); + ASSERT(r == UV_EINVAL); + uv_close((uv_handle_t*) &idle, NULL); + + r = uv_tcp_init(loop, &tcp); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &tcp, &fd); + ASSERT(r == UV_EBADF); + r = uv_tcp_bind(&tcp, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &tcp, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &tcp, NULL); + r = uv_fileno((uv_handle_t*) &tcp, &fd); + ASSERT(r == UV_EBADF); + + r = uv_udp_init(loop, &udp); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &udp, &fd); + ASSERT(r == UV_EBADF); + r = uv_udp_bind(&udp, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &udp, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &udp, NULL); + r = uv_fileno((uv_handle_t*) &udp, &fd); + ASSERT(r == UV_EBADF); + + r = uv_pipe_init(loop, &pipe, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &pipe, &fd); + ASSERT(r == UV_EBADF); + r = uv_pipe_bind(&pipe, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &pipe, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &pipe, NULL); + r = uv_fileno((uv_handle_t*) &pipe, &fd); + ASSERT(r == UV_EBADF); + + tty_fd = get_tty_fd(); + if (tty_fd < 0) { + fprintf(stderr, "Cannot open a TTY fd"); + fflush(stderr); + } else { + r = uv_tty_init(loop, &tty, tty_fd, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &tty, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &tty, NULL); + r = uv_fileno((uv_handle_t*) &tty, &fd); + ASSERT(r == UV_EBADF); + } + + uv_run(loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-homedir.c b/3rdparty/libuv/test/test-homedir.c new file mode 100644 index 00000000000..cbc47566c55 --- /dev/null +++ b/3rdparty/libuv/test/test-homedir.c @@ -0,0 +1,49 @@ +#include "uv.h" +#include "task.h" +#include <string.h> + +#define PATHMAX 1024 +#define SMALLPATH 1 + +TEST_IMPL(homedir) { + char homedir[PATHMAX]; + size_t len; + char last; + int r; + + /* Test the normal case */ + len = sizeof homedir; + homedir[0] = '\0'; + ASSERT(strlen(homedir) == 0); + r = uv_os_homedir(homedir, &len); + ASSERT(r == 0); + ASSERT(strlen(homedir) == len); + ASSERT(len > 0); + ASSERT(homedir[len] == '\0'); + + if (len > 1) { + last = homedir[len - 1]; +#ifdef _WIN32 + ASSERT(last != '\\'); +#else + ASSERT(last != '/'); +#endif + } + + /* Test the case where the buffer is too small */ + len = SMALLPATH; + r = uv_os_homedir(homedir, &len); + ASSERT(r == UV_ENOBUFS); + ASSERT(len > SMALLPATH); + + /* Test invalid inputs */ + r = uv_os_homedir(NULL, &len); + ASSERT(r == UV_EINVAL); + r = uv_os_homedir(homedir, NULL); + ASSERT(r == UV_EINVAL); + len = 0; + r = uv_os_homedir(homedir, &len); + ASSERT(r == UV_EINVAL); + + return 0; +} diff --git a/3rdparty/libuv/test/test-hrtime.c b/3rdparty/libuv/test/test-hrtime.c new file mode 100644 index 00000000000..72a4d4b181d --- /dev/null +++ b/3rdparty/libuv/test/test-hrtime.c @@ -0,0 +1,54 @@ +/* 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" + +#ifndef MILLISEC +# define MILLISEC 1000 +#endif + +#ifndef NANOSEC +# define NANOSEC ((uint64_t) 1e9) +#endif + + +TEST_IMPL(hrtime) { + uint64_t a, b, diff; + int i = 75; + while (i > 0) { + a = uv_hrtime(); + uv_sleep(45); + b = uv_hrtime(); + + diff = b - a; + + /* printf("i= %d diff = %llu\n", i, (unsigned long long int) diff); */ + + /* The windows Sleep() function has only a resolution of 10-20 ms. */ + /* Check that the difference between the two hrtime values is somewhat in */ + /* the range we expect it to be. */ + ASSERT(diff > (uint64_t) 25 * NANOSEC / MILLISEC); + ASSERT(diff < (uint64_t) 80 * NANOSEC / MILLISEC); + --i; + } + return 0; +} diff --git a/3rdparty/libuv/test/test-idle.c b/3rdparty/libuv/test/test-idle.c new file mode 100644 index 00000000000..f49d1964827 --- /dev/null +++ b/3rdparty/libuv/test/test-idle.c @@ -0,0 +1,99 @@ +/* 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_idle_t idle_handle; +static uv_check_t check_handle; +static uv_timer_t timer_handle; + +static int idle_cb_called = 0; +static int check_cb_called = 0; +static int timer_cb_called = 0; +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer_handle); + + uv_close((uv_handle_t*) &idle_handle, close_cb); + uv_close((uv_handle_t*) &check_handle, close_cb); + uv_close((uv_handle_t*) &timer_handle, close_cb); + + timer_cb_called++; + fprintf(stderr, "timer_cb %d\n", timer_cb_called); + fflush(stderr); +} + + +static void idle_cb(uv_idle_t* handle) { + ASSERT(handle == &idle_handle); + + idle_cb_called++; + fprintf(stderr, "idle_cb %d\n", idle_cb_called); + fflush(stderr); +} + + +static void check_cb(uv_check_t* handle) { + ASSERT(handle == &check_handle); + + check_cb_called++; + fprintf(stderr, "check_cb %d\n", check_cb_called); + fflush(stderr); +} + + +TEST_IMPL(idle_starvation) { + int r; + + r = uv_idle_init(uv_default_loop(), &idle_handle); + ASSERT(r == 0); + r = uv_idle_start(&idle_handle, idle_cb); + ASSERT(r == 0); + + r = uv_check_init(uv_default_loop(), &check_handle); + ASSERT(r == 0); + r = uv_check_start(&check_handle, check_cb); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer_handle); + ASSERT(r == 0); + r = uv_timer_start(&timer_handle, timer_cb, 50, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(idle_cb_called > 0); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-ip4-addr.c b/3rdparty/libuv/test/test-ip4-addr.c new file mode 100644 index 00000000000..3d6e0cf286a --- /dev/null +++ b/3rdparty/libuv/test/test-ip4-addr.c @@ -0,0 +1,46 @@ +/* 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 <string.h> + + +TEST_IMPL(ip4_addr) { + + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT(0 == uv_ip4_addr("255.255.255.255", TEST_PORT, &addr)); + ASSERT(UV_EINVAL == uv_ip4_addr("255.255.255*000", TEST_PORT, &addr)); + ASSERT(UV_EINVAL == uv_ip4_addr("255.255.255.256", TEST_PORT, &addr)); + ASSERT(UV_EINVAL == uv_ip4_addr("2555.0.0.0", TEST_PORT, &addr)); + ASSERT(UV_EINVAL == uv_ip4_addr("255", TEST_PORT, &addr)); + + /* for broken address family */ + ASSERT(UV_EAFNOSUPPORT == uv_inet_pton(42, "127.0.0.1", + &addr.sin_addr.s_addr)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-ip6-addr.c b/3rdparty/libuv/test/test-ip6-addr.c new file mode 100644 index 00000000000..869b099e0fc --- /dev/null +++ b/3rdparty/libuv/test/test-ip6-addr.c @@ -0,0 +1,141 @@ +/* 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 <string.h> + +#ifdef __linux__ +# include <sys/socket.h> +# include <net/if.h> +#endif + + +TEST_IMPL(ip6_addr_link_local) { + char string_address[INET6_ADDRSTRLEN]; + uv_interface_address_t* addresses; + uv_interface_address_t* address; + struct sockaddr_in6 addr; + unsigned int iface_index; + const char* device_name; + /* 40 bytes address, 16 bytes device name, plus reserve. */ + char scoped_addr[128]; + int count; + int ix; + + ASSERT(0 == uv_interface_addresses(&addresses, &count)); + + for (ix = 0; ix < count; ix++) { + address = addresses + ix; + + if (address->address.address6.sin6_family != AF_INET6) + continue; + + ASSERT(0 == uv_inet_ntop(AF_INET6, + &address->address.address6.sin6_addr, + string_address, + sizeof(string_address))); + + /* Skip addresses that are not link-local. */ + if (strncmp(string_address, "fe80::", 6) != 0) + continue; + + iface_index = address->address.address6.sin6_scope_id; + device_name = address->name; + +#ifdef _WIN32 + snprintf(scoped_addr, + sizeof(scoped_addr), + "%s%%%d", + string_address, + iface_index); +#else + snprintf(scoped_addr, + sizeof(scoped_addr), + "%s%%%s", + string_address, + device_name); +#endif + + fprintf(stderr, "Testing link-local address %s " + "(iface_index: 0x%02x, device_name: %s)\n", + scoped_addr, + iface_index, + device_name); + fflush(stderr); + + ASSERT(0 == uv_ip6_addr(scoped_addr, TEST_PORT, &addr)); + fprintf(stderr, "Got scope_id 0x%02x\n", addr.sin6_scope_id); + fflush(stderr); + ASSERT(iface_index == addr.sin6_scope_id); + } + + uv_free_interface_addresses(addresses, count); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#define GOOD_ADDR_LIST(X) \ + X("::") \ + X("::1") \ + X("fe80::1") \ + X("fe80::") \ + X("fe80::2acf:daff:fedd:342a") \ + X("fe80:0:0:0:2acf:daff:fedd:342a") \ + X("fe80:0:0:0:2acf:daff:1.2.3.4") \ + X("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") \ + +#define BAD_ADDR_LIST(X) \ + X(":::1") \ + X("abcde::1") \ + X("fe80:0:0:0:2acf:daff:fedd:342a:5678") \ + X("fe80:0:0:0:2acf:daff:abcd:1.2.3.4") \ + X("fe80:0:0:2acf:daff:1.2.3.4.5") \ + X("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255.255") \ + +#define TEST_GOOD(ADDR) \ + ASSERT(0 == uv_inet_pton(AF_INET6, ADDR, &addr)); \ + ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ + ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ + ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ + +#define TEST_BAD(ADDR) \ + ASSERT(0 != uv_inet_pton(AF_INET6, ADDR, &addr)); \ + ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ + ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ + ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ + +TEST_IMPL(ip6_pton) { + struct in6_addr addr; + + GOOD_ADDR_LIST(TEST_GOOD) + BAD_ADDR_LIST(TEST_BAD) + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#undef GOOD_ADDR_LIST +#undef BAD_ADDR_LIST diff --git a/3rdparty/libuv/test/test-ipc-send-recv.c b/3rdparty/libuv/test/test-ipc-send-recv.c new file mode 100644 index 00000000000..c445483fa08 --- /dev/null +++ b/3rdparty/libuv/test/test-ipc-send-recv.c @@ -0,0 +1,411 @@ +/* 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 <string.h> + +/* See test-ipc.ctx */ +void spawn_helper(uv_pipe_t* channel, + uv_process_t* process, + const char* helper); + +void ipc_send_recv_helper_threadproc(void* arg); + +union handles { + uv_handle_t handle; + uv_stream_t stream; + uv_pipe_t pipe; + uv_tcp_t tcp; + uv_tty_t tty; +}; + +struct test_ctx { + uv_pipe_t channel; + uv_connect_t connect_req; + uv_write_t write_req; + uv_write_t write_req2; + uv_handle_type expected_type; + union handles send; + union handles send2; + union handles recv; + union handles recv2; +}; + +struct echo_ctx { + uv_pipe_t listen; + uv_pipe_t channel; + uv_write_t write_req; + uv_write_t write_req2; + uv_handle_type expected_type; + union handles recv; + union handles recv2; +}; + +static struct test_ctx ctx; +static struct echo_ctx ctx2; + +/* Used in write2_cb to decide if we need to cleanup or not */ +static int is_child_process; +static int is_in_process; +static int read_cb_called; +static int recv_cb_called; +static int write2_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + /* we're not actually reading anything so a small buffer is okay */ + static char slab[8]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void recv_cb(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + uv_handle_type pending; + uv_pipe_t* pipe; + int r; + union handles* recv; + + if (++recv_cb_called == 1) { + recv = &ctx.recv; + } else { + recv = &ctx.recv2; + } + + pipe = (uv_pipe_t*) handle; + ASSERT(pipe == &ctx.channel); + + /* Depending on the OS, the final recv_cb can be called after the child + * process has terminated which can result in nread being UV_EOF instead of + * the number of bytes read. Since the other end of the pipe has closed this + * UV_EOF is an acceptable value. */ + if (nread == UV_EOF) { + /* UV_EOF is only acceptable for the final recv_cb call */ + ASSERT(recv_cb_called == 2); + } else { + ASSERT(nread >= 0); + ASSERT(1 == uv_pipe_pending_count(pipe)); + + pending = uv_pipe_pending_type(pipe); + ASSERT(pending == ctx.expected_type); + + if (pending == UV_NAMED_PIPE) + r = uv_pipe_init(ctx.channel.loop, &recv->pipe, 0); + else if (pending == UV_TCP) + r = uv_tcp_init(ctx.channel.loop, &recv->tcp); + else + abort(); + ASSERT(r == 0); + + r = uv_accept(handle, &recv->stream); + ASSERT(r == 0); + } + + /* Close after two writes received */ + if (recv_cb_called == 2) { + uv_close((uv_handle_t*)&ctx.channel, NULL); + } +} + +static void connect_cb(uv_connect_t* req, int status) { + int r; + uv_buf_t buf; + + ASSERT(req == &ctx.connect_req); + ASSERT(status == 0); + + buf = uv_buf_init(".", 1); + r = uv_write2(&ctx.write_req, + (uv_stream_t*)&ctx.channel, + &buf, 1, + &ctx.send.stream, + NULL); + ASSERT(r == 0); + + /* Perform two writes to the same pipe to make sure that on Windows we are + * not running into issue 505: + * https://github.com/libuv/libuv/issues/505 */ + buf = uv_buf_init(".", 1); + r = uv_write2(&ctx.write_req2, + (uv_stream_t*)&ctx.channel, + &buf, 1, + &ctx.send2.stream, + NULL); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&ctx.channel, alloc_cb, recv_cb); + ASSERT(r == 0); +} + +static int run_test(int inprocess) { + uv_process_t process; + uv_thread_t tid; + int r; + + if (inprocess) { + r = uv_thread_create(&tid, ipc_send_recv_helper_threadproc, (void *) 42); + ASSERT(r == 0); + + uv_sleep(1000); + + r = uv_pipe_init(uv_default_loop(), &ctx.channel, 1); + ASSERT(r == 0); + + uv_pipe_connect(&ctx.connect_req, &ctx.channel, TEST_PIPENAME_3, connect_cb); + } else { + spawn_helper(&ctx.channel, &process, "ipc_send_recv_helper"); + + connect_cb(&ctx.connect_req, 0); + } + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(recv_cb_called == 2); + + if (inprocess) { + r = uv_thread_join(&tid); + ASSERT(r == 0); + } + + return 0; +} + +static int run_ipc_send_recv_pipe(int inprocess) { + int r; + + ctx.expected_type = UV_NAMED_PIPE; + + r = uv_pipe_init(uv_default_loop(), &ctx.send.pipe, 1); + ASSERT(r == 0); + + r = uv_pipe_bind(&ctx.send.pipe, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &ctx.send2.pipe, 1); + ASSERT(r == 0); + + r = uv_pipe_bind(&ctx.send2.pipe, TEST_PIPENAME_2); + ASSERT(r == 0); + + r = run_test(inprocess); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(ipc_send_recv_pipe) { + return run_ipc_send_recv_pipe(0); +} + +TEST_IMPL(ipc_send_recv_pipe_inprocess) { + return run_ipc_send_recv_pipe(1); +} + +static int run_ipc_send_recv_tcp(int inprocess) { + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ctx.expected_type = UV_TCP; + + r = uv_tcp_init(uv_default_loop(), &ctx.send.tcp); + ASSERT(r == 0); + + r = uv_tcp_init(uv_default_loop(), &ctx.send2.tcp); + ASSERT(r == 0); + + r = uv_tcp_bind(&ctx.send.tcp, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_tcp_bind(&ctx.send2.tcp, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = run_test(inprocess); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(ipc_send_recv_tcp) { + return run_ipc_send_recv_tcp(0); +} + +TEST_IMPL(ipc_send_recv_tcp_inprocess) { + return run_ipc_send_recv_tcp(1); +} + + +/* Everything here runs in a child process or second thread. */ + +static void write2_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + + /* After two successful writes in the child process, allow the child + * process to be closed. */ + if (++write2_cb_called == 2 && (is_child_process || is_in_process)) { + uv_close(&ctx2.recv.handle, NULL); + uv_close(&ctx2.recv2.handle, NULL); + uv_close((uv_handle_t*)&ctx2.channel, NULL); + uv_close((uv_handle_t*)&ctx2.listen, NULL); + } +} + +static void read_cb(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* rdbuf) { + uv_buf_t wrbuf; + uv_pipe_t* pipe; + uv_handle_type pending; + int r; + union handles* recv; + uv_write_t* write_req; + + if (nread == UV__EOF || nread == UV__ECONNABORTED) { + return; + } + + if (++read_cb_called == 2) { + recv = &ctx2.recv; + write_req = &ctx2.write_req; + } else { + recv = &ctx2.recv2; + write_req = &ctx2.write_req2; + } + + pipe = (uv_pipe_t*) handle; + ASSERT(pipe == &ctx2.channel); + ASSERT(nread >= 0); + ASSERT(1 == uv_pipe_pending_count(pipe)); + + pending = uv_pipe_pending_type(pipe); + ASSERT(pending == UV_NAMED_PIPE || pending == UV_TCP); + + if (pending == UV_NAMED_PIPE) + r = uv_pipe_init(ctx2.channel.loop, &recv->pipe, 0); + else if (pending == UV_TCP) + r = uv_tcp_init(ctx2.channel.loop, &recv->tcp); + else + abort(); + ASSERT(r == 0); + + r = uv_accept(handle, &recv->stream); + ASSERT(r == 0); + + wrbuf = uv_buf_init(".", 1); + r = uv_write2(write_req, + (uv_stream_t*)&ctx2.channel, + &wrbuf, + 1, + &recv->stream, + write2_cb); + ASSERT(r == 0); +} + +static void send_recv_start() { + int r; + ASSERT(1 == uv_is_readable((uv_stream_t*)&ctx2.channel)); + ASSERT(1 == uv_is_writable((uv_stream_t*)&ctx2.channel)); + ASSERT(0 == uv_is_closing((uv_handle_t*)&ctx2.channel)); + + r = uv_read_start((uv_stream_t*)&ctx2.channel, alloc_cb, read_cb); + ASSERT(r == 0); +} + +static void listen_cb(uv_stream_t* handle, int status) { + int r; + ASSERT(handle == (uv_stream_t*)&ctx2.listen); + ASSERT(status == 0); + + r = uv_accept((uv_stream_t*)&ctx2.listen, (uv_stream_t*)&ctx2.channel); + ASSERT(r == 0); + + send_recv_start(); +} + +int run_ipc_send_recv_helper(uv_loop_t* loop, int inprocess) { + int r; + + is_in_process = inprocess; + + memset(&ctx2, 0, sizeof(ctx2)); + + r = uv_pipe_init(loop, &ctx2.listen, 0); + ASSERT(r == 0); + + r = uv_pipe_init(loop, &ctx2.channel, 1); + ASSERT(r == 0); + + if (inprocess) { + r = uv_pipe_bind(&ctx2.listen, TEST_PIPENAME_3); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&ctx2.listen, SOMAXCONN, listen_cb); + ASSERT(r == 0); + } else { + r = uv_pipe_open(&ctx2.channel, 0); + ASSERT(r == 0); + + send_recv_start(); + } + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + return 0; +} + +/* stdin is a duplex channel over which a handle is sent. + * We receive it and send it back where it came from. + */ +int ipc_send_recv_helper(void) { + int r; + + r = run_ipc_send_recv_helper(uv_default_loop(), 0); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +void ipc_send_recv_helper_threadproc(void* arg) { + int r; + uv_loop_t loop; + + r = uv_loop_init(&loop); + ASSERT(r == 0); + + r = run_ipc_send_recv_helper(&loop, 1); + ASSERT(r == 0); + + r = uv_loop_close(&loop); + ASSERT(r == 0); +} diff --git a/3rdparty/libuv/test/test-ipc.c b/3rdparty/libuv/test/test-ipc.c new file mode 100644 index 00000000000..f018c2d4d49 --- /dev/null +++ b/3rdparty/libuv/test/test-ipc.c @@ -0,0 +1,779 @@ +/* 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 <string.h> + +static uv_pipe_t channel; +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_server2; +static uv_tcp_t tcp_connection; + +static int exit_cb_called; +static int read_cb_called; +static int tcp_write_cb_called; +static int tcp_read_cb_called; +static int on_pipe_read_called; +static int local_conn_accepted; +static int remote_conn_accepted; +static int tcp_server_listening; +static uv_write_t write_req; +static uv_write_t conn_notify_req; +static int close_cb_called; +static int connection_accepted; +static int tcp_conn_read_cb_called; +static int tcp_conn_write_cb_called; + +typedef struct { + uv_connect_t conn_req; + uv_write_t tcp_write_req; + uv_tcp_t conn; +} tcp_conn; + +#define CONN_COUNT 100 +#define BACKLOG 128 + + +static void close_server_conn_cb(uv_handle_t* handle) { + free(handle); +} + + +static void on_connection(uv_stream_t* server, int status) { + uv_tcp_t* conn; + int r; + + if (!local_conn_accepted) { + /* Accept the connection and close it. Also and close the server. */ + ASSERT(status == 0); + ASSERT((uv_stream_t*)&tcp_server == server); + + conn = malloc(sizeof(*conn)); + ASSERT(conn); + r = uv_tcp_init(server->loop, conn); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)conn); + ASSERT(r == 0); + + uv_close((uv_handle_t*)conn, close_server_conn_cb); + uv_close((uv_handle_t*)server, NULL); + local_conn_accepted = 1; + } +} + + +static void exit_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 0); + uv_close((uv_handle_t*)process, NULL); +} + + +static void on_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + + +static void close_client_conn_cb(uv_handle_t* handle) { + tcp_conn* p = (tcp_conn*)handle->data; + free(p); +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_close((uv_handle_t*)req->handle, close_client_conn_cb); +} + + +static void make_many_connections(void) { + tcp_conn* conn; + struct sockaddr_in addr; + int r, i; + + for (i = 0; i < CONN_COUNT; i++) { + conn = malloc(sizeof(*conn)); + ASSERT(conn); + + r = uv_tcp_init(uv_default_loop(), &conn->conn); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_connect(&conn->conn_req, + (uv_tcp_t*) &conn->conn, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + conn->conn.data = conn; + } +} + + +static void on_read(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + int r; + uv_pipe_t* pipe; + uv_handle_type pending; + uv_buf_t outbuf; + + pipe = (uv_pipe_t*) handle; + + if (nread == 0) { + /* Everything OK, but nothing read. */ + free(buf->base); + return; + } + + if (nread < 0) { + if (nread == UV_EOF) { + free(buf->base); + return; + } + + printf("error recving on channel: %s\n", uv_strerror(nread)); + abort(); + } + + fprintf(stderr, "got %d bytes\n", (int)nread); + + pending = uv_pipe_pending_type(pipe); + if (!tcp_server_listening) { + ASSERT(1 == uv_pipe_pending_count(pipe)); + ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + read_cb_called++; + + /* Accept the pending TCP server, and start listening on it. */ + ASSERT(pending == UV_TCP); + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); + ASSERT(r == 0); + + tcp_server_listening = 1; + + /* Make sure that the expected data is correctly multiplexed. */ + ASSERT(memcmp("hello\n", buf->base, nread) == 0); + + outbuf = uv_buf_init("world\n", 6); + r = uv_write(&write_req, (uv_stream_t*)pipe, &outbuf, 1, NULL); + ASSERT(r == 0); + + /* Create a bunch of connections to get both servers to accept. */ + make_many_connections(); + } else if (memcmp("accepted_connection\n", buf->base, nread) == 0) { + /* Remote server has accepted a connection. Close the channel. */ + ASSERT(0 == uv_pipe_pending_count(pipe)); + ASSERT(pending == UV_UNKNOWN_HANDLE); + remote_conn_accepted = 1; + uv_close((uv_handle_t*)&channel, NULL); + } + + free(buf->base); +} + +#ifdef _WIN32 +static void on_read_listen_after_bound_twice(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + int r; + uv_pipe_t* pipe; + uv_handle_type pending; + + pipe = (uv_pipe_t*) handle; + + if (nread == 0) { + /* Everything OK, but nothing read. */ + free(buf->base); + return; + } + + if (nread < 0) { + if (nread == UV_EOF) { + free(buf->base); + return; + } + + printf("error recving on channel: %s\n", uv_strerror(nread)); + abort(); + } + + fprintf(stderr, "got %d bytes\n", (int)nread); + + ASSERT(uv_pipe_pending_count(pipe) > 0); + pending = uv_pipe_pending_type(pipe); + ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + read_cb_called++; + + if (read_cb_called == 1) { + /* Accept the first TCP server, and start listening on it. */ + ASSERT(pending == UV_TCP); + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); + ASSERT(r == 0); + } else if (read_cb_called == 2) { + /* Accept the second TCP server, and start listening on it. */ + ASSERT(pending == UV_TCP); + r = uv_tcp_init(uv_default_loop(), &tcp_server2); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server2); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server2, BACKLOG, on_connection); + ASSERT(r == UV_EADDRINUSE); + + uv_close((uv_handle_t*)&tcp_server, NULL); + uv_close((uv_handle_t*)&tcp_server2, NULL); + ASSERT(0 == uv_pipe_pending_count(pipe)); + uv_close((uv_handle_t*)&channel, NULL); + } + + free(buf->base); +} +#endif + +void spawn_helper(uv_pipe_t* channel, + uv_process_t* process, + const char* helper) { + uv_process_options_t options; + size_t exepath_size; + char exepath[1024]; + char* args[3]; + int r; + uv_stdio_container_t stdio[1]; + + r = uv_pipe_init(uv_default_loop(), channel, 1); + ASSERT(r == 0); + ASSERT(channel->ipc); + + exepath_size = sizeof(exepath); + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = (char*)helper; + args[2] = NULL; + + memset(&options, 0, sizeof(options)); + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; + + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | + UV_READABLE_PIPE | UV_WRITABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)channel; + options.stdio_count = 1; + + r = uv_spawn(uv_default_loop(), process, &options); + ASSERT(r == 0); +} + + +static void on_tcp_write(uv_write_t* req, int status) { + ASSERT(status == 0); + ASSERT(req->handle == (uv_stream_t*)&tcp_connection); + tcp_write_cb_called++; +} + + +static void on_read_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + + +static void on_tcp_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + ASSERT(nread > 0); + ASSERT(memcmp("hello again\n", buf->base, nread) == 0); + ASSERT(tcp == (uv_stream_t*)&tcp_connection); + free(buf->base); + + tcp_read_cb_called++; + + uv_close((uv_handle_t*)tcp, NULL); + uv_close((uv_handle_t*)&channel, NULL); +} + + +static void on_read_connection(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + int r; + uv_buf_t outbuf; + uv_pipe_t* pipe; + uv_handle_type pending; + + pipe = (uv_pipe_t*) handle; + if (nread == 0) { + /* Everything OK, but nothing read. */ + free(buf->base); + return; + } + + if (nread < 0) { + if (nread == UV_EOF) { + free(buf->base); + return; + } + + printf("error recving on channel: %s\n", uv_strerror(nread)); + abort(); + } + + fprintf(stderr, "got %d bytes\n", (int)nread); + + ASSERT(1 == uv_pipe_pending_count(pipe)); + pending = uv_pipe_pending_type(pipe); + + ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + read_cb_called++; + + /* Accept the pending TCP connection */ + ASSERT(pending == UV_TCP); + r = uv_tcp_init(uv_default_loop(), &tcp_connection); + ASSERT(r == 0); + + r = uv_accept(handle, (uv_stream_t*)&tcp_connection); + ASSERT(r == 0); + + /* Make sure that the expected data is correctly multiplexed. */ + ASSERT(memcmp("hello\n", buf->base, nread) == 0); + + /* Write/read to/from the connection */ + outbuf = uv_buf_init("world\n", 6); + r = uv_write(&write_req, (uv_stream_t*)&tcp_connection, &outbuf, 1, + on_tcp_write); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&tcp_connection, on_read_alloc, on_tcp_read); + ASSERT(r == 0); + + free(buf->base); +} + + +static int run_ipc_test(const char* helper, uv_read_cb read_cb) { + uv_process_t process; + int r; + + spawn_helper(&channel, &process, helper); + uv_read_start((uv_stream_t*)&channel, on_alloc, read_cb); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(ipc_listen_before_write) { + int r = run_ipc_test("ipc_helper_listen_before_write", on_read); + ASSERT(local_conn_accepted == 1); + ASSERT(remote_conn_accepted == 1); + ASSERT(read_cb_called == 1); + ASSERT(exit_cb_called == 1); + return r; +} + + +TEST_IMPL(ipc_listen_after_write) { + int r = run_ipc_test("ipc_helper_listen_after_write", on_read); + ASSERT(local_conn_accepted == 1); + ASSERT(remote_conn_accepted == 1); + ASSERT(read_cb_called == 1); + ASSERT(exit_cb_called == 1); + return r; +} + + +TEST_IMPL(ipc_tcp_connection) { + int r = run_ipc_test("ipc_helper_tcp_connection", on_read_connection); + ASSERT(read_cb_called == 1); + ASSERT(tcp_write_cb_called == 1); + ASSERT(tcp_read_cb_called == 1); + ASSERT(exit_cb_called == 1); + return r; +} + + +#ifdef _WIN32 +TEST_IMPL(listen_with_simultaneous_accepts) { + uv_tcp_t server; + int r; + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_tcp_simultaneous_accepts(&server, 1); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); + ASSERT(r == 0); + ASSERT(server.reqs_pending == 32); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(listen_no_simultaneous_accepts) { + uv_tcp_t server; + int r; + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_tcp_simultaneous_accepts(&server, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); + ASSERT(r == 0); + ASSERT(server.reqs_pending == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(ipc_listen_after_bind_twice) { + int r = run_ipc_test("ipc_helper_bind_twice", on_read_listen_after_bound_twice); + ASSERT(read_cb_called == 2); + ASSERT(exit_cb_called == 1); + return r; +} +#endif + + +/* Everything here runs in a child process. */ + +static tcp_conn conn; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void conn_notify_write_cb(uv_write_t* req, int status) { + uv_close((uv_handle_t*)&tcp_server, close_cb); + uv_close((uv_handle_t*)&channel, close_cb); +} + + +static void tcp_connection_write_cb(uv_write_t* req, int status) { + ASSERT((uv_handle_t*)&conn.conn == (uv_handle_t*)req->handle); + uv_close((uv_handle_t*)req->handle, close_cb); + uv_close((uv_handle_t*)&channel, close_cb); + uv_close((uv_handle_t*)&tcp_server, close_cb); + tcp_conn_write_cb_called++; +} + + +static void on_tcp_child_process_read(uv_stream_t* tcp, + ssize_t nread, + const uv_buf_t* buf) { + uv_buf_t outbuf; + int r; + + if (nread < 0) { + if (nread == UV_EOF) { + free(buf->base); + return; + } + + printf("error recving on tcp connection: %s\n", uv_strerror(nread)); + abort(); + } + + ASSERT(nread > 0); + ASSERT(memcmp("world\n", buf->base, nread) == 0); + on_pipe_read_called++; + free(buf->base); + + /* Write to the socket */ + outbuf = uv_buf_init("hello again\n", 12); + r = uv_write(&conn.tcp_write_req, tcp, &outbuf, 1, tcp_connection_write_cb); + ASSERT(r == 0); + + tcp_conn_read_cb_called++; +} + + +static void connect_child_process_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(status == 0); + r = uv_read_start(req->handle, on_read_alloc, on_tcp_child_process_read); + ASSERT(r == 0); +} + + +static void ipc_on_connection(uv_stream_t* server, int status) { + int r; + uv_buf_t buf; + + if (!connection_accepted) { + /* + * Accept the connection and close it. Also let the other + * side know. + */ + ASSERT(status == 0); + ASSERT((uv_stream_t*)&tcp_server == server); + + r = uv_tcp_init(server->loop, &conn.conn); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)&conn.conn); + ASSERT(r == 0); + + uv_close((uv_handle_t*)&conn.conn, close_cb); + + buf = uv_buf_init("accepted_connection\n", 20); + r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1, + NULL, conn_notify_write_cb); + ASSERT(r == 0); + + connection_accepted = 1; + } +} + + +static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) { + int r; + uv_buf_t buf; + uv_tcp_t* conn; + + ASSERT(status == 0); + ASSERT((uv_stream_t*)&tcp_server == server); + + conn = malloc(sizeof(*conn)); + ASSERT(conn); + + r = uv_tcp_init(server->loop, conn); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)conn); + ASSERT(r == 0); + + /* Send the accepted connection to the other process */ + buf = uv_buf_init("hello\n", 6); + r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1, + (uv_stream_t*)conn, NULL); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) conn, + on_read_alloc, + on_tcp_child_process_read); + ASSERT(r == 0); + + uv_close((uv_handle_t*)conn, close_cb); +} + + +int ipc_helper(int listen_after_write) { + /* + * This is launched from test-ipc.c. stdin is a duplex channel that we + * over which a handle will be transmitted. + */ + struct sockaddr_in addr; + uv_write_t write_req; + int r; + uv_buf_t buf; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_pipe_init(uv_default_loop(), &channel, 1); + ASSERT(r == 0); + + uv_pipe_open(&channel, 0); + + ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); + ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + if (!listen_after_write) { + r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); + ASSERT(r == 0); + } + + buf = uv_buf_init("hello\n", 6); + r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, + (uv_stream_t*)&tcp_server, NULL); + ASSERT(r == 0); + + if (listen_after_write) { + r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); + ASSERT(r == 0); + } + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(connection_accepted == 1); + ASSERT(close_cb_called == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +int ipc_helper_tcp_connection(void) { + /* + * This is launched from test-ipc.c. stdin is a duplex channel + * over which a handle will be transmitted. + */ + + int r; + struct sockaddr_in addr; + + r = uv_pipe_init(uv_default_loop(), &channel, 1); + ASSERT(r == 0); + + uv_pipe_open(&channel, 0); + + ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); + ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection_tcp_conn); + ASSERT(r == 0); + + /* Make a connection to the server */ + r = uv_tcp_init(uv_default_loop(), &conn.conn); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_connect(&conn.conn_req, + (uv_tcp_t*) &conn.conn, + (const struct sockaddr*) &addr, + connect_child_process_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(tcp_conn_read_cb_called == 1); + ASSERT(tcp_conn_write_cb_called == 1); + ASSERT(close_cb_called == 4); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +int ipc_helper_bind_twice(void) { + /* + * This is launched from test-ipc.c. stdin is a duplex channel + * over which two handles will be transmitted. + */ + struct sockaddr_in addr; + uv_write_t write_req; + uv_write_t write_req2; + int r; + uv_buf_t buf; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_pipe_init(uv_default_loop(), &channel, 1); + ASSERT(r == 0); + + uv_pipe_open(&channel, 0); + + ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); + ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + + buf = uv_buf_init("hello\n", 6); + + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + r = uv_tcp_init(uv_default_loop(), &tcp_server2); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + r = uv_tcp_bind(&tcp_server2, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, + (uv_stream_t*)&tcp_server, NULL); + ASSERT(r == 0); + r = uv_write2(&write_req2, (uv_stream_t*)&channel, &buf, 1, + (uv_stream_t*)&tcp_server2, NULL); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-list.h b/3rdparty/libuv/test/test-list.h new file mode 100644 index 00000000000..858a20af49c --- /dev/null +++ b/3rdparty/libuv/test/test-list.h @@ -0,0 +1,732 @@ +/* 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. + */ + +TEST_DECLARE (platform_output) +TEST_DECLARE (callback_order) +TEST_DECLARE (close_order) +TEST_DECLARE (run_once) +TEST_DECLARE (run_nowait) +TEST_DECLARE (loop_alive) +TEST_DECLARE (loop_close) +TEST_DECLARE (loop_stop) +TEST_DECLARE (loop_update_time) +TEST_DECLARE (loop_backend_timeout) +TEST_DECLARE (loop_configure) +TEST_DECLARE (default_loop_close) +TEST_DECLARE (barrier_1) +TEST_DECLARE (barrier_2) +TEST_DECLARE (barrier_3) +TEST_DECLARE (condvar_1) +TEST_DECLARE (condvar_2) +TEST_DECLARE (condvar_3) +TEST_DECLARE (condvar_4) +TEST_DECLARE (condvar_5) +TEST_DECLARE (semaphore_1) +TEST_DECLARE (semaphore_2) +TEST_DECLARE (semaphore_3) +TEST_DECLARE (tty) +TEST_DECLARE (tty_file) +TEST_DECLARE (stdio_over_pipes) +TEST_DECLARE (ip6_pton) +TEST_DECLARE (ipc_listen_before_write) +TEST_DECLARE (ipc_listen_after_write) +#ifndef _WIN32 +TEST_DECLARE (ipc_send_recv_pipe) +TEST_DECLARE (ipc_send_recv_pipe_inprocess) +#endif +TEST_DECLARE (ipc_send_recv_tcp) +TEST_DECLARE (ipc_send_recv_tcp_inprocess) +TEST_DECLARE (ipc_tcp_connection) +TEST_DECLARE (tcp_ping_pong) +TEST_DECLARE (tcp_ping_pong_v6) +TEST_DECLARE (pipe_ping_pong) +TEST_DECLARE (delayed_accept) +TEST_DECLARE (multiple_listen) +#ifndef _WIN32 +TEST_DECLARE (tcp_write_after_connect) +#endif +TEST_DECLARE (tcp_writealot) +TEST_DECLARE (tcp_write_fail) +TEST_DECLARE (tcp_try_write) +TEST_DECLARE (tcp_write_queue_order) +TEST_DECLARE (tcp_open) +TEST_DECLARE (tcp_open_twice) +TEST_DECLARE (tcp_connect_error_after_write) +TEST_DECLARE (tcp_shutdown_after_write) +TEST_DECLARE (tcp_bind_error_addrinuse) +TEST_DECLARE (tcp_bind_error_addrnotavail_1) +TEST_DECLARE (tcp_bind_error_addrnotavail_2) +TEST_DECLARE (tcp_bind_error_fault) +TEST_DECLARE (tcp_bind_error_inval) +TEST_DECLARE (tcp_bind_localhost_ok) +TEST_DECLARE (tcp_bind_invalid_flags) +TEST_DECLARE (tcp_listen_without_bind) +TEST_DECLARE (tcp_connect_error_fault) +TEST_DECLARE (tcp_connect_timeout) +TEST_DECLARE (tcp_close_while_connecting) +TEST_DECLARE (tcp_close) +TEST_DECLARE (tcp_create_early) +TEST_DECLARE (tcp_create_early_bad_bind) +TEST_DECLARE (tcp_create_early_bad_domain) +TEST_DECLARE (tcp_create_early_accept) +#ifndef _WIN32 +TEST_DECLARE (tcp_close_accept) +TEST_DECLARE (tcp_oob) +#endif +TEST_DECLARE (tcp_flags) +TEST_DECLARE (tcp_write_to_half_open_connection) +TEST_DECLARE (tcp_unexpected_read) +TEST_DECLARE (tcp_read_stop) +TEST_DECLARE (tcp_bind6_error_addrinuse) +TEST_DECLARE (tcp_bind6_error_addrnotavail) +TEST_DECLARE (tcp_bind6_error_fault) +TEST_DECLARE (tcp_bind6_error_inval) +TEST_DECLARE (tcp_bind6_localhost_ok) +TEST_DECLARE (udp_bind) +TEST_DECLARE (udp_bind_reuseaddr) +TEST_DECLARE (udp_create_early) +TEST_DECLARE (udp_create_early_bad_bind) +TEST_DECLARE (udp_create_early_bad_domain) +TEST_DECLARE (udp_send_and_recv) +TEST_DECLARE (udp_send_immediate) +TEST_DECLARE (udp_send_unreachable) +TEST_DECLARE (udp_multicast_join) +TEST_DECLARE (udp_multicast_join6) +TEST_DECLARE (udp_multicast_ttl) +TEST_DECLARE (udp_multicast_interface) +TEST_DECLARE (udp_multicast_interface6) +TEST_DECLARE (udp_dgram_too_big) +TEST_DECLARE (udp_dual_stack) +TEST_DECLARE (udp_ipv6_only) +TEST_DECLARE (udp_options) +TEST_DECLARE (udp_options6) +TEST_DECLARE (udp_no_autobind) +TEST_DECLARE (udp_open) +TEST_DECLARE (udp_open_twice) +TEST_DECLARE (udp_try_send) +TEST_DECLARE (pipe_bind_error_addrinuse) +TEST_DECLARE (pipe_bind_error_addrnotavail) +TEST_DECLARE (pipe_bind_error_inval) +TEST_DECLARE (pipe_connect_multiple) +TEST_DECLARE (pipe_listen_without_bind) +TEST_DECLARE (pipe_connect_bad_name) +TEST_DECLARE (pipe_connect_to_file) +TEST_DECLARE (pipe_connect_on_prepare) +TEST_DECLARE (pipe_getsockname) +TEST_DECLARE (pipe_getsockname_abstract) +TEST_DECLARE (pipe_getsockname_blocking) +TEST_DECLARE (pipe_pending_instances) +TEST_DECLARE (pipe_sendmsg) +TEST_DECLARE (pipe_server_close) +TEST_DECLARE (connection_fail) +TEST_DECLARE (connection_fail_doesnt_auto_close) +TEST_DECLARE (shutdown_close_tcp) +TEST_DECLARE (shutdown_close_pipe) +TEST_DECLARE (shutdown_eof) +TEST_DECLARE (shutdown_twice) +TEST_DECLARE (callback_stack) +TEST_DECLARE (error_message) +TEST_DECLARE (timer) +TEST_DECLARE (timer_init) +TEST_DECLARE (timer_again) +TEST_DECLARE (timer_start_twice) +TEST_DECLARE (timer_order) +TEST_DECLARE (timer_huge_timeout) +TEST_DECLARE (timer_huge_repeat) +TEST_DECLARE (timer_run_once) +TEST_DECLARE (timer_from_check) +TEST_DECLARE (timer_null_callback) +TEST_DECLARE (idle_starvation) +TEST_DECLARE (loop_handles) +TEST_DECLARE (get_loadavg) +TEST_DECLARE (walk_handles) +TEST_DECLARE (watcher_cross_stop) +TEST_DECLARE (ref) +TEST_DECLARE (idle_ref) +TEST_DECLARE (async_ref) +TEST_DECLARE (prepare_ref) +TEST_DECLARE (check_ref) +TEST_DECLARE (unref_in_prepare_cb) +TEST_DECLARE (timer_ref) +TEST_DECLARE (timer_ref2) +TEST_DECLARE (fs_event_ref) +TEST_DECLARE (fs_poll_ref) +TEST_DECLARE (tcp_ref) +TEST_DECLARE (tcp_ref2) +TEST_DECLARE (tcp_ref2b) +TEST_DECLARE (tcp_ref3) +TEST_DECLARE (tcp_ref4) +TEST_DECLARE (udp_ref) +TEST_DECLARE (udp_ref2) +TEST_DECLARE (udp_ref3) +TEST_DECLARE (pipe_ref) +TEST_DECLARE (pipe_ref2) +TEST_DECLARE (pipe_ref3) +TEST_DECLARE (pipe_ref4) +#ifndef _WIN32 +TEST_DECLARE (pipe_close_stdout_read_stdin) +#endif +TEST_DECLARE (pipe_set_non_blocking) +TEST_DECLARE (process_ref) +TEST_DECLARE (has_ref) +TEST_DECLARE (active) +TEST_DECLARE (embed) +TEST_DECLARE (async) +TEST_DECLARE (async_null_cb) +TEST_DECLARE (get_currentexe) +TEST_DECLARE (process_title) +TEST_DECLARE (cwd_and_chdir) +TEST_DECLARE (get_memory) +TEST_DECLARE (handle_fileno) +TEST_DECLARE (homedir) +TEST_DECLARE (hrtime) +TEST_DECLARE (getaddrinfo_fail) +TEST_DECLARE (getaddrinfo_fail_sync) +TEST_DECLARE (getaddrinfo_basic) +TEST_DECLARE (getaddrinfo_basic_sync) +TEST_DECLARE (getaddrinfo_concurrent) +TEST_DECLARE (getnameinfo_basic_ip4) +TEST_DECLARE (getnameinfo_basic_ip4_sync) +TEST_DECLARE (getnameinfo_basic_ip6) +TEST_DECLARE (getsockname_tcp) +TEST_DECLARE (getsockname_udp) +TEST_DECLARE (fail_always) +TEST_DECLARE (pass_always) +TEST_DECLARE (socket_buffer_size) +TEST_DECLARE (spawn_fails) +#ifndef _WIN32 +TEST_DECLARE (spawn_fails_check_for_waitpid_cleanup) +#endif +TEST_DECLARE (spawn_exit_code) +TEST_DECLARE (spawn_stdout) +TEST_DECLARE (spawn_stdin) +TEST_DECLARE (spawn_stdio_greater_than_3) +TEST_DECLARE (spawn_ignored_stdio) +TEST_DECLARE (spawn_and_kill) +TEST_DECLARE (spawn_detached) +TEST_DECLARE (spawn_and_kill_with_std) +TEST_DECLARE (spawn_and_ping) +TEST_DECLARE (spawn_preserve_env) +TEST_DECLARE (spawn_setuid_fails) +TEST_DECLARE (spawn_setgid_fails) +TEST_DECLARE (spawn_stdout_to_file) +TEST_DECLARE (spawn_stdout_and_stderr_to_file) +TEST_DECLARE (spawn_stdout_and_stderr_to_file2) +TEST_DECLARE (spawn_stdout_and_stderr_to_file_swap) +TEST_DECLARE (spawn_auto_unref) +TEST_DECLARE (spawn_closed_process_io) +TEST_DECLARE (spawn_reads_child_path) +TEST_DECLARE (spawn_inherit_streams) +TEST_DECLARE (fs_poll) +TEST_DECLARE (fs_poll_getpath) +TEST_DECLARE (kill) +TEST_DECLARE (fs_file_noent) +TEST_DECLARE (fs_file_nametoolong) +TEST_DECLARE (fs_file_loop) +TEST_DECLARE (fs_file_async) +TEST_DECLARE (fs_file_sync) +TEST_DECLARE (fs_file_write_null_buffer) +TEST_DECLARE (fs_async_dir) +TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (fs_mkdtemp) +TEST_DECLARE (fs_fstat) +TEST_DECLARE (fs_access) +TEST_DECLARE (fs_chmod) +TEST_DECLARE (fs_unlink_readonly) +TEST_DECLARE (fs_chown) +TEST_DECLARE (fs_link) +TEST_DECLARE (fs_readlink) +TEST_DECLARE (fs_realpath) +TEST_DECLARE (fs_symlink) +TEST_DECLARE (fs_symlink_dir) +TEST_DECLARE (fs_utime) +TEST_DECLARE (fs_futime) +TEST_DECLARE (fs_file_open_append) +TEST_DECLARE (fs_stat_missing_path) +TEST_DECLARE (fs_read_file_eof) +TEST_DECLARE (fs_event_watch_dir) +TEST_DECLARE (fs_event_watch_dir_recursive) +TEST_DECLARE (fs_event_watch_file) +TEST_DECLARE (fs_event_watch_file_twice) +TEST_DECLARE (fs_event_watch_file_current_dir) +TEST_DECLARE (fs_event_no_callback_after_close) +TEST_DECLARE (fs_event_no_callback_on_close) +TEST_DECLARE (fs_event_immediate_close) +TEST_DECLARE (fs_event_close_with_pending_event) +TEST_DECLARE (fs_event_close_in_callback) +TEST_DECLARE (fs_event_start_and_close) +TEST_DECLARE (fs_event_error_reporting) +TEST_DECLARE (fs_event_getpath) +TEST_DECLARE (fs_scandir_empty_dir) +TEST_DECLARE (fs_scandir_file) +TEST_DECLARE (fs_open_dir) +TEST_DECLARE (fs_rename_to_existing_file) +TEST_DECLARE (fs_write_multiple_bufs) +TEST_DECLARE (fs_read_write_null_arguments) +TEST_DECLARE (fs_write_alotof_bufs) +TEST_DECLARE (fs_write_alotof_bufs_with_offset) +TEST_DECLARE (threadpool_queue_work_simple) +TEST_DECLARE (threadpool_queue_work_einval) +TEST_DECLARE (threadpool_multiple_event_loops) +TEST_DECLARE (threadpool_cancel_getaddrinfo) +TEST_DECLARE (threadpool_cancel_getnameinfo) +TEST_DECLARE (threadpool_cancel_work) +TEST_DECLARE (threadpool_cancel_fs) +TEST_DECLARE (threadpool_cancel_single) +TEST_DECLARE (thread_local_storage) +TEST_DECLARE (thread_mutex) +TEST_DECLARE (thread_rwlock) +TEST_DECLARE (thread_rwlock_trylock) +TEST_DECLARE (thread_create) +TEST_DECLARE (thread_equal) +TEST_DECLARE (dlerror) +TEST_DECLARE (poll_duplex) +TEST_DECLARE (poll_unidirectional) +TEST_DECLARE (poll_close) + +TEST_DECLARE (ip4_addr) +TEST_DECLARE (ip6_addr_link_local) + +#ifdef _WIN32 +TEST_DECLARE (poll_close_doesnt_corrupt_stack) +TEST_DECLARE (poll_closesocket) +TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) +#if !defined(USING_UV_SHARED) +TEST_DECLARE (argument_escaping) +TEST_DECLARE (environment_creation) +#endif +TEST_DECLARE (listen_with_simultaneous_accepts) +TEST_DECLARE (listen_no_simultaneous_accepts) +TEST_DECLARE (fs_stat_root) +TEST_DECLARE (spawn_with_an_odd_path) +TEST_DECLARE (ipc_listen_after_bind_twice) +#else +TEST_DECLARE (emfile) +TEST_DECLARE (close_fd) +TEST_DECLARE (spawn_fs_open) +TEST_DECLARE (spawn_setuid_setgid) +TEST_DECLARE (we_get_signal) +TEST_DECLARE (we_get_signals) +TEST_DECLARE (signal_multiple_loops) +TEST_DECLARE (closed_fd_events) +#endif +#ifdef __APPLE__ +TEST_DECLARE (osx_select) +TEST_DECLARE (osx_select_many_fds) +#endif +HELPER_DECLARE (tcp4_echo_server) +HELPER_DECLARE (tcp6_echo_server) +HELPER_DECLARE (udp4_echo_server) +HELPER_DECLARE (pipe_echo_server) + +TEST_DECLARE (queue_foreach_delete) + +TASK_LIST_START + TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000) + +#if 0 + TEST_ENTRY (callback_order) +#endif + TEST_ENTRY (close_order) + TEST_ENTRY (run_once) + TEST_ENTRY (run_nowait) + TEST_ENTRY (loop_alive) + TEST_ENTRY (loop_close) + TEST_ENTRY (loop_stop) + TEST_ENTRY (loop_update_time) + TEST_ENTRY (loop_backend_timeout) + TEST_ENTRY (loop_configure) + TEST_ENTRY (default_loop_close) + TEST_ENTRY (barrier_1) + TEST_ENTRY (barrier_2) + TEST_ENTRY (barrier_3) + TEST_ENTRY (condvar_1) + TEST_ENTRY (condvar_2) + TEST_ENTRY (condvar_3) + TEST_ENTRY (condvar_4) + TEST_ENTRY (condvar_5) + TEST_ENTRY (semaphore_1) + TEST_ENTRY (semaphore_2) + TEST_ENTRY (semaphore_3) + + TEST_ENTRY (pipe_connect_bad_name) + TEST_ENTRY (pipe_connect_to_file) + TEST_ENTRY (pipe_connect_on_prepare) + + TEST_ENTRY (pipe_server_close) +#ifndef _WIN32 + TEST_ENTRY (pipe_close_stdout_read_stdin) +#endif + TEST_ENTRY (pipe_set_non_blocking) + TEST_ENTRY (tty) + TEST_ENTRY (tty_file) + TEST_ENTRY (stdio_over_pipes) + TEST_ENTRY (ip6_pton) + TEST_ENTRY (ipc_listen_before_write) + TEST_ENTRY (ipc_listen_after_write) +#ifndef _WIN32 + TEST_ENTRY (ipc_send_recv_pipe) + TEST_ENTRY (ipc_send_recv_pipe_inprocess) +#endif + TEST_ENTRY (ipc_send_recv_tcp) + TEST_ENTRY (ipc_send_recv_tcp_inprocess) + TEST_ENTRY (ipc_tcp_connection) + + TEST_ENTRY (tcp_ping_pong) + TEST_HELPER (tcp_ping_pong, tcp4_echo_server) + + TEST_ENTRY (tcp_ping_pong_v6) + TEST_HELPER (tcp_ping_pong_v6, tcp6_echo_server) + + TEST_ENTRY (pipe_ping_pong) + TEST_HELPER (pipe_ping_pong, pipe_echo_server) + + TEST_ENTRY (delayed_accept) + TEST_ENTRY (multiple_listen) + +#ifndef _WIN32 + TEST_ENTRY (tcp_write_after_connect) +#endif + + TEST_ENTRY (tcp_writealot) + TEST_HELPER (tcp_writealot, tcp4_echo_server) + + TEST_ENTRY (tcp_write_fail) + TEST_HELPER (tcp_write_fail, tcp4_echo_server) + + TEST_ENTRY (tcp_try_write) + + TEST_ENTRY (tcp_write_queue_order) + + TEST_ENTRY (tcp_open) + TEST_HELPER (tcp_open, tcp4_echo_server) + TEST_ENTRY (tcp_open_twice) + + TEST_ENTRY (tcp_shutdown_after_write) + TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server) + + TEST_ENTRY (tcp_connect_error_after_write) + TEST_ENTRY (tcp_bind_error_addrinuse) + TEST_ENTRY (tcp_bind_error_addrnotavail_1) + TEST_ENTRY (tcp_bind_error_addrnotavail_2) + TEST_ENTRY (tcp_bind_error_fault) + TEST_ENTRY (tcp_bind_error_inval) + TEST_ENTRY (tcp_bind_localhost_ok) + TEST_ENTRY (tcp_bind_invalid_flags) + TEST_ENTRY (tcp_listen_without_bind) + TEST_ENTRY (tcp_connect_error_fault) + TEST_ENTRY (tcp_connect_timeout) + TEST_ENTRY (tcp_close_while_connecting) + TEST_ENTRY (tcp_close) + TEST_ENTRY (tcp_create_early) + TEST_ENTRY (tcp_create_early_bad_bind) + TEST_ENTRY (tcp_create_early_bad_domain) + TEST_ENTRY (tcp_create_early_accept) +#ifndef _WIN32 + TEST_ENTRY (tcp_close_accept) + TEST_ENTRY (tcp_oob) +#endif + TEST_ENTRY (tcp_flags) + TEST_ENTRY (tcp_write_to_half_open_connection) + TEST_ENTRY (tcp_unexpected_read) + + TEST_ENTRY (tcp_read_stop) + TEST_HELPER (tcp_read_stop, tcp4_echo_server) + + TEST_ENTRY (tcp_bind6_error_addrinuse) + TEST_ENTRY (tcp_bind6_error_addrnotavail) + TEST_ENTRY (tcp_bind6_error_fault) + TEST_ENTRY (tcp_bind6_error_inval) + TEST_ENTRY (tcp_bind6_localhost_ok) + + TEST_ENTRY (udp_bind) + TEST_ENTRY (udp_bind_reuseaddr) + TEST_ENTRY (udp_create_early) + TEST_ENTRY (udp_create_early_bad_bind) + TEST_ENTRY (udp_create_early_bad_domain) + TEST_ENTRY (udp_send_and_recv) + TEST_ENTRY (udp_send_immediate) + TEST_ENTRY (udp_send_unreachable) + TEST_ENTRY (udp_dgram_too_big) + TEST_ENTRY (udp_dual_stack) + TEST_ENTRY (udp_ipv6_only) + TEST_ENTRY (udp_options) + TEST_ENTRY (udp_options6) + TEST_ENTRY (udp_no_autobind) + TEST_ENTRY (udp_multicast_interface) + TEST_ENTRY (udp_multicast_interface6) + TEST_ENTRY (udp_multicast_join) + TEST_ENTRY (udp_multicast_join6) + TEST_ENTRY (udp_multicast_ttl) + TEST_ENTRY (udp_try_send) + + TEST_ENTRY (udp_open) + TEST_HELPER (udp_open, udp4_echo_server) + TEST_ENTRY (udp_open_twice) + + TEST_ENTRY (pipe_bind_error_addrinuse) + TEST_ENTRY (pipe_bind_error_addrnotavail) + TEST_ENTRY (pipe_bind_error_inval) + TEST_ENTRY (pipe_connect_multiple) + TEST_ENTRY (pipe_listen_without_bind) + TEST_ENTRY (pipe_getsockname) + TEST_ENTRY (pipe_getsockname_abstract) + TEST_ENTRY (pipe_getsockname_blocking) + TEST_ENTRY (pipe_pending_instances) + TEST_ENTRY (pipe_sendmsg) + + TEST_ENTRY (connection_fail) + TEST_ENTRY (connection_fail_doesnt_auto_close) + + TEST_ENTRY (shutdown_close_tcp) + TEST_HELPER (shutdown_close_tcp, tcp4_echo_server) + TEST_ENTRY (shutdown_close_pipe) + TEST_HELPER (shutdown_close_pipe, pipe_echo_server) + + TEST_ENTRY (shutdown_eof) + TEST_HELPER (shutdown_eof, tcp4_echo_server) + + TEST_ENTRY (shutdown_twice) + TEST_HELPER (shutdown_twice, tcp4_echo_server) + + TEST_ENTRY (callback_stack) + TEST_HELPER (callback_stack, tcp4_echo_server) + + TEST_ENTRY (error_message) + + TEST_ENTRY (timer) + TEST_ENTRY (timer_init) + TEST_ENTRY (timer_again) + TEST_ENTRY (timer_start_twice) + TEST_ENTRY (timer_order) + TEST_ENTRY (timer_huge_timeout) + TEST_ENTRY (timer_huge_repeat) + TEST_ENTRY (timer_run_once) + TEST_ENTRY (timer_from_check) + TEST_ENTRY (timer_null_callback) + + TEST_ENTRY (idle_starvation) + + TEST_ENTRY (ref) + TEST_ENTRY (idle_ref) + TEST_ENTRY (fs_poll_ref) + TEST_ENTRY (async_ref) + TEST_ENTRY (prepare_ref) + TEST_ENTRY (check_ref) + TEST_ENTRY (unref_in_prepare_cb) + TEST_ENTRY (timer_ref) + TEST_ENTRY (timer_ref2) + TEST_ENTRY (fs_event_ref) + TEST_ENTRY (tcp_ref) + TEST_ENTRY (tcp_ref2) + TEST_ENTRY (tcp_ref2b) + TEST_ENTRY (tcp_ref3) + TEST_HELPER (tcp_ref3, tcp4_echo_server) + TEST_ENTRY (tcp_ref4) + TEST_HELPER (tcp_ref4, tcp4_echo_server) + TEST_ENTRY (udp_ref) + TEST_ENTRY (udp_ref2) + TEST_ENTRY (udp_ref3) + TEST_HELPER (udp_ref3, udp4_echo_server) + TEST_ENTRY (pipe_ref) + TEST_ENTRY (pipe_ref2) + TEST_ENTRY (pipe_ref3) + TEST_HELPER (pipe_ref3, pipe_echo_server) + TEST_ENTRY (pipe_ref4) + TEST_HELPER (pipe_ref4, pipe_echo_server) + TEST_ENTRY (process_ref) + TEST_ENTRY (has_ref) + + TEST_ENTRY (loop_handles) + TEST_ENTRY (walk_handles) + + TEST_ENTRY (watcher_cross_stop) + + TEST_ENTRY (active) + + TEST_ENTRY (embed) + + TEST_ENTRY (async) + TEST_ENTRY (async_null_cb) + + TEST_ENTRY (get_currentexe) + + TEST_ENTRY (process_title) + + TEST_ENTRY (cwd_and_chdir) + + TEST_ENTRY (get_memory) + + TEST_ENTRY (get_loadavg) + + TEST_ENTRY (handle_fileno) + + TEST_ENTRY (homedir) + + TEST_ENTRY (hrtime) + + TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000) + TEST_ENTRY (getaddrinfo_fail_sync) + + TEST_ENTRY (getaddrinfo_basic) + TEST_ENTRY (getaddrinfo_basic_sync) + TEST_ENTRY (getaddrinfo_concurrent) + + TEST_ENTRY (getnameinfo_basic_ip4) + TEST_ENTRY (getnameinfo_basic_ip4_sync) + TEST_ENTRY (getnameinfo_basic_ip6) + + TEST_ENTRY (getsockname_tcp) + TEST_ENTRY (getsockname_udp) + + TEST_ENTRY (poll_duplex) + TEST_ENTRY (poll_unidirectional) + TEST_ENTRY (poll_close) + + TEST_ENTRY (socket_buffer_size) + + TEST_ENTRY (spawn_fails) +#ifndef _WIN32 + TEST_ENTRY (spawn_fails_check_for_waitpid_cleanup) +#endif + TEST_ENTRY (spawn_exit_code) + TEST_ENTRY (spawn_stdout) + TEST_ENTRY (spawn_stdin) + TEST_ENTRY (spawn_stdio_greater_than_3) + TEST_ENTRY (spawn_ignored_stdio) + TEST_ENTRY (spawn_and_kill) + TEST_ENTRY (spawn_detached) + TEST_ENTRY (spawn_and_kill_with_std) + TEST_ENTRY (spawn_and_ping) + TEST_ENTRY (spawn_preserve_env) + TEST_ENTRY (spawn_setuid_fails) + TEST_ENTRY (spawn_setgid_fails) + TEST_ENTRY (spawn_stdout_to_file) + TEST_ENTRY (spawn_stdout_and_stderr_to_file) + TEST_ENTRY (spawn_stdout_and_stderr_to_file2) + TEST_ENTRY (spawn_stdout_and_stderr_to_file_swap) + TEST_ENTRY (spawn_auto_unref) + TEST_ENTRY (spawn_closed_process_io) + TEST_ENTRY (spawn_reads_child_path) + TEST_ENTRY (spawn_inherit_streams) + TEST_ENTRY (fs_poll) + TEST_ENTRY (fs_poll_getpath) + TEST_ENTRY (kill) + +#ifdef _WIN32 + TEST_ENTRY (poll_close_doesnt_corrupt_stack) + TEST_ENTRY (poll_closesocket) + TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) +#if !defined(USING_UV_SHARED) + TEST_ENTRY (argument_escaping) + TEST_ENTRY (environment_creation) +# endif + TEST_ENTRY (listen_with_simultaneous_accepts) + TEST_ENTRY (listen_no_simultaneous_accepts) + TEST_ENTRY (fs_stat_root) + TEST_ENTRY (spawn_with_an_odd_path) + TEST_ENTRY (ipc_listen_after_bind_twice) +#else + TEST_ENTRY (emfile) + TEST_ENTRY (close_fd) + TEST_ENTRY (spawn_fs_open) + TEST_ENTRY (spawn_setuid_setgid) + TEST_ENTRY (we_get_signal) + TEST_ENTRY (we_get_signals) + TEST_ENTRY (signal_multiple_loops) + TEST_ENTRY (closed_fd_events) +#endif + +#ifdef __APPLE__ + TEST_ENTRY (osx_select) + TEST_ENTRY (osx_select_many_fds) +#endif + + TEST_ENTRY (fs_file_noent) + TEST_ENTRY (fs_file_nametoolong) + TEST_ENTRY (fs_file_loop) + TEST_ENTRY (fs_file_async) + TEST_ENTRY (fs_file_sync) + TEST_ENTRY (fs_file_write_null_buffer) + TEST_ENTRY (fs_async_dir) + TEST_ENTRY (fs_async_sendfile) + TEST_ENTRY (fs_mkdtemp) + TEST_ENTRY (fs_fstat) + TEST_ENTRY (fs_access) + TEST_ENTRY (fs_chmod) + TEST_ENTRY (fs_unlink_readonly) + TEST_ENTRY (fs_chown) + TEST_ENTRY (fs_utime) + TEST_ENTRY (fs_futime) + TEST_ENTRY (fs_readlink) + TEST_ENTRY (fs_realpath) + TEST_ENTRY (fs_symlink) + TEST_ENTRY (fs_symlink_dir) + TEST_ENTRY (fs_stat_missing_path) + TEST_ENTRY (fs_read_file_eof) + TEST_ENTRY (fs_file_open_append) + TEST_ENTRY (fs_event_watch_dir) + TEST_ENTRY (fs_event_watch_dir_recursive) + TEST_ENTRY (fs_event_watch_file) + TEST_ENTRY (fs_event_watch_file_twice) + TEST_ENTRY (fs_event_watch_file_current_dir) + TEST_ENTRY (fs_event_no_callback_after_close) + TEST_ENTRY (fs_event_no_callback_on_close) + TEST_ENTRY (fs_event_immediate_close) + TEST_ENTRY (fs_event_close_with_pending_event) + TEST_ENTRY (fs_event_close_in_callback) + TEST_ENTRY (fs_event_start_and_close) + TEST_ENTRY (fs_event_error_reporting) + TEST_ENTRY (fs_event_getpath) + TEST_ENTRY (fs_scandir_empty_dir) + TEST_ENTRY (fs_scandir_file) + TEST_ENTRY (fs_open_dir) + TEST_ENTRY (fs_rename_to_existing_file) + TEST_ENTRY (fs_write_multiple_bufs) + TEST_ENTRY (fs_write_alotof_bufs) + TEST_ENTRY (fs_write_alotof_bufs_with_offset) + TEST_ENTRY (fs_read_write_null_arguments) + TEST_ENTRY (threadpool_queue_work_simple) + TEST_ENTRY (threadpool_queue_work_einval) + TEST_ENTRY (threadpool_multiple_event_loops) + TEST_ENTRY (threadpool_cancel_getaddrinfo) + TEST_ENTRY (threadpool_cancel_getnameinfo) + TEST_ENTRY (threadpool_cancel_work) + TEST_ENTRY (threadpool_cancel_fs) + TEST_ENTRY (threadpool_cancel_single) + TEST_ENTRY (thread_local_storage) + TEST_ENTRY (thread_mutex) + TEST_ENTRY (thread_rwlock) + TEST_ENTRY (thread_rwlock_trylock) + TEST_ENTRY (thread_create) + TEST_ENTRY (thread_equal) + TEST_ENTRY (dlerror) + TEST_ENTRY (ip4_addr) + TEST_ENTRY (ip6_addr_link_local) + + TEST_ENTRY (queue_foreach_delete) + +#if 0 + /* These are for testing the test runner. */ + TEST_ENTRY (fail_always) + TEST_ENTRY (pass_always) +#endif +TASK_LIST_END diff --git a/3rdparty/libuv/test/test-loop-alive.c b/3rdparty/libuv/test/test-loop-alive.c new file mode 100644 index 00000000000..cf4d301930c --- /dev/null +++ b/3rdparty/libuv/test/test-loop-alive.c @@ -0,0 +1,67 @@ +/* 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 void timer_cb(uv_timer_t* handle) { + ASSERT(handle); +} + + +static uv_work_t work_req; + +static void work_cb(uv_work_t* req) { + ASSERT(req); +} + +static void after_work_cb(uv_work_t* req, int status) { + ASSERT(req); + ASSERT(status == 0); +} + + +TEST_IMPL(loop_alive) { + int r; + ASSERT(!uv_loop_alive(uv_default_loop())); + + /* loops with handles are alive */ + uv_timer_init(uv_default_loop(), &timer_handle); + uv_timer_start(&timer_handle, timer_cb, 100, 0); + ASSERT(uv_loop_alive(uv_default_loop())); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(!uv_loop_alive(uv_default_loop())); + + /* loops with requests are alive */ + r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); + ASSERT(r == 0); + ASSERT(uv_loop_alive(uv_default_loop())); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(!uv_loop_alive(uv_default_loop())); + + return 0; +} diff --git a/3rdparty/libuv/test/test-loop-close.c b/3rdparty/libuv/test/test-loop-close.c new file mode 100644 index 00000000000..5aec234ed03 --- /dev/null +++ b/3rdparty/libuv/test/test-loop-close.c @@ -0,0 +1,53 @@ +/* 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 void timer_cb(uv_timer_t* handle) { + ASSERT(handle); + uv_stop(handle->loop); +} + + +TEST_IMPL(loop_close) { + int r; + uv_loop_t loop; + + ASSERT(0 == uv_loop_init(&loop)); + + uv_timer_init(&loop, &timer_handle); + uv_timer_start(&timer_handle, timer_cb, 100, 100); + + ASSERT(UV_EBUSY == uv_loop_close(&loop)); + + uv_run(&loop, UV_RUN_DEFAULT); + + uv_close((uv_handle_t*) &timer_handle, NULL); + r = uv_run(&loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(0 == uv_loop_close(&loop)); + + return 0; +} diff --git a/3rdparty/libuv/test/test-loop-configure.c b/3rdparty/libuv/test/test-loop-configure.c new file mode 100644 index 00000000000..d057c1ed8a7 --- /dev/null +++ b/3rdparty/libuv/test/test-loop-configure.c @@ -0,0 +1,38 @@ +/* Copyright (c) 2014, Ben Noordhuis <info@bnoordhuis.nl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static void timer_cb(uv_timer_t* handle) { + uv_close((uv_handle_t*) handle, NULL); +} + + +TEST_IMPL(loop_configure) { + uv_timer_t timer_handle; + uv_loop_t loop; + ASSERT(0 == uv_loop_init(&loop)); +#ifdef _WIN32 + ASSERT(UV_ENOSYS == uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, 0)); +#else + ASSERT(0 == uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, SIGPROF)); +#endif + ASSERT(0 == uv_timer_init(&loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 10, 0)); + ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT(0 == uv_loop_close(&loop)); + return 0; +} diff --git a/3rdparty/libuv/test/test-loop-handles.c b/3rdparty/libuv/test/test-loop-handles.c new file mode 100644 index 00000000000..c3e8498ae90 --- /dev/null +++ b/3rdparty/libuv/test/test-loop-handles.c @@ -0,0 +1,337 @@ +/* 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. + */ + +/* Tests commented out with XXX are ones that are failing on Linux */ + +/* + * Purpose of this test is to check semantics of starting and stopping + * prepare, check and idle watchers. + * + * - A watcher must be able to safely stop or close itself; + * - Once a watcher is stopped or closed its callback should never be called. + * - If a watcher is closed, it is implicitly stopped and its close_cb should + * be called exactly once. + * - A watcher can safely start and stop other watchers of the same type. + * - Prepare and check watchers are called once per event loop iterations. + * - All active idle watchers are queued when the event loop has no more work + * to do. This is done repeatedly until all idle watchers are inactive. + * - If a watcher starts another watcher of the same type its callback is not + * immediately queued. For check and prepare watchers, that means that if + * a watcher makes another of the same type active, it'll not be called until + * the next event loop iteration. For idle. watchers this means that the + * newly activated idle watcher might not be queued immediately. + * - Prepare, check, idle watchers keep the event loop alive even when they're + * not active. + * + * This is what the test globally does: + * + * - prepare_1 is always active and counts event loop iterations. It also + * creates and starts prepare_2 every other iteration. Finally it verifies + * that no idle watchers are active before polling. + * - prepare_2 is started by prepare_1 every other iteration. It immediately + * stops itself. It verifies that a watcher is not queued immediately + * if created by another watcher of the same type. + * - There's a check watcher that stops the event loop after a certain number + * of iterations. It starts a varying number of idle_1 watchers. + * - Idle_1 watchers stop themselves after being called a few times. All idle_1 + * watchers try to start the idle_2 watcher if it is not already started or + * awaiting its close callback. + * - The idle_2 watcher always exists but immediately closes itself after + * being started by a check_1 watcher. It verifies that a watcher is + * implicitly stopped when closed, and that a watcher can close itself + * safely. + * - There is a repeating timer. It does not keep the event loop alive + * (ev_unref) but makes sure that the loop keeps polling the system for + * events. + */ + + +#include "uv.h" +#include "task.h" + +#include <math.h> + + +#define IDLE_COUNT 7 +#define ITERATIONS 21 +#define TIMEOUT 100 + + +static uv_prepare_t prepare_1_handle; +static uv_prepare_t prepare_2_handle; + +static uv_check_t check_handle; + +static uv_idle_t idle_1_handles[IDLE_COUNT]; +static uv_idle_t idle_2_handle; + +static uv_timer_t timer_handle; + + +static int loop_iteration = 0; + +static int prepare_1_cb_called = 0; +static int prepare_1_close_cb_called = 0; + +static int prepare_2_cb_called = 0; +static int prepare_2_close_cb_called = 0; + +static int check_cb_called = 0; +static int check_close_cb_called = 0; + +static int idle_1_cb_called = 0; +static int idle_1_close_cb_called = 0; +static int idles_1_active = 0; + +static int idle_2_cb_called = 0; +static int idle_2_close_cb_called = 0; +static int idle_2_cb_started = 0; +static int idle_2_is_active = 0; + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer_handle); +} + + +static void idle_2_close_cb(uv_handle_t* handle) { + fprintf(stderr, "%s", "IDLE_2_CLOSE_CB\n"); + fflush(stderr); + + ASSERT(handle == (uv_handle_t*)&idle_2_handle); + + ASSERT(idle_2_is_active); + + idle_2_close_cb_called++; + idle_2_is_active = 0; +} + + +static void idle_2_cb(uv_idle_t* handle) { + fprintf(stderr, "%s", "IDLE_2_CB\n"); + fflush(stderr); + + ASSERT(handle == &idle_2_handle); + + idle_2_cb_called++; + + uv_close((uv_handle_t*)handle, idle_2_close_cb); +} + + +static void idle_1_cb(uv_idle_t* handle) { + int r; + + fprintf(stderr, "%s", "IDLE_1_CB\n"); + fflush(stderr); + + ASSERT(handle != NULL); + ASSERT(idles_1_active > 0); + + /* Init idle_2 and make it active */ + if (!idle_2_is_active && !uv_is_closing((uv_handle_t*)&idle_2_handle)) { + r = uv_idle_init(uv_default_loop(), &idle_2_handle); + ASSERT(r == 0); + r = uv_idle_start(&idle_2_handle, idle_2_cb); + ASSERT(r == 0); + idle_2_is_active = 1; + idle_2_cb_started++; + } + + idle_1_cb_called++; + + if (idle_1_cb_called % 5 == 0) { + r = uv_idle_stop((uv_idle_t*)handle); + ASSERT(r == 0); + idles_1_active--; + } +} + + +static void idle_1_close_cb(uv_handle_t* handle) { + fprintf(stderr, "%s", "IDLE_1_CLOSE_CB\n"); + fflush(stderr); + + ASSERT(handle != NULL); + + idle_1_close_cb_called++; +} + + +static void prepare_1_close_cb(uv_handle_t* handle) { + fprintf(stderr, "%s", "PREPARE_1_CLOSE_CB"); + fflush(stderr); + ASSERT(handle == (uv_handle_t*)&prepare_1_handle); + + prepare_1_close_cb_called++; +} + + +static void check_close_cb(uv_handle_t* handle) { + fprintf(stderr, "%s", "CHECK_CLOSE_CB\n"); + fflush(stderr); + ASSERT(handle == (uv_handle_t*)&check_handle); + + check_close_cb_called++; +} + + +static void prepare_2_close_cb(uv_handle_t* handle) { + fprintf(stderr, "%s", "PREPARE_2_CLOSE_CB\n"); + fflush(stderr); + ASSERT(handle == (uv_handle_t*)&prepare_2_handle); + + prepare_2_close_cb_called++; +} + + +static void check_cb(uv_check_t* handle) { + int i, r; + + fprintf(stderr, "%s", "CHECK_CB\n"); + fflush(stderr); + ASSERT(handle == &check_handle); + + if (loop_iteration < ITERATIONS) { + /* Make some idle watchers active */ + for (i = 0; i < 1 + (loop_iteration % IDLE_COUNT); i++) { + r = uv_idle_start(&idle_1_handles[i], idle_1_cb); + ASSERT(r == 0); + idles_1_active++; + } + + } else { + /* End of the test - close all handles */ + uv_close((uv_handle_t*)&prepare_1_handle, prepare_1_close_cb); + uv_close((uv_handle_t*)&check_handle, check_close_cb); + uv_close((uv_handle_t*)&prepare_2_handle, prepare_2_close_cb); + + for (i = 0; i < IDLE_COUNT; i++) { + uv_close((uv_handle_t*)&idle_1_handles[i], idle_1_close_cb); + } + + /* This handle is closed/recreated every time, close it only if it is */ + /* active.*/ + if (idle_2_is_active) { + uv_close((uv_handle_t*)&idle_2_handle, idle_2_close_cb); + } + } + + check_cb_called++; +} + + +static void prepare_2_cb(uv_prepare_t* handle) { + int r; + + fprintf(stderr, "%s", "PREPARE_2_CB\n"); + fflush(stderr); + ASSERT(handle == &prepare_2_handle); + + /* prepare_2 gets started by prepare_1 when (loop_iteration % 2 == 0), */ + /* and it stops itself immediately. A started watcher is not queued */ + /* until the next round, so when this callback is made */ + /* (loop_iteration % 2 == 0) cannot be true. */ + ASSERT(loop_iteration % 2 != 0); + + r = uv_prepare_stop((uv_prepare_t*)handle); + ASSERT(r == 0); + + prepare_2_cb_called++; +} + + +static void prepare_1_cb(uv_prepare_t* handle) { + int r; + + fprintf(stderr, "%s", "PREPARE_1_CB\n"); + fflush(stderr); + ASSERT(handle == &prepare_1_handle); + + if (loop_iteration % 2 == 0) { + r = uv_prepare_start(&prepare_2_handle, prepare_2_cb); + ASSERT(r == 0); + } + + prepare_1_cb_called++; + loop_iteration++; + + printf("Loop iteration %d of %d.\n", loop_iteration, ITERATIONS); +} + + +TEST_IMPL(loop_handles) { + int i; + int r; + + r = uv_prepare_init(uv_default_loop(), &prepare_1_handle); + ASSERT(r == 0); + r = uv_prepare_start(&prepare_1_handle, prepare_1_cb); + ASSERT(r == 0); + + r = uv_check_init(uv_default_loop(), &check_handle); + ASSERT(r == 0); + r = uv_check_start(&check_handle, check_cb); + ASSERT(r == 0); + + /* initialize only, prepare_2 is started by prepare_1_cb */ + r = uv_prepare_init(uv_default_loop(), &prepare_2_handle); + ASSERT(r == 0); + + for (i = 0; i < IDLE_COUNT; i++) { + /* initialize only, idle_1 handles are started by check_cb */ + r = uv_idle_init(uv_default_loop(), &idle_1_handles[i]); + ASSERT(r == 0); + } + + /* don't init or start idle_2, both is done by idle_1_cb */ + + /* the timer callback is there to keep the event loop polling */ + /* unref it as it is not supposed to keep the loop alive */ + r = uv_timer_init(uv_default_loop(), &timer_handle); + ASSERT(r == 0); + r = uv_timer_start(&timer_handle, timer_cb, TIMEOUT, TIMEOUT); + ASSERT(r == 0); + uv_unref((uv_handle_t*)&timer_handle); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(loop_iteration == ITERATIONS); + + ASSERT(prepare_1_cb_called == ITERATIONS); + ASSERT(prepare_1_close_cb_called == 1); + + ASSERT(prepare_2_cb_called == floor(ITERATIONS / 2.0)); + ASSERT(prepare_2_close_cb_called == 1); + + ASSERT(check_cb_called == ITERATIONS); + ASSERT(check_close_cb_called == 1); + + /* idle_1_cb should be called a lot */ + ASSERT(idle_1_close_cb_called == IDLE_COUNT); + + ASSERT(idle_2_close_cb_called == idle_2_cb_started); + ASSERT(idle_2_is_active == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-loop-stop.c b/3rdparty/libuv/test/test-loop-stop.c new file mode 100644 index 00000000000..14b8c11186c --- /dev/null +++ b/3rdparty/libuv/test/test-loop-stop.c @@ -0,0 +1,71 @@ +/* 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_prepare_t prepare_handle; +static uv_timer_t timer_handle; +static int prepare_called = 0; +static int timer_called = 0; +static int num_ticks = 10; + + +static void prepare_cb(uv_prepare_t* handle) { + ASSERT(handle == &prepare_handle); + prepare_called++; + if (prepare_called == num_ticks) + uv_prepare_stop(handle); +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer_handle); + timer_called++; + if (timer_called == 1) + uv_stop(uv_default_loop()); + else if (timer_called == num_ticks) + uv_timer_stop(handle); +} + + +TEST_IMPL(loop_stop) { + int r; + uv_prepare_init(uv_default_loop(), &prepare_handle); + uv_prepare_start(&prepare_handle, prepare_cb); + uv_timer_init(uv_default_loop(), &timer_handle); + uv_timer_start(&timer_handle, timer_cb, 100, 100); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r != 0); + ASSERT(timer_called == 1); + + r = uv_run(uv_default_loop(), UV_RUN_NOWAIT); + ASSERT(r != 0); + ASSERT(prepare_called > 1); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(timer_called == 10); + ASSERT(prepare_called == 10); + + return 0; +} diff --git a/3rdparty/libuv/test/test-loop-time.c b/3rdparty/libuv/test/test-loop-time.c new file mode 100644 index 00000000000..a2db42cceec --- /dev/null +++ b/3rdparty/libuv/test/test-loop-time.c @@ -0,0 +1,63 @@ +/* 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" + + +TEST_IMPL(loop_update_time) { + uint64_t start; + + start = uv_now(uv_default_loop()); + while (uv_now(uv_default_loop()) - start < 1000) + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +static void cb(uv_timer_t* timer) { + uv_close((uv_handle_t*)timer, NULL); +} + +TEST_IMPL(loop_backend_timeout) { + uv_loop_t *loop = uv_default_loop(); + uv_timer_t timer; + int r; + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + ASSERT(!uv_loop_alive(loop)); + ASSERT(uv_backend_timeout(loop) == 0); + + r = uv_timer_start(&timer, cb, 1000, 0); /* 1 sec */ + ASSERT(r == 0); + ASSERT(uv_backend_timeout(loop) > 100); /* 0.1 sec */ + ASSERT(uv_backend_timeout(loop) <= 1000); /* 1 sec */ + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(uv_backend_timeout(loop) == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-multiple-listen.c b/3rdparty/libuv/test/test-multiple-listen.c new file mode 100644 index 00000000000..4ae5fa67b3a --- /dev/null +++ b/3rdparty/libuv/test/test-multiple-listen.c @@ -0,0 +1,109 @@ +/* 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 connection_cb_called = 0; +static int close_cb_called = 0; +static int connect_cb_called = 0; +static uv_tcp_t server; +static uv_tcp_t client; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT(status == 0); + uv_close((uv_handle_t*)&server, close_cb); + connection_cb_called++; +} + + +static void start_server(void) { + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server, 128, connection_cb); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server, 128, connection_cb); + ASSERT(r == 0); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + free(req); + uv_close((uv_handle_t*)&client, close_cb); + connect_cb_called++; +} + + +static void client_connect(void) { + struct sockaddr_in addr; + uv_connect_t* connect_req = malloc(sizeof *connect_req); + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT(connect_req != NULL); + + r = uv_tcp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_tcp_connect(connect_req, + &client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); +} + + + +TEST_IMPL(multiple_listen) { + start_server(); + + client_connect(); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(connection_cb_called == 1); + ASSERT(connect_cb_called == 1); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-mutexes.c b/3rdparty/libuv/test/test-mutexes.c new file mode 100644 index 00000000000..af5e4e88a22 --- /dev/null +++ b/3rdparty/libuv/test/test-mutexes.c @@ -0,0 +1,162 @@ +/* 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 uv_cond_t condvar; +static uv_mutex_t mutex; +static uv_rwlock_t rwlock; +static int step; + +/* The mutex and rwlock tests are really poor. + * They're very basic sanity checks and nothing more. + * Apologies if that rhymes. + */ + +TEST_IMPL(thread_mutex) { + uv_mutex_t mutex; + int r; + + r = uv_mutex_init(&mutex); + ASSERT(r == 0); + + uv_mutex_lock(&mutex); + uv_mutex_unlock(&mutex); + uv_mutex_destroy(&mutex); + + return 0; +} + + +TEST_IMPL(thread_rwlock) { + uv_rwlock_t rwlock; + int r; + + r = uv_rwlock_init(&rwlock); + ASSERT(r == 0); + + uv_rwlock_rdlock(&rwlock); + uv_rwlock_rdunlock(&rwlock); + uv_rwlock_wrlock(&rwlock); + uv_rwlock_wrunlock(&rwlock); + uv_rwlock_destroy(&rwlock); + + return 0; +} + + +/* Call when holding |mutex|. */ +static void synchronize_nowait(void) { + step += 1; + uv_cond_signal(&condvar); +} + + +/* Call when holding |mutex|. */ +static void synchronize(void) { + int current; + + synchronize_nowait(); + /* Wait for the other thread. Guard against spurious wakeups. */ + for (current = step; current == step; uv_cond_wait(&condvar, &mutex)); + ASSERT(step == current + 1); +} + + +static void thread_rwlock_trylock_peer(void* unused) { + (void) &unused; + + uv_mutex_lock(&mutex); + + /* Write lock held by other thread. */ + ASSERT(UV_EBUSY == uv_rwlock_tryrdlock(&rwlock)); + ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + synchronize(); + + /* Read lock held by other thread. */ + ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + uv_rwlock_rdunlock(&rwlock); + ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + synchronize(); + + /* Acquire write lock. */ + ASSERT(0 == uv_rwlock_trywrlock(&rwlock)); + synchronize(); + + /* Release write lock and acquire read lock. */ + uv_rwlock_wrunlock(&rwlock); + ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + synchronize(); + + uv_rwlock_rdunlock(&rwlock); + synchronize_nowait(); /* Signal main thread we're going away. */ + uv_mutex_unlock(&mutex); +} + + +TEST_IMPL(thread_rwlock_trylock) { + uv_thread_t thread; + + ASSERT(0 == uv_cond_init(&condvar)); + ASSERT(0 == uv_mutex_init(&mutex)); + ASSERT(0 == uv_rwlock_init(&rwlock)); + + uv_mutex_lock(&mutex); + ASSERT(0 == uv_thread_create(&thread, thread_rwlock_trylock_peer, NULL)); + + /* Hold write lock. */ + ASSERT(0 == uv_rwlock_trywrlock(&rwlock)); + synchronize(); /* Releases the mutex to the other thread. */ + + /* Release write lock and acquire read lock. Pthreads doesn't support + * the notion of upgrading or downgrading rwlocks, so neither do we. + */ + uv_rwlock_wrunlock(&rwlock); + ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + synchronize(); + + /* Release read lock. */ + uv_rwlock_rdunlock(&rwlock); + synchronize(); + + /* Write lock held by other thread. */ + ASSERT(UV_EBUSY == uv_rwlock_tryrdlock(&rwlock)); + ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + synchronize(); + + /* Read lock held by other thread. */ + ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + uv_rwlock_rdunlock(&rwlock); + ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + synchronize(); + + ASSERT(0 == uv_thread_join(&thread)); + uv_rwlock_destroy(&rwlock); + uv_mutex_unlock(&mutex); + uv_mutex_destroy(&mutex); + uv_cond_destroy(&condvar); + + return 0; +} diff --git a/3rdparty/libuv/test/test-osx-select.c b/3rdparty/libuv/test/test-osx-select.c new file mode 100644 index 00000000000..a0afda9181e --- /dev/null +++ b/3rdparty/libuv/test/test-osx-select.c @@ -0,0 +1,140 @@ +/* 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" + +#ifdef __APPLE__ + +#include <sys/ioctl.h> +#include <string.h> + +static int read_count; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + static char slab[1024]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + fprintf(stdout, "got data %d\n", ++read_count); + fflush(stdout); + + if (read_count == 3) + uv_close((uv_handle_t*) stream, NULL); +} + + +TEST_IMPL(osx_select) { + int r; + int fd; + size_t i; + size_t len; + const char* str; + uv_tty_t tty; + + fd = open("/dev/tty", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); + fflush(stderr); + return TEST_SKIP; + } + + r = uv_tty_init(uv_default_loop(), &tty, fd, 1); + ASSERT(r == 0); + + uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); + + /* Emulate user-input */ + str = "got some input\n" + "with a couple of lines\n" + "feel pretty happy\n"; + for (i = 0, len = strlen(str); i < len; i++) { + r = ioctl(fd, TIOCSTI, str + i); + ASSERT(r == 0); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(read_count == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(osx_select_many_fds) { + int r; + int fd; + size_t i; + size_t len; + const char* str; + struct sockaddr_in addr; + uv_tty_t tty; + uv_tcp_t tcps[1500]; + + TEST_FILE_LIMIT(ARRAY_SIZE(tcps) + 100); + + r = uv_ip4_addr("127.0.0.1", 0, &addr); + ASSERT(r == 0); + + for (i = 0; i < ARRAY_SIZE(tcps); i++) { + r = uv_tcp_init(uv_default_loop(), &tcps[i]); + ASSERT(r == 0); + r = uv_tcp_bind(&tcps[i], (const struct sockaddr *) &addr, 0); + ASSERT(r == 0); + uv_unref((uv_handle_t*) &tcps[i]); + } + + fd = open("/dev/tty", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); + fflush(stderr); + return TEST_SKIP; + } + + r = uv_tty_init(uv_default_loop(), &tty, fd, 1); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); + ASSERT(r == 0); + + /* Emulate user-input */ + str = "got some input\n" + "with a couple of lines\n" + "feel pretty happy\n"; + for (i = 0, len = strlen(str); i < len; i++) { + r = ioctl(fd, TIOCSTI, str + i); + ASSERT(r == 0); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(read_count == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* __APPLE__ */ diff --git a/3rdparty/libuv/test/test-pass-always.c b/3rdparty/libuv/test/test-pass-always.c new file mode 100644 index 00000000000..4fb58ff94be --- /dev/null +++ b/3rdparty/libuv/test/test-pass-always.c @@ -0,0 +1,28 @@ +/* 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 "task.h" + + +TEST_IMPL(pass_always) { + /* This test always passes. It is used to test the test runner. */ + return 0; +} diff --git a/3rdparty/libuv/test/test-ping-pong.c b/3rdparty/libuv/test/test-ping-pong.c new file mode 100644 index 00000000000..c074178541b --- /dev/null +++ b/3rdparty/libuv/test/test-ping-pong.c @@ -0,0 +1,270 @@ +/* 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 <stdlib.h> +#include <stdio.h> + +static int completed_pingers = 0; + +#define NUM_PINGS 1000 + +/* 64 bytes is enough for a pinger */ +#define BUFSIZE 10240 + +static char PING[] = "PING\n"; +static int pinger_on_connect_count; + + +typedef struct { + int pongs; + int state; + union { + uv_tcp_t tcp; + uv_pipe_t pipe; + } stream; + uv_connect_t connect_req; + char read_buffer[BUFSIZE]; +} pinger_t; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; +} + + +static void pinger_on_close(uv_handle_t* handle) { + pinger_t* pinger = (pinger_t*)handle->data; + + ASSERT(NUM_PINGS == pinger->pongs); + + free(pinger); + + completed_pingers++; +} + + +static void pinger_after_write(uv_write_t *req, int status) { + ASSERT(status == 0); + free(req); +} + + +static void pinger_write_ping(pinger_t* pinger) { + uv_write_t *req; + uv_buf_t buf; + + buf = uv_buf_init(PING, sizeof(PING) - 1); + + req = malloc(sizeof(*req)); + if (uv_write(req, + (uv_stream_t*) &pinger->stream.tcp, + &buf, + 1, + pinger_after_write)) { + FATAL("uv_write failed"); + } + + puts("PING"); +} + + +static void pinger_read_cb(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) { + ssize_t i; + pinger_t* pinger; + + pinger = (pinger_t*)stream->data; + + if (nread < 0) { + ASSERT(nread == UV_EOF); + + puts("got EOF"); + free(buf->base); + + uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); + + return; + } + + /* Now we count the pings */ + for (i = 0; i < nread; i++) { + ASSERT(buf->base[i] == PING[pinger->state]); + pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); + + if (pinger->state != 0) + continue; + + printf("PONG %d\n", pinger->pongs); + pinger->pongs++; + + if (pinger->pongs < NUM_PINGS) { + pinger_write_ping(pinger); + } else { + uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); + break; + } + } + + free(buf->base); +} + + +static void pinger_on_connect(uv_connect_t *req, int status) { + pinger_t *pinger = (pinger_t*)req->handle->data; + + pinger_on_connect_count++; + + ASSERT(status == 0); + + ASSERT(1 == uv_is_readable(req->handle)); + ASSERT(1 == uv_is_writable(req->handle)); + ASSERT(0 == uv_is_closing((uv_handle_t *) req->handle)); + + pinger_write_ping(pinger); + + uv_read_start((uv_stream_t*)(req->handle), alloc_cb, pinger_read_cb); +} + + +/* same ping-pong test, but using IPv6 connection */ +static void tcp_pinger_v6_new(void) { + int r; + struct sockaddr_in6 server_addr; + pinger_t *pinger; + + + ASSERT(0 ==uv_ip6_addr("::1", TEST_PORT, &server_addr)); + pinger = malloc(sizeof(*pinger)); + ASSERT(pinger != NULL); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connect to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); + pinger->stream.tcp.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + r = uv_tcp_connect(&pinger->connect_req, + &pinger->stream.tcp, + (const struct sockaddr*) &server_addr, + pinger_on_connect); + ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +static void tcp_pinger_new(void) { + int r; + struct sockaddr_in server_addr; + pinger_t *pinger; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + pinger = malloc(sizeof(*pinger)); + ASSERT(pinger != NULL); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connect to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); + pinger->stream.tcp.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + r = uv_tcp_connect(&pinger->connect_req, + &pinger->stream.tcp, + (const struct sockaddr*) &server_addr, + pinger_on_connect); + ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +static void pipe_pinger_new(void) { + int r; + pinger_t *pinger; + + pinger = (pinger_t*)malloc(sizeof(*pinger)); + ASSERT(pinger != NULL); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connect to the server and do NUM_PINGS ping-pongs. */ + r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0); + pinger->stream.pipe.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + + uv_pipe_connect(&pinger->connect_req, &pinger->stream.pipe, TEST_PIPENAME, + pinger_on_connect); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +TEST_IMPL(tcp_ping_pong) { + tcp_pinger_new(); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(completed_pingers == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_ping_pong_v6) { + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + tcp_pinger_v6_new(); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(completed_pingers == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_ping_pong) { + pipe_pinger_new(); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(completed_pingers == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-bind-error.c b/3rdparty/libuv/test/test-pipe-bind-error.c new file mode 100644 index 00000000000..38b57db6991 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-bind-error.c @@ -0,0 +1,136 @@ +/* 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> + + +#ifdef _WIN32 +# define BAD_PIPENAME "bad-pipe" +#else +# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" +#endif + + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(pipe_bind_error_addrinuse) { + uv_pipe_t server1, server2; + int r; + + r = uv_pipe_init(uv_default_loop(), &server1, 0); + ASSERT(r == 0); + r = uv_pipe_bind(&server1, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &server2, 0); + ASSERT(r == 0); + r = uv_pipe_bind(&server2, TEST_PIPENAME); + ASSERT(r == UV_EADDRINUSE); + + r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server1, close_cb); + uv_close((uv_handle_t*)&server2, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_bind_error_addrnotavail) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server, 0); + ASSERT(r == 0); + + r = uv_pipe_bind(&server, BAD_PIPENAME); + ASSERT(r == UV_EACCES); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_bind_error_inval) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server, 0); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME_2); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_listen_without_bind) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-close-stdout-read-stdin.c b/3rdparty/libuv/test/test-pipe-close-stdout-read-stdin.c new file mode 100644 index 00000000000..ee8bb2a9a8b --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-close-stdout-read-stdin.c @@ -0,0 +1,104 @@ +/* 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. + */ + +#ifndef _WIN32 + +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/types.h> + +#include "uv.h" +#include "task.h" + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t* buf) +{ + static char buffer[1024]; + + buf->base = buffer; + buf->len = sizeof(buffer); +} + +void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t* buf) +{ + if (nread < 0) { + uv_close((uv_handle_t*)stream, NULL); + return; + } +} + +/* + * This test is a reproduction of joyent/libuv#1419 . + */ +TEST_IMPL(pipe_close_stdout_read_stdin) { + int r = -1; + int pid; + int fd[2]; + int status; + uv_pipe_t stdin_pipe; + + r = pipe(fd); + ASSERT(r == 0); + + if ((pid = fork()) == 0) { + /* + * Make the read side of the pipe our stdin. + * The write side will be closed by the parent process. + */ + close(fd[1]); + close(0); + r = dup(fd[0]); + ASSERT(r != -1); + + /* Create a stream that reads from the pipe. */ + r = uv_pipe_init(uv_default_loop(), (uv_pipe_t *)&stdin_pipe, 0); + ASSERT(r == 0); + + r = uv_pipe_open((uv_pipe_t *)&stdin_pipe, 0); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t *)&stdin_pipe, alloc_buffer, read_stdin); + ASSERT(r == 0); + + /* + * Because the other end of the pipe was closed, there should + * be no event left to process after one run of the event loop. + * Otherwise, it means that events were not processed correctly. + */ + ASSERT(uv_run(uv_default_loop(), UV_RUN_NOWAIT) == 0); + } else { + /* + * Close both ends of the pipe so that the child + * get a POLLHUP event when it tries to read from + * the other end. + */ + close(fd[1]); + close(fd[0]); + + waitpid(pid, &status, 0); + ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0); + } + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* ifndef _WIN32 */ diff --git a/3rdparty/libuv/test/test-pipe-connect-error.c b/3rdparty/libuv/test/test-pipe-connect-error.c new file mode 100644 index 00000000000..ebb2a6ca826 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-connect-error.c @@ -0,0 +1,95 @@ +/* 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> + + +#ifdef _WIN32 +# define BAD_PIPENAME "bad-pipe" +#else +# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" +#endif + + +static int close_cb_called = 0; +static int connect_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* connect_req, int status) { + ASSERT(status == UV_ENOENT); + uv_close((uv_handle_t*)connect_req->handle, close_cb); + connect_cb_called++; +} + + +static void connect_cb_file(uv_connect_t* connect_req, int status) { + ASSERT(status == UV_ENOTSOCK || status == UV_ECONNREFUSED); + uv_close((uv_handle_t*)connect_req->handle, close_cb); + connect_cb_called++; +} + + +TEST_IMPL(pipe_connect_bad_name) { + uv_pipe_t client; + uv_connect_t req; + int r; + + r = uv_pipe_init(uv_default_loop(), &client, 0); + ASSERT(r == 0); + uv_pipe_connect(&req, &client, BAD_PIPENAME, connect_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + ASSERT(connect_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_connect_to_file) { + const char* path = "test/fixtures/empty_file"; + uv_pipe_t client; + uv_connect_t req; + int r; + + r = uv_pipe_init(uv_default_loop(), &client, 0); + ASSERT(r == 0); + uv_pipe_connect(&req, &client, path, connect_cb_file); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + ASSERT(connect_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-connect-multiple.c b/3rdparty/libuv/test/test-pipe-connect-multiple.c new file mode 100644 index 00000000000..3de5a9a0bf4 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-connect-multiple.c @@ -0,0 +1,104 @@ +/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>. + * 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 connection_cb_called = 0; +static int connect_cb_called = 0; + +#define NUM_CLIENTS 4 + +typedef struct { + uv_pipe_t pipe_handle; + uv_connect_t conn_req; +} client_t; + +static uv_pipe_t server_handle; +static client_t clients[NUM_CLIENTS]; +static uv_pipe_t connections[NUM_CLIENTS]; + + +static void connection_cb(uv_stream_t* server, int status) { + int r; + uv_pipe_t* conn; + ASSERT(status == 0); + + conn = &connections[connection_cb_called]; + r = uv_pipe_init(server->loop, conn, 0); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)conn); + ASSERT(r == 0); + + if (++connection_cb_called == NUM_CLIENTS && + connect_cb_called == NUM_CLIENTS) { + uv_stop(server->loop); + } +} + + +static void connect_cb(uv_connect_t* connect_req, int status) { + ASSERT(status == 0); + if (++connect_cb_called == NUM_CLIENTS && + connection_cb_called == NUM_CLIENTS) { + uv_stop(connect_req->handle->loop); + } +} + + +TEST_IMPL(pipe_connect_multiple) { + int i; + int r; + uv_loop_t* loop; + + loop = uv_default_loop(); + + r = uv_pipe_init(loop, &server_handle, 0); + ASSERT(r == 0); + + r = uv_pipe_bind(&server_handle, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server_handle, 128, connection_cb); + ASSERT(r == 0); + + for (i = 0; i < NUM_CLIENTS; i++) { + r = uv_pipe_init(loop, &clients[i].pipe_handle, 0); + ASSERT(r == 0); + uv_pipe_connect(&clients[i].conn_req, + &clients[i].pipe_handle, + TEST_PIPENAME, + connect_cb); + } + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(connection_cb_called == NUM_CLIENTS); + ASSERT(connect_cb_called == NUM_CLIENTS); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-connect-prepare.c b/3rdparty/libuv/test/test-pipe-connect-prepare.c new file mode 100644 index 00000000000..a86e7284a79 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-connect-prepare.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>. + * 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> + + +#ifdef _WIN32 +# define BAD_PIPENAME "bad-pipe" +#else +# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" +#endif + + +static int close_cb_called = 0; +static int connect_cb_called = 0; + +static uv_pipe_t pipe_handle; +static uv_prepare_t prepare_handle; +static uv_connect_t conn_req; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* connect_req, int status) { + ASSERT(status == UV_ENOENT); + connect_cb_called++; + uv_close((uv_handle_t*)&prepare_handle, close_cb); + uv_close((uv_handle_t*)&pipe_handle, close_cb); +} + + +static void prepare_cb(uv_prepare_t* handle) { + ASSERT(handle == &prepare_handle); + uv_pipe_connect(&conn_req, &pipe_handle, BAD_PIPENAME, connect_cb); +} + + +TEST_IMPL(pipe_connect_on_prepare) { + int r; + + r = uv_pipe_init(uv_default_loop(), &pipe_handle, 0); + ASSERT(r == 0); + + r = uv_prepare_init(uv_default_loop(), &prepare_handle); + ASSERT(r == 0); + r = uv_prepare_start(&prepare_handle, prepare_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 2); + ASSERT(connect_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-getsockname.c b/3rdparty/libuv/test/test-pipe-getsockname.c new file mode 100644 index 00000000000..5e036f9d528 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-getsockname.c @@ -0,0 +1,263 @@ +/* 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 <string.h> + +#if defined(__linux__) + #include <sys/socket.h> + #include <sys/un.h> +#endif + +#ifndef _WIN32 +# include <unistd.h> /* close */ +#else +# include <fcntl.h> +#endif + +static uv_pipe_t pipe_client; +static uv_pipe_t pipe_server; +static uv_connect_t connect_req; + +static int pipe_close_cb_called = 0; +static int pipe_client_connect_cb_called = 0; + + +static void pipe_close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*) &pipe_client || + handle == (uv_handle_t*) &pipe_server); + pipe_close_cb_called++; +} + + +static void pipe_client_connect_cb(uv_connect_t* req, int status) { + char buf[1024]; + size_t len; + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + len = sizeof buf; + r = uv_pipe_getpeername(&pipe_client, buf, &len); + ASSERT(r == 0); + + ASSERT(buf[len - 1] != 0); + ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&pipe_client, buf, &len); + ASSERT(r == 0 && len == 0); + + pipe_client_connect_cb_called++; + + + uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); + uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); +} + + +static void pipe_server_connection_cb(uv_stream_t* handle, int status) { + /* This function *may* be called, depending on whether accept or the + * connection callback is called first. + */ + ASSERT(status == 0); +} + + +TEST_IMPL(pipe_getsockname) { + uv_loop_t* loop; + char buf[1024]; + size_t len; + int r; + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + r = uv_pipe_init(loop, &pipe_server, 0); + ASSERT(r == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&pipe_server, buf, &len); + ASSERT(r == UV_EBADF); + + len = sizeof buf; + r = uv_pipe_getpeername(&pipe_server, buf, &len); + ASSERT(r == UV_EBADF); + + r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); + ASSERT(r == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&pipe_server, buf, &len); + ASSERT(r == 0); + + ASSERT(buf[len - 1] != 0); + ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + + len = sizeof buf; + r = uv_pipe_getpeername(&pipe_server, buf, &len); + ASSERT(r == UV_ENOTCONN); + + r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); + ASSERT(r == 0); + + r = uv_pipe_init(loop, &pipe_client, 0); + ASSERT(r == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&pipe_client, buf, &len); + ASSERT(r == UV_EBADF); + + len = sizeof buf; + r = uv_pipe_getpeername(&pipe_client, buf, &len); + ASSERT(r == UV_EBADF); + + uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); + + len = sizeof buf; + r = uv_pipe_getsockname(&pipe_client, buf, &len); + ASSERT(r == 0 && len == 0); + + len = sizeof buf; + r = uv_pipe_getpeername(&pipe_client, buf, &len); + ASSERT(r == 0); + + ASSERT(buf[len - 1] != 0); + ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(pipe_client_connect_cb_called == 1); + ASSERT(pipe_close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_getsockname_abstract) { +#if defined(__linux__) + char buf[1024]; + size_t len; + int r; + int sock; + struct sockaddr_un sun; + socklen_t sun_len; + char abstract_pipe[] = "\0test-pipe"; + + sock = socket(AF_LOCAL, SOCK_STREAM, 0); + ASSERT(sock != -1); + + sun_len = sizeof sun; + memset(&sun, 0, sun_len); + sun.sun_family = AF_UNIX; + memcpy(sun.sun_path, abstract_pipe, sizeof abstract_pipe); + + r = bind(sock, (struct sockaddr*)&sun, sun_len); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &pipe_server, 0); + ASSERT(r == 0); + r = uv_pipe_open(&pipe_server, sock); + ASSERT(r == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&pipe_server, buf, &len); + ASSERT(r == 0); + + ASSERT(memcmp(buf, abstract_pipe, sizeof abstract_pipe) == 0); + + uv_close((uv_handle_t*)&pipe_server, pipe_close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + close(sock); + + ASSERT(pipe_close_cb_called == 1); + MAKE_VALGRIND_HAPPY(); + return 0; +#else + MAKE_VALGRIND_HAPPY(); + return 0; +#endif +} + +TEST_IMPL(pipe_getsockname_blocking) { +#ifdef _WIN32 + HANDLE readh, writeh; + int readfd; + char buf1[1024], buf2[1024]; + size_t len1, len2; + int r; + + r = CreatePipe(&readh, &writeh, NULL, 65536); + ASSERT(r != 0); + + r = uv_pipe_init(uv_default_loop(), &pipe_client, 0); + ASSERT(r == 0); + readfd = _open_osfhandle((intptr_t)readh, _O_RDONLY); + ASSERT(r != -1); + r = uv_pipe_open(&pipe_client, readfd); + ASSERT(r == 0); + r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); + ASSERT(r == 0); + Sleep(100); + r = uv_read_stop((uv_stream_t*)&pipe_client); + ASSERT(r == 0); + + len1 = sizeof buf1; + r = uv_pipe_getsockname(&pipe_client, buf1, &len1); + ASSERT(r == 0); + ASSERT(buf1[len1 - 1] != 0); + + r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); + ASSERT(r == 0); + Sleep(100); + + len2 = sizeof buf2; + r = uv_pipe_getsockname(&pipe_client, buf2, &len2); + ASSERT(r == 0); + ASSERT(buf2[len2 - 1] != 0); + + r = uv_read_stop((uv_stream_t*)&pipe_client); + ASSERT(r == 0); + + ASSERT(len1 == len2); + ASSERT(memcmp(buf1, buf2, len1) == 0); + + pipe_close_cb_called = 0; + uv_close((uv_handle_t*)&pipe_client, pipe_close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(pipe_close_cb_called == 1); + + _close(readfd); + CloseHandle(writeh); +#endif + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-pending-instances.c b/3rdparty/libuv/test/test-pipe-pending-instances.c new file mode 100644 index 00000000000..b6ff911a0f2 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-pending-instances.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>. + * 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 void connection_cb(uv_stream_t* server, int status) { + ASSERT(0 && "this will never be called"); +} + + +TEST_IMPL(pipe_pending_instances) { + int r; + uv_pipe_t pipe_handle; + uv_loop_t* loop; + + loop = uv_default_loop(); + + r = uv_pipe_init(loop, &pipe_handle, 0); + ASSERT(r == 0); + + uv_pipe_pending_instances(&pipe_handle, 8); + + r = uv_pipe_bind(&pipe_handle, TEST_PIPENAME); + ASSERT(r == 0); + + uv_pipe_pending_instances(&pipe_handle, 16); + + r = uv_listen((uv_stream_t*)&pipe_handle, 128, connection_cb); + ASSERT(r == 0); + + uv_close((uv_handle_t*)&pipe_handle, NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-sendmsg.c b/3rdparty/libuv/test/test-pipe-sendmsg.c new file mode 100644 index 00000000000..f6d893b4494 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-sendmsg.c @@ -0,0 +1,169 @@ +/* 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" + + +#ifndef _WIN32 + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + + +/* NOTE: size should be divisible by 2 */ +static uv_pipe_t incoming[4]; +static unsigned int incoming_count; +static unsigned int close_called; + + +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 void close_cb(uv_handle_t* handle) { + close_called++; +} + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + static char base[1]; + + buf->base = base; + buf->len = sizeof(base); +} + + +static void read_cb(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + uv_pipe_t* p; + uv_pipe_t* inc; + uv_handle_type pending; + unsigned int i; + + p = (uv_pipe_t*) handle; + ASSERT(nread >= 0); + + while (uv_pipe_pending_count(p) != 0) { + pending = uv_pipe_pending_type(p); + ASSERT(pending == UV_NAMED_PIPE); + + ASSERT(incoming_count < ARRAY_SIZE(incoming)); + inc = &incoming[incoming_count++]; + ASSERT(0 == uv_pipe_init(p->loop, inc, 0)); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) inc)); + } + + if (incoming_count != ARRAY_SIZE(incoming)) + return; + + ASSERT(0 == uv_read_stop((uv_stream_t*) p)); + uv_close((uv_handle_t*) p, close_cb); + for (i = 0; i < ARRAY_SIZE(incoming); i++) + uv_close((uv_handle_t*) &incoming[i], close_cb); +} + + +TEST_IMPL(pipe_sendmsg) { + uv_pipe_t p; + int r; + int fds[2]; + int send_fds[ARRAY_SIZE(incoming)]; + struct msghdr msg; + char scratch[64]; + struct cmsghdr *cmsg; + unsigned int i; + uv_buf_t buf; + + ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fds)); + for (i = 0; i < ARRAY_SIZE(send_fds); i += 2) + ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, send_fds + i)); + ASSERT(i == ARRAY_SIZE(send_fds)); + ASSERT(0 == uv_pipe_init(uv_default_loop(), &p, 1)); + ASSERT(0 == uv_pipe_open(&p, fds[1])); + + buf = uv_buf_init("X", 1); + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = (struct iovec*) &buf; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + msg.msg_control = (void*) scratch; + msg.msg_controllen = CMSG_LEN(sizeof(send_fds)); + ASSERT(sizeof(scratch) >= msg.msg_controllen); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = msg.msg_controllen; + + /* silence aliasing warning */ + { + void* pv = CMSG_DATA(cmsg); + int* pi = pv; + for (i = 0; i < ARRAY_SIZE(send_fds); i++) + pi[i] = send_fds[i]; + } + + set_nonblocking(fds[1]); + ASSERT(0 == uv_read_start((uv_stream_t*) &p, alloc_cb, read_cb)); + + do + r = sendmsg(fds[0], &msg, 0); + while (r == -1 && errno == EINTR); + ASSERT(r == 1); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(ARRAY_SIZE(incoming) == incoming_count); + ASSERT(ARRAY_SIZE(incoming) + 1 == close_called); + close(fds[0]); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#else /* !_WIN32 */ + +TEST_IMPL(pipe_sendmsg) { + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* _WIN32 */ diff --git a/3rdparty/libuv/test/test-pipe-server-close.c b/3rdparty/libuv/test/test-pipe-server-close.c new file mode 100644 index 00000000000..1dcdfffaf7c --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-server-close.c @@ -0,0 +1,91 @@ +/* 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 <string.h> +#include <errno.h> + + +static uv_pipe_t pipe_client; +static uv_pipe_t pipe_server; +static uv_connect_t connect_req; + +static int pipe_close_cb_called = 0; +static int pipe_client_connect_cb_called = 0; + + +static void pipe_close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*) &pipe_client || + handle == (uv_handle_t*) &pipe_server); + pipe_close_cb_called++; +} + + +static void pipe_client_connect_cb(uv_connect_t* req, int status) { + ASSERT(req == &connect_req); + ASSERT(status == 0); + + pipe_client_connect_cb_called++; + + uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); + uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); +} + + +static void pipe_server_connection_cb(uv_stream_t* handle, int status) { + /* This function *may* be called, depending on whether accept or the + * connection callback is called first. + */ + ASSERT(status == 0); +} + + +TEST_IMPL(pipe_server_close) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + r = uv_pipe_init(loop, &pipe_server, 0); + ASSERT(r == 0); + + r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); + ASSERT(r == 0); + + r = uv_pipe_init(loop, &pipe_client, 0); + ASSERT(r == 0); + + uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(pipe_client_connect_cb_called == 1); + ASSERT(pipe_close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-pipe-set-non-blocking.c b/3rdparty/libuv/test/test-pipe-set-non-blocking.c new file mode 100644 index 00000000000..fcc9fc0da85 --- /dev/null +++ b/3rdparty/libuv/test/test-pipe-set-non-blocking.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2015, Ben Noordhuis <info@bnoordhuis.nl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#ifdef _WIN32 + +TEST_IMPL(pipe_set_non_blocking) { + RETURN_SKIP("Test not implemented on Windows."); +} + +#else /* !_WIN32 */ + +#include <errno.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +struct thread_ctx { + uv_barrier_t barrier; + int fd; +}; + +static void thread_main(void* arg) { + struct thread_ctx* ctx; + char buf[4096]; + ssize_t n; + + ctx = arg; + uv_barrier_wait(&ctx->barrier); + + do + n = read(ctx->fd, buf, sizeof(buf)); + while (n > 0 || (n == -1 && errno == EINTR)); + + ASSERT(n == 0); +} + +TEST_IMPL(pipe_set_non_blocking) { + struct thread_ctx ctx; + uv_pipe_t pipe_handle; + uv_thread_t thread; + size_t nwritten; + char data[4096]; + uv_buf_t buf; + int fd[2]; + int n; + + ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); + ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1)); + + ctx.fd = fd[1]; + ASSERT(0 == uv_barrier_init(&ctx.barrier, 2)); + ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); + uv_barrier_wait(&ctx.barrier); + + buf.len = sizeof(data); + buf.base = data; + memset(data, '.', sizeof(data)); + + nwritten = 0; + while (nwritten < 10 << 20) { + /* The stream is in blocking mode so uv_try_write() should always succeed + * with the exact number of bytes that we wanted written. + */ + n = uv_try_write((uv_stream_t*) &pipe_handle, &buf, 1); + ASSERT(n == sizeof(data)); + nwritten += n; + } + + uv_close((uv_handle_t*) &pipe_handle, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(0 == uv_thread_join(&thread)); + ASSERT(0 == close(fd[1])); /* fd[0] is closed by uv_close(). */ + uv_barrier_destroy(&ctx.barrier); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* !_WIN32 */ diff --git a/3rdparty/libuv/test/test-platform-output.c b/3rdparty/libuv/test/test-platform-output.c new file mode 100644 index 00000000000..76495e14fd8 --- /dev/null +++ b/3rdparty/libuv/test/test-platform-output.c @@ -0,0 +1,126 @@ +/* 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 <string.h> + + +TEST_IMPL(platform_output) { + char buffer[512]; + size_t rss; + size_t size; + double uptime; + uv_rusage_t rusage; + uv_cpu_info_t* cpus; + uv_interface_address_t* interfaces; + int count; + int i; + int err; + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(err == 0); + printf("uv_get_process_title: %s\n", buffer); + + size = sizeof(buffer); + err = uv_cwd(buffer, &size); + ASSERT(err == 0); + printf("uv_cwd: %s\n", buffer); + + err = uv_resident_set_memory(&rss); + ASSERT(err == 0); + printf("uv_resident_set_memory: %llu\n", (unsigned long long) rss); + + err = uv_uptime(&uptime); + ASSERT(err == 0); + ASSERT(uptime > 0); + printf("uv_uptime: %f\n", uptime); + + err = uv_getrusage(&rusage); + ASSERT(err == 0); + ASSERT(rusage.ru_utime.tv_sec >= 0); + ASSERT(rusage.ru_utime.tv_usec >= 0); + ASSERT(rusage.ru_stime.tv_sec >= 0); + ASSERT(rusage.ru_stime.tv_usec >= 0); + printf("uv_getrusage:\n"); + printf(" user: %llu sec %llu microsec\n", + (unsigned long long) rusage.ru_utime.tv_sec, + (unsigned long long) rusage.ru_utime.tv_usec); + printf(" system: %llu sec %llu microsec\n", + (unsigned long long) rusage.ru_stime.tv_sec, + (unsigned long long) rusage.ru_stime.tv_usec); + + err = uv_cpu_info(&cpus, &count); + ASSERT(err == 0); + + printf("uv_cpu_info:\n"); + for (i = 0; i < count; i++) { + printf(" model: %s\n", cpus[i].model); + printf(" speed: %d\n", cpus[i].speed); + printf(" times.sys: %llu\n", (unsigned long long) cpus[i].cpu_times.sys); + printf(" times.user: %llu\n", + (unsigned long long) cpus[i].cpu_times.user); + printf(" times.idle: %llu\n", + (unsigned long long) cpus[i].cpu_times.idle); + printf(" times.irq: %llu\n", (unsigned long long) cpus[i].cpu_times.irq); + printf(" times.nice: %llu\n", + (unsigned long long) cpus[i].cpu_times.nice); + } + uv_free_cpu_info(cpus, count); + + err = uv_interface_addresses(&interfaces, &count); + ASSERT(err == 0); + + printf("uv_interface_addresses:\n"); + for (i = 0; i < count; i++) { + printf(" name: %s\n", interfaces[i].name); + printf(" internal: %d\n", interfaces[i].is_internal); + printf(" physical address: "); + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned char)interfaces[i].phys_addr[0], + (unsigned char)interfaces[i].phys_addr[1], + (unsigned char)interfaces[i].phys_addr[2], + (unsigned char)interfaces[i].phys_addr[3], + (unsigned char)interfaces[i].phys_addr[4], + (unsigned char)interfaces[i].phys_addr[5]); + + if (interfaces[i].address.address4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].address.address4, buffer, sizeof(buffer)); + } else if (interfaces[i].address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].address.address6, buffer, sizeof(buffer)); + } + + printf(" address: %s\n", buffer); + + if (interfaces[i].netmask.netmask4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].netmask.netmask4, buffer, sizeof(buffer)); + printf(" netmask: %s\n", buffer); + } else if (interfaces[i].netmask.netmask4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].netmask.netmask6, buffer, sizeof(buffer)); + printf(" netmask: %s\n", buffer); + } else { + printf(" netmask: none\n"); + } + } + uv_free_interface_addresses(interfaces, count); + + return 0; +} diff --git a/3rdparty/libuv/test/test-poll-close-doesnt-corrupt-stack.c b/3rdparty/libuv/test/test-poll-close-doesnt-corrupt-stack.c new file mode 100644 index 00000000000..fc2cc004f16 --- /dev/null +++ b/3rdparty/libuv/test/test-poll-close-doesnt-corrupt-stack.c @@ -0,0 +1,114 @@ +/* Copyright Bert Belder, and other libuv 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. + */ + +#ifdef _WIN32 + +#include <errno.h> +#include <stdio.h> + +#include "uv.h" +#include "task.h" + +#ifdef _MSC_VER /* msvc */ +# define NO_INLINE __declspec(noinline) +#else /* gcc */ +# define NO_INLINE __attribute__ ((noinline)) +#endif + + +uv_os_sock_t sock; +uv_poll_t handle; + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* h) { + close_cb_called++; +} + + +static void poll_cb(uv_poll_t* h, int status, int events) { + ASSERT(0 && "should never get here"); +} + + +static void NO_INLINE close_socket_and_verify_stack() { + const uint32_t MARKER = 0xDEADBEEF; + const int VERIFY_AFTER = 10; /* ms */ + int r; + + volatile uint32_t data[65536]; + size_t i; + + for (i = 0; i < ARRAY_SIZE(data); i++) + data[i] = MARKER; + + r = closesocket(sock); + ASSERT(r == 0); + + uv_sleep(VERIFY_AFTER); + + for (i = 0; i < ARRAY_SIZE(data); i++) + ASSERT(data[i] == MARKER); +} + + +TEST_IMPL(poll_close_doesnt_corrupt_stack) { + struct WSAData wsa_data; + int r; + unsigned long on; + struct sockaddr_in addr; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + + sock = socket(AF_INET, SOCK_STREAM, 0); + ASSERT(sock != INVALID_SOCKET); + on = 1; + r = ioctlsocket(sock, FIONBIO, &on); + ASSERT(r == 0); + + r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); + ASSERT(r == 0); + + r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); + ASSERT(r != 0); + ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + + r = uv_poll_init_socket(uv_default_loop(), &handle, sock); + ASSERT(r == 0); + r = uv_poll_start(&handle, UV_READABLE | UV_WRITABLE, poll_cb); + ASSERT(r == 0); + + uv_close((uv_handle_t*) &handle, close_cb); + + close_socket_and_verify_stack(); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* _WIN32 */ diff --git a/3rdparty/libuv/test/test-poll-close.c b/3rdparty/libuv/test/test-poll-close.c new file mode 100644 index 00000000000..2eccddf5b0b --- /dev/null +++ b/3rdparty/libuv/test/test-poll-close.c @@ -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 <errno.h> + +#ifndef _WIN32 +# include <fcntl.h> +# include <sys/socket.h> +# include <unistd.h> +#endif + +#include "uv.h" +#include "task.h" + +#define NUM_SOCKETS 64 + + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +TEST_IMPL(poll_close) { + uv_os_sock_t sockets[NUM_SOCKETS]; + uv_poll_t poll_handles[NUM_SOCKETS]; + int i; + +#ifdef _WIN32 + { + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + } +#endif + + for (i = 0; i < NUM_SOCKETS; i++) { + sockets[i] = socket(AF_INET, SOCK_STREAM, 0); + uv_poll_init_socket(uv_default_loop(), &poll_handles[i], sockets[i]); + uv_poll_start(&poll_handles[i], UV_READABLE | UV_WRITABLE, NULL); + } + + for (i = 0; i < NUM_SOCKETS; i++) { + uv_close((uv_handle_t*) &poll_handles[i], close_cb); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == NUM_SOCKETS); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-poll-closesocket.c b/3rdparty/libuv/test/test-poll-closesocket.c new file mode 100644 index 00000000000..4db74a01f63 --- /dev/null +++ b/3rdparty/libuv/test/test-poll-closesocket.c @@ -0,0 +1,89 @@ +/* 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. + */ + +#ifdef _WIN32 + +#include <errno.h> + +#include "uv.h" +#include "task.h" + +uv_os_sock_t sock; +uv_poll_t handle; + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* h) { + close_cb_called++; +} + + +static void poll_cb(uv_poll_t* h, int status, int events) { + int r; + + ASSERT(status == 0); + ASSERT(h == &handle); + + r = uv_poll_start(&handle, UV_READABLE, poll_cb); + ASSERT(r == 0); + + closesocket(sock); + uv_close((uv_handle_t*) &handle, close_cb); + +} + + +TEST_IMPL(poll_closesocket) { + struct WSAData wsa_data; + int r; + unsigned long on; + struct sockaddr_in addr; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + + sock = socket(AF_INET, SOCK_STREAM, 0); + ASSERT(sock != INVALID_SOCKET); + on = 1; + r = ioctlsocket(sock, FIONBIO, &on); + ASSERT(r == 0); + + r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); + ASSERT(r == 0); + + r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); + ASSERT(r != 0); + ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + + r = uv_poll_init_socket(uv_default_loop(), &handle, sock); + ASSERT(r == 0); + r = uv_poll_start(&handle, UV_WRITABLE, poll_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif diff --git a/3rdparty/libuv/test/test-poll.c b/3rdparty/libuv/test/test-poll.c new file mode 100644 index 00000000000..be8b00c32ca --- /dev/null +++ b/3rdparty/libuv/test/test-poll.c @@ -0,0 +1,560 @@ +/* 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 <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); + + +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(void) { +#ifdef _WIN32 + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EAGAIN + || errno == EINPROGRESS +#ifdef EWOULDBLOCK + || errno == EWOULDBLOCK; +#endif + ; +#endif +} + + +static uv_os_sock_t create_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 + +#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; + unsigned 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(1 == uv_is_active((uv_handle_t*) handle)); + } else { + ASSERT(0 == uv_is_active((uv_handle_t*) handle)); + } +} + + +static void delay_timer_cb(uv_timer_t* timer) { + connection_context_t* context = (connection_context_t*) timer->data; + int r; + + /* Timer should auto stop. */ + ASSERT(0 == 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 + + 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(void) { + server_context_t* context; + struct sockaddr_in addr; + uv_os_sock_t sock; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + sock = create_bound_socket(addr); + 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(void) { + uv_os_sock_t sock; + connection_context_t* context; + struct sockaddr_in server_addr; + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &addr)); + + sock = create_bound_socket(addr); + 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(void) { + int i, r; + +#ifdef _WIN32 + { + struct WSAData wsa_data; + int 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(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + /* Assert that at most five percent of the writable wakeups was spurious. */ + ASSERT(spurious_writable_wakeups == 0 || + (valid_writable_wakeups + spurious_writable_wakeups) / + spurious_writable_wakeups > 20); + + ASSERT(closed_connections == NUM_CLIENTS * 2); + + MAKE_VALGRIND_HAPPY(); +} + + +TEST_IMPL(poll_duplex) { + test_mode = DUPLEX; + start_poll_test(); + return 0; +} + + +TEST_IMPL(poll_unidirectional) { + test_mode = UNIDIRECTIONAL; + start_poll_test(); + return 0; +} diff --git a/3rdparty/libuv/test/test-process-title.c b/3rdparty/libuv/test/test-process-title.c new file mode 100644 index 00000000000..42ade441604 --- /dev/null +++ b/3rdparty/libuv/test/test-process-title.c @@ -0,0 +1,53 @@ +/* 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 <string.h> + + +static void set_title(const char* title) { + char buffer[512]; + int err; + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(err == 0); + + err = uv_set_process_title(title); + ASSERT(err == 0); + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(err == 0); + + ASSERT(strcmp(buffer, title) == 0); +} + + +TEST_IMPL(process_title) { +#if defined(__sun) || defined(_AIX) + RETURN_SKIP("uv_(get|set)_process_title is not implemented."); +#else + /* Check for format string vulnerabilities. */ + set_title("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"); + set_title("new title"); + return 0; +#endif +} diff --git a/3rdparty/libuv/test/test-queue-foreach-delete.c b/3rdparty/libuv/test/test-queue-foreach-delete.c new file mode 100644 index 00000000000..45da225381f --- /dev/null +++ b/3rdparty/libuv/test/test-queue-foreach-delete.c @@ -0,0 +1,200 @@ +/* Copyright The libuv project and 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 <string.h> + + +/* + * The idea behind the test is as follows. + * Certain handle types are stored in a queue internally. + * Extra care should be taken for removal of a handle from the queue while iterating over the queue. + * (i.e., QUEUE_REMOVE() called within QUEUE_FOREACH()) + * This usually happens when someone closes or stops a handle from within its callback. + * So we need to check that we haven't screwed the queue on close/stop. + * To do so we do the following (for each handle type): + * 1. Create and start 3 handles (#0, #1, and #2). + * + * The queue after the start() calls: + * ..=> [queue head] <=> [handle] <=> [handle #1] <=> [handle] <=.. + * + * 2. Trigger handles to fire (for uv_idle_t, uv_prepare_t, and uv_check_t there is nothing to do). + * + * 3. In the callback for the first-executed handle (#0 or #2 depending on handle type) + * stop the handle and the next one (#1). + * (for uv_idle_t, uv_prepare_t, and uv_check_t callbacks are executed in the reverse order as they are start()'ed, + * so callback for handle #2 will be called first) + * + * The queue after the stop() calls: + * correct foreach "next" | + * \/ + * ..=> [queue head] <==============================> [handle] <=.. + * [ ] <- [handle] <=> [handle #1] -> [ ] + * /\ + * wrong foreach "next" | + * + * 4. The callback for handle #1 shouldn't be called because the handle #1 is stopped in the previous step. + * However, if QUEUE_REMOVE() is not handled properly within QUEUE_FOREACH(), the callback _will_ be called. + */ + +static const unsigned first_handle_number_idle = 2; +static const unsigned first_handle_number_prepare = 2; +static const unsigned first_handle_number_check = 2; +#ifdef __linux__ +static const unsigned first_handle_number_fs_event = 0; +#endif + + +#define DEFINE_GLOBALS_AND_CBS(name) \ + static uv_##name##_t (name)[3]; \ + static unsigned name##_cb_calls[3]; \ + \ + static void name##2_cb(uv_##name##_t* handle) { \ + ASSERT(handle == &(name)[2]); \ + if (first_handle_number_##name == 2) { \ + uv_close((uv_handle_t*)&(name)[2], NULL); \ + uv_close((uv_handle_t*)&(name)[1], NULL); \ + } \ + name##_cb_calls[2]++; \ + } \ + \ + static void name##1_cb(uv_##name##_t* handle) { \ + ASSERT(handle == &(name)[1]); \ + ASSERT(0 && "Shouldn't be called" && (&name[0])); \ + } \ + \ + static void name##0_cb(uv_##name##_t* handle) { \ + ASSERT(handle == &(name)[0]); \ + if (first_handle_number_##name == 0) { \ + uv_close((uv_handle_t*)&(name)[0], NULL); \ + uv_close((uv_handle_t*)&(name)[1], NULL); \ + } \ + name##_cb_calls[0]++; \ + } \ + \ + static const uv_##name##_cb name##_cbs[] = { \ + (uv_##name##_cb)name##0_cb, \ + (uv_##name##_cb)name##1_cb, \ + (uv_##name##_cb)name##2_cb, \ + }; + +#define INIT_AND_START(name, loop) \ + do { \ + size_t i; \ + for (i = 0; i < ARRAY_SIZE(name); i++) { \ + int r; \ + r = uv_##name##_init((loop), &(name)[i]); \ + ASSERT(r == 0); \ + \ + r = uv_##name##_start(&(name)[i], name##_cbs[i]); \ + ASSERT(r == 0); \ + } \ + } while (0) + +#define END_ASSERTS(name) \ + do { \ + ASSERT(name##_cb_calls[0] == 1); \ + ASSERT(name##_cb_calls[1] == 0); \ + ASSERT(name##_cb_calls[2] == 1); \ + } while (0) + +DEFINE_GLOBALS_AND_CBS(idle) +DEFINE_GLOBALS_AND_CBS(prepare) +DEFINE_GLOBALS_AND_CBS(check) + +#ifdef __linux__ +DEFINE_GLOBALS_AND_CBS(fs_event) + +static const char watched_dir[] = "."; +static uv_timer_t timer; +static unsigned helper_timer_cb_calls; + + +static void init_and_start_fs_events(uv_loop_t* loop) { + size_t i; + for (i = 0; i < ARRAY_SIZE(fs_event); i++) { + int r; + r = uv_fs_event_init(loop, &fs_event[i]); + ASSERT(r == 0); + + r = uv_fs_event_start(&fs_event[i], + (uv_fs_event_cb)fs_event_cbs[i], + watched_dir, + 0); + ASSERT(r == 0); + } +} + +static void helper_timer_cb(uv_timer_t* thandle) { + int r; + uv_fs_t fs_req; + + /* fire all fs_events */ + r = uv_fs_utime(thandle->loop, &fs_req, watched_dir, 0, 0, NULL); + ASSERT(r == 0); + ASSERT(fs_req.result == 0); + ASSERT(fs_req.fs_type == UV_FS_UTIME); + ASSERT(strcmp(fs_req.path, watched_dir) == 0); + uv_fs_req_cleanup(&fs_req); + + helper_timer_cb_calls++; +} +#endif + + +TEST_IMPL(queue_foreach_delete) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + INIT_AND_START(idle, loop); + INIT_AND_START(prepare, loop); + INIT_AND_START(check, loop); + +#ifdef __linux__ + init_and_start_fs_events(loop); + + /* helper timer to trigger async and fs_event callbacks */ + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, helper_timer_cb, 0, 0); + ASSERT(r == 0); +#endif + + r = uv_run(loop, UV_RUN_NOWAIT); + ASSERT(r == 1); + + END_ASSERTS(idle); + END_ASSERTS(prepare); + END_ASSERTS(check); + +#ifdef __linux__ + ASSERT(helper_timer_cb_calls == 1); +#endif + + MAKE_VALGRIND_HAPPY(); + + return 0; +} diff --git a/3rdparty/libuv/test/test-ref.c b/3rdparty/libuv/test/test-ref.c new file mode 100644 index 00000000000..ddaa1738083 --- /dev/null +++ b/3rdparty/libuv/test/test-ref.c @@ -0,0 +1,442 @@ +/* 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 <stdlib.h> +#include <string.h> + + +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; +static uv_connect_t connect_req; + +static char buffer[32767]; + +static int req_cb_called; +static int connect_cb_called; +static int write_cb_called; +static int shutdown_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void do_close(void* handle) { + close_cb_called = 0; + uv_close((uv_handle_t*)handle, close_cb); + ASSERT(close_cb_called == 0); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(close_cb_called == 1); +} + + +static void fail_cb(void) { + FATAL("fail_cb should not have been called"); +} + + +static void fail_cb2(void) { + ASSERT(0 && "fail_cb2 should not have been called"); +} + + +static void req_cb(uv_handle_t* req, int status) { + req_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &shutdown_req); + shutdown_cb_called++; +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req == &write_req); + uv_shutdown(&shutdown_req, req->handle, shutdown_cb); + write_cb_called++; +} + + +static void connect_and_write(uv_connect_t* req, int status) { + uv_buf_t buf = uv_buf_init(buffer, sizeof buffer); + ASSERT(req == &connect_req); + ASSERT(status == 0); + uv_write(&write_req, req->handle, &buf, 1, write_cb); + connect_cb_called++; +} + + + +static void connect_and_shutdown(uv_connect_t* req, int status) { + ASSERT(req == &connect_req); + ASSERT(status == 0); + uv_shutdown(&shutdown_req, req->handle, shutdown_cb); + connect_cb_called++; +} + + +TEST_IMPL(ref) { + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(idle_ref) { + uv_idle_t h; + uv_idle_init(uv_default_loop(), &h); + uv_idle_start(&h, (uv_idle_cb) fail_cb2); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(async_ref) { + uv_async_t h; + uv_async_init(uv_default_loop(), &h, NULL); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(prepare_ref) { + uv_prepare_t h; + uv_prepare_init(uv_default_loop(), &h); + uv_prepare_start(&h, (uv_prepare_cb) fail_cb2); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(check_ref) { + uv_check_t h; + uv_check_init(uv_default_loop(), &h); + uv_check_start(&h, (uv_check_cb) fail_cb2); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void prepare_cb(uv_prepare_t* h) { + ASSERT(h != NULL); + uv_unref((uv_handle_t*)h); +} + + +TEST_IMPL(unref_in_prepare_cb) { + uv_prepare_t h; + uv_prepare_init(uv_default_loop(), &h); + uv_prepare_start(&h, prepare_cb); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(timer_ref) { + uv_timer_t h; + uv_timer_init(uv_default_loop(), &h); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(timer_ref2) { + uv_timer_t h; + uv_timer_init(uv_default_loop(), &h); + uv_timer_start(&h, (uv_timer_cb)fail_cb, 42, 42); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_event_ref) { + uv_fs_event_t h; + uv_fs_event_init(uv_default_loop(), &h); + uv_fs_event_start(&h, (uv_fs_event_cb)fail_cb, ".", 0); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(fs_poll_ref) { + uv_fs_poll_t h; + uv_fs_poll_init(uv_default_loop(), &h); + uv_fs_poll_start(&h, NULL, ".", 999); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_ref) { + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_ref2) { + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_ref2b) { + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); + uv_unref((uv_handle_t*)&h); + uv_close((uv_handle_t*)&h, close_cb); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(close_cb_called == 1); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_ref3) { + struct sockaddr_in addr; + uv_tcp_t h; + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + uv_tcp_init(uv_default_loop(), &h); + uv_tcp_connect(&connect_req, + &h, + (const struct sockaddr*) &addr, + connect_and_shutdown); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(connect_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_ref4) { + struct sockaddr_in addr; + uv_tcp_t h; + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + uv_tcp_init(uv_default_loop(), &h); + uv_tcp_connect(&connect_req, + &h, + (const struct sockaddr*) &addr, + connect_and_write); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_ref) { + uv_udp_t h; + uv_udp_init(uv_default_loop(), &h); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_ref2) { + struct sockaddr_in addr; + uv_udp_t h; + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + uv_udp_init(uv_default_loop(), &h); + uv_udp_bind(&h, (const struct sockaddr*) &addr, 0); + uv_udp_recv_start(&h, (uv_alloc_cb)fail_cb, (uv_udp_recv_cb)fail_cb); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_ref3) { + struct sockaddr_in addr; + uv_buf_t buf = uv_buf_init("PING", 4); + uv_udp_send_t req; + uv_udp_t h; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + uv_udp_init(uv_default_loop(), &h); + uv_udp_send(&req, + &h, + &buf, + 1, + (const struct sockaddr*) &addr, + (uv_udp_send_cb) req_cb); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(req_cb_called == 1); + do_close(&h); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_ref) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_ref2) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_ref3) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_and_shutdown); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(connect_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_ref4) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_and_write); + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + do_close(&h); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(process_ref) { + /* spawn_helper4 blocks indefinitely. */ + char *argv[] = { NULL, "spawn_helper4", NULL }; + uv_process_options_t options; + size_t exepath_size; + char exepath[256]; + uv_process_t h; + int r; + + memset(&options, 0, sizeof(options)); + exepath_size = sizeof(exepath); + + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + + argv[0] = exepath; + options.file = exepath; + options.args = argv; + options.exit_cb = NULL; + + r = uv_spawn(uv_default_loop(), &h, &options); + ASSERT(r == 0); + + uv_unref((uv_handle_t*)&h); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + r = uv_process_kill(&h, /* SIGTERM */ 15); + ASSERT(r == 0); + + do_close(&h); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(has_ref) { + uv_idle_t h; + uv_idle_init(uv_default_loop(), &h); + uv_ref((uv_handle_t*)&h); + ASSERT(uv_has_ref((uv_handle_t*)&h) == 1); + uv_unref((uv_handle_t*)&h); + ASSERT(uv_has_ref((uv_handle_t*)&h) == 0); + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-run-nowait.c b/3rdparty/libuv/test/test-run-nowait.c new file mode 100644 index 00000000000..43524f636d8 --- /dev/null +++ b/3rdparty/libuv/test/test-run-nowait.c @@ -0,0 +1,45 @@ +/* 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 int timer_called = 0; + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer_handle); + timer_called = 1; +} + + +TEST_IMPL(run_nowait) { + int r; + uv_timer_init(uv_default_loop(), &timer_handle); + uv_timer_start(&timer_handle, timer_cb, 100, 100); + + r = uv_run(uv_default_loop(), UV_RUN_NOWAIT); + ASSERT(r != 0); + ASSERT(timer_called == 0); + + return 0; +} diff --git a/3rdparty/libuv/test/test-run-once.c b/3rdparty/libuv/test/test-run-once.c new file mode 100644 index 00000000000..10cbf95e4ad --- /dev/null +++ b/3rdparty/libuv/test/test-run-once.c @@ -0,0 +1,48 @@ +/* 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 NUM_TICKS 64 + +static uv_idle_t idle_handle; +static int idle_counter; + + +static void idle_cb(uv_idle_t* handle) { + ASSERT(handle == &idle_handle); + + if (++idle_counter == NUM_TICKS) + uv_idle_stop(handle); +} + + +TEST_IMPL(run_once) { + uv_idle_init(uv_default_loop(), &idle_handle); + uv_idle_start(&idle_handle, idle_cb); + + while (uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT(idle_counter == NUM_TICKS); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-semaphore.c b/3rdparty/libuv/test/test-semaphore.c new file mode 100644 index 00000000000..ac03bb08f17 --- /dev/null +++ b/3rdparty/libuv/test/test-semaphore.c @@ -0,0 +1,111 @@ +/* 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 <stdlib.h> +#include <string.h> + +typedef struct { + uv_mutex_t mutex; + uv_sem_t sem; + int delay; + volatile int posted; +} worker_config; + + +static void worker(void* arg) { + worker_config* c = arg; + + if (c->delay) + uv_sleep(c->delay); + + uv_mutex_lock(&c->mutex); + ASSERT(c->posted == 0); + uv_sem_post(&c->sem); + c->posted = 1; + uv_mutex_unlock(&c->mutex); +} + + +TEST_IMPL(semaphore_1) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + + ASSERT(0 == uv_sem_init(&wc.sem, 0)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_sleep(100); + uv_mutex_lock(&wc.mutex); + ASSERT(wc.posted == 1); + uv_sem_wait(&wc.sem); /* should not block */ + uv_mutex_unlock(&wc.mutex); /* ergo, it should be ok to unlock after wait */ + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_sem_destroy(&wc.sem); + + return 0; +} + + +TEST_IMPL(semaphore_2) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_sem_init(&wc.sem, 0)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_sem_wait(&wc.sem); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_sem_destroy(&wc.sem); + + return 0; +} + + +TEST_IMPL(semaphore_3) { + uv_sem_t sem; + + ASSERT(0 == uv_sem_init(&sem, 3)); + uv_sem_wait(&sem); /* should not block */ + uv_sem_wait(&sem); /* should not block */ + ASSERT(0 == uv_sem_trywait(&sem)); + ASSERT(UV_EAGAIN == uv_sem_trywait(&sem)); + + uv_sem_post(&sem); + ASSERT(0 == uv_sem_trywait(&sem)); + ASSERT(UV_EAGAIN == uv_sem_trywait(&sem)); + + uv_sem_destroy(&sem); + + return 0; +} diff --git a/3rdparty/libuv/test/test-shutdown-close.c b/3rdparty/libuv/test/test-shutdown-close.c new file mode 100644 index 00000000000..78c369be2d9 --- /dev/null +++ b/3rdparty/libuv/test/test-shutdown-close.c @@ -0,0 +1,108 @@ +/* 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. + */ + +/* + * These tests verify that the uv_shutdown callback is always made, even when + * it is immediately followed by an uv_close call. + */ + +#include "uv.h" +#include "task.h" + + +static uv_shutdown_t shutdown_req; +static uv_connect_t connect_req; + +static int connect_cb_called = 0; +static int shutdown_cb_called = 0; +static int close_cb_called = 0; + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &shutdown_req); + ASSERT(status == 0 || status == UV_ECANCELED); + shutdown_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); + ASSERT(r == 0); + ASSERT(0 == uv_is_closing((uv_handle_t*) req->handle)); + uv_close((uv_handle_t*) req->handle, close_cb); + ASSERT(1 == uv_is_closing((uv_handle_t*) req->handle)); + + connect_cb_called++; +} + + +TEST_IMPL(shutdown_close_tcp) { + struct sockaddr_in addr; + uv_tcp_t h; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + r = uv_tcp_init(uv_default_loop(), &h); + ASSERT(r == 0); + r = uv_tcp_connect(&connect_req, + &h, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(connect_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(shutdown_close_pipe) { + uv_pipe_t h; + int r; + + r = uv_pipe_init(uv_default_loop(), &h, 0); + ASSERT(r == 0); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_cb); + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(connect_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-shutdown-eof.c b/3rdparty/libuv/test/test-shutdown-eof.c new file mode 100644 index 00000000000..9f95e7561f2 --- /dev/null +++ b/3rdparty/libuv/test/test-shutdown-eof.c @@ -0,0 +1,182 @@ +/* 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 uv_timer_t timer; +static uv_tcp_t tcp; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; +static uv_buf_t qbuf; +static int got_q; +static int got_eof; +static int called_connect_cb; +static int called_shutdown_cb; +static int called_tcp_close_cb; +static int called_timer_close_cb; +static int called_timer_cb; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; +} + + +static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { + ASSERT((uv_tcp_t*)t == &tcp); + + if (nread == 0) { + free(buf->base); + return; + } + + if (!got_q) { + ASSERT(nread == 1); + ASSERT(!got_eof); + ASSERT(buf->base[0] == 'Q'); + free(buf->base); + got_q = 1; + puts("got Q"); + } else { + ASSERT(nread == UV_EOF); + if (buf->base) { + free(buf->base); + } + got_eof = 1; + puts("got EOF"); + } +} + + +static void shutdown_cb(uv_shutdown_t *req, int status) { + ASSERT(req == &shutdown_req); + + ASSERT(called_connect_cb == 1); + ASSERT(!got_eof); + ASSERT(called_tcp_close_cb == 0); + ASSERT(called_timer_close_cb == 0); + ASSERT(called_timer_cb == 0); + + called_shutdown_cb++; +} + + +static void connect_cb(uv_connect_t *req, int status) { + ASSERT(status == 0); + ASSERT(req == &connect_req); + + /* Start reading from our connection so we can receive the EOF. */ + uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb); + + /* + * Write the letter 'Q' to gracefully kill the echo-server. This will not + * effect our connection. + */ + uv_write(&write_req, (uv_stream_t*) &tcp, &qbuf, 1, NULL); + + /* Shutdown our end of the connection. */ + uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb); + + called_connect_cb++; + ASSERT(called_shutdown_cb == 0); +} + + +static void tcp_close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*) &tcp); + + ASSERT(called_connect_cb == 1); + ASSERT(got_q); + ASSERT(got_eof); + ASSERT(called_timer_cb == 1); + + called_tcp_close_cb++; +} + + +static void timer_close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*) &timer); + called_timer_close_cb++; +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer); + uv_close((uv_handle_t*) handle, timer_close_cb); + + /* + * The most important assert of the test: we have not received + * tcp_close_cb yet. + */ + ASSERT(called_tcp_close_cb == 0); + uv_close((uv_handle_t*) &tcp, tcp_close_cb); + + called_timer_cb++; +} + + +/* + * This test has a client which connects to the echo_server and immediately + * issues a shutdown. The echo-server, in response, will also shutdown their + * connection. We check, with a timer, that libuv is not automatically + * calling uv_close when the client receives the EOF from echo-server. + */ +TEST_IMPL(shutdown_eof) { + struct sockaddr_in server_addr; + int r; + + qbuf.base = "Q"; + qbuf.len = 1; + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + uv_timer_start(&timer, timer_cb, 100, 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + r = uv_tcp_init(uv_default_loop(), &tcp); + ASSERT(!r); + + r = uv_tcp_connect(&connect_req, + &tcp, + (const struct sockaddr*) &server_addr, + connect_cb); + ASSERT(!r); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(called_connect_cb == 1); + ASSERT(called_shutdown_cb == 1); + ASSERT(got_eof); + ASSERT(got_q); + ASSERT(called_tcp_close_cb == 1); + ASSERT(called_timer_close_cb == 1); + ASSERT(called_timer_cb == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + diff --git a/3rdparty/libuv/test/test-shutdown-twice.c b/3rdparty/libuv/test/test-shutdown-twice.c new file mode 100644 index 00000000000..75c05435499 --- /dev/null +++ b/3rdparty/libuv/test/test-shutdown-twice.c @@ -0,0 +1,84 @@ +/* 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. + */ + +/* + * This is a regression test for issue #1113 (calling uv_shutdown twice will + * leave a ghost request in the system) + */ + +#include "uv.h" +#include "task.h" + +static uv_shutdown_t req1; +static uv_shutdown_t req2; + +static int shutdown_cb_called = 0; + +static void close_cb(uv_handle_t* handle) { + +} + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &req1); + ASSERT(status == 0); + shutdown_cb_called++; + uv_close((uv_handle_t*) req->handle, close_cb); +} + +static void connect_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(status == 0); + + r = uv_shutdown(&req1, req->handle, shutdown_cb); + ASSERT(r == 0); + r = uv_shutdown(&req2, req->handle, shutdown_cb); + ASSERT(r != 0); + +} + +TEST_IMPL(shutdown_twice) { + struct sockaddr_in addr; + uv_loop_t* loop; + int r; + uv_tcp_t h; + + uv_connect_t connect_req; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + loop = uv_default_loop(); + + r = uv_tcp_init(loop, &h); + + r = uv_tcp_connect(&connect_req, + &h, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(shutdown_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-signal-multiple-loops.c b/3rdparty/libuv/test/test-signal-multiple-loops.c new file mode 100644 index 00000000000..158129919bd --- /dev/null +++ b/3rdparty/libuv/test/test-signal-multiple-loops.c @@ -0,0 +1,290 @@ +/* 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. + */ + + +/* This test does not pretend to be cross-platform. */ +#ifndef _WIN32 + +#include "uv.h" +#include "task.h" + +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* The value of NUM_SIGNAL_HANDLING_THREADS is not arbitrary; it needs to be a + * multiple of three for reasons that will become clear when you scroll down. + * We're basically creating three different thread groups. The total needs + * to be divisible by three in order for the numbers in the final check to + * match up. + */ +#define NUM_SIGNAL_HANDLING_THREADS 24 +#define NUM_LOOP_CREATING_THREADS 10 + +enum signal_action { + ONLY_SIGUSR1, + ONLY_SIGUSR2, + SIGUSR1_AND_SIGUSR2 +}; + +static uv_sem_t sem; +static uv_mutex_t counter_lock; +static volatile int stop = 0; + +static volatile int signal1_cb_counter = 0; +static volatile int signal2_cb_counter = 0; +static volatile int loop_creation_counter = 0; + + +static void increment_counter(volatile int* counter) { + uv_mutex_lock(&counter_lock); + ++(*counter); + uv_mutex_unlock(&counter_lock); +} + + +static void signal1_cb(uv_signal_t* handle, int signum) { + ASSERT(signum == SIGUSR1); + increment_counter(&signal1_cb_counter); + uv_signal_stop(handle); +} + + +static void signal2_cb(uv_signal_t* handle, int signum) { + ASSERT(signum == SIGUSR2); + increment_counter(&signal2_cb_counter); + uv_signal_stop(handle); +} + + +static void signal_handling_worker(void* context) { + enum signal_action action; + uv_signal_t signal1a; + uv_signal_t signal1b; + uv_signal_t signal2; + uv_loop_t loop; + int r; + + action = (enum signal_action) (uintptr_t) context; + + ASSERT(0 == uv_loop_init(&loop)); + + /* Setup the signal watchers and start them. */ + if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { + r = uv_signal_init(&loop, &signal1a); + ASSERT(r == 0); + r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1); + ASSERT(r == 0); + r = uv_signal_init(&loop, &signal1b); + ASSERT(r == 0); + r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1); + ASSERT(r == 0); + } + + if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { + r = uv_signal_init(&loop, &signal2); + ASSERT(r == 0); + r = uv_signal_start(&signal2, signal2_cb, SIGUSR2); + ASSERT(r == 0); + } + + /* Signal watchers are now set up. */ + uv_sem_post(&sem); + + /* Wait for all signals. The signal callbacks stop the watcher, so uv_run + * will return when all signal watchers caught a signal. + */ + r = uv_run(&loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + /* Restart the signal watchers. */ + if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { + r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1); + ASSERT(r == 0); + r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1); + ASSERT(r == 0); + } + + if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { + r = uv_signal_start(&signal2, signal2_cb, SIGUSR2); + ASSERT(r == 0); + } + + /* Wait for signals once more. */ + uv_sem_post(&sem); + + r = uv_run(&loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + /* Close the watchers. */ + if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { + uv_close((uv_handle_t*) &signal1a, NULL); + uv_close((uv_handle_t*) &signal1b, NULL); + } + + if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { + uv_close((uv_handle_t*) &signal2, NULL); + } + + /* Wait for the signal watchers to close. */ + r = uv_run(&loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + uv_loop_close(&loop); +} + + +static void signal_unexpected_cb(uv_signal_t* handle, int signum) { + ASSERT(0 && "signal_unexpected_cb should never be called"); +} + + +static void loop_creating_worker(void* context) { + (void) context; + + do { + uv_loop_t *loop; + uv_signal_t signal; + int r; + + loop = malloc(sizeof(*loop)); + ASSERT(loop != NULL); + ASSERT(0 == uv_loop_init(loop)); + + r = uv_signal_init(loop, &signal); + ASSERT(r == 0); + + r = uv_signal_start(&signal, signal_unexpected_cb, SIGTERM); + ASSERT(r == 0); + + uv_close((uv_handle_t*) &signal, NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + uv_loop_close(loop); + free(loop); + + increment_counter(&loop_creation_counter); + } while (!stop); +} + + +TEST_IMPL(signal_multiple_loops) { + uv_thread_t loop_creating_threads[NUM_LOOP_CREATING_THREADS]; + uv_thread_t signal_handling_threads[NUM_SIGNAL_HANDLING_THREADS]; + enum signal_action action; + sigset_t sigset; + int i; + int r; + + r = uv_sem_init(&sem, 0); + ASSERT(r == 0); + + r = uv_mutex_init(&counter_lock); + ASSERT(r == 0); + + /* Create a couple of threads that create a destroy loops continuously. */ + for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) { + r = uv_thread_create(&loop_creating_threads[i], + loop_creating_worker, + NULL); + ASSERT(r == 0); + } + + /* Create a couple of threads that actually handle signals. */ + for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) { + switch (i % 3) { + case 0: action = ONLY_SIGUSR1; break; + case 1: action = ONLY_SIGUSR2; break; + case 2: action = SIGUSR1_AND_SIGUSR2; break; + } + + r = uv_thread_create(&signal_handling_threads[i], + signal_handling_worker, + (void*) (uintptr_t) action); + ASSERT(r == 0); + } + + /* Wait until all threads have started and set up their signal watchers. */ + for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) + uv_sem_wait(&sem); + + r = kill(getpid(), SIGUSR1); + ASSERT(r == 0); + r = kill(getpid(), SIGUSR2); + ASSERT(r == 0); + + /* Wait for all threads to handle these signals. */ + for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) + uv_sem_wait(&sem); + + /* Block all signals to this thread, so we are sure that from here the signal + * handler runs in another thread. This is is more likely to catch thread and + * signal safety issues if there are any. + */ + sigfillset(&sigset); + pthread_sigmask(SIG_SETMASK, &sigset, NULL); + + r = kill(getpid(), SIGUSR1); + ASSERT(r == 0); + r = kill(getpid(), SIGUSR2); + ASSERT(r == 0); + + /* Wait for all signal handling threads to exit. */ + for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) { + r = uv_thread_join(&signal_handling_threads[i]); + ASSERT(r == 0); + } + + /* Tell all loop creating threads to stop. */ + stop = 1; + + /* Wait for all loop creating threads to exit. */ + for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) { + r = uv_thread_join(&loop_creating_threads[i]); + ASSERT(r == 0); + } + + printf("signal1_cb calls: %d\n", signal1_cb_counter); + printf("signal2_cb calls: %d\n", signal2_cb_counter); + printf("loops created and destroyed: %d\n", loop_creation_counter); + + /* The division by three reflects the fact that we spawn three different + * thread groups of (NUM_SIGNAL_HANDLING_THREADS / 3) threads each. + */ + ASSERT(signal1_cb_counter == 8 * (NUM_SIGNAL_HANDLING_THREADS / 3)); + ASSERT(signal2_cb_counter == 4 * (NUM_SIGNAL_HANDLING_THREADS / 3)); + + /* We don't know exactly how much loops will be created and destroyed, but at + * least there should be 1 for every loop creating thread. + */ + ASSERT(loop_creation_counter >= NUM_LOOP_CREATING_THREADS); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* !_WIN32 */ diff --git a/3rdparty/libuv/test/test-signal.c b/3rdparty/libuv/test/test-signal.c new file mode 100644 index 00000000000..fcdd8e4d2dd --- /dev/null +++ b/3rdparty/libuv/test/test-signal.c @@ -0,0 +1,152 @@ +/* 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. + */ + + +/* This test does not pretend to be cross-platform. */ +#ifndef _WIN32 + +#include "uv.h" +#include "task.h" + +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define NSIGNALS 10 + +struct timer_ctx { + unsigned int ncalls; + uv_timer_t handle; + int signum; +}; + +struct signal_ctx { + enum { CLOSE, STOP } stop_or_close; + unsigned int ncalls; + uv_signal_t handle; + int signum; +}; + + +static void signal_cb(uv_signal_t* handle, int signum) { + struct signal_ctx* ctx = container_of(handle, struct signal_ctx, handle); + ASSERT(signum == ctx->signum); + + if (++ctx->ncalls == NSIGNALS) { + if (ctx->stop_or_close == STOP) + uv_signal_stop(handle); + else if (ctx->stop_or_close == CLOSE) + uv_close((uv_handle_t*)handle, NULL); + else + ASSERT(0); + } +} + + +static void timer_cb(uv_timer_t* handle) { + struct timer_ctx* ctx = container_of(handle, struct timer_ctx, handle); + + raise(ctx->signum); + + if (++ctx->ncalls == NSIGNALS) + uv_close((uv_handle_t*)handle, NULL); +} + + +static void start_watcher(uv_loop_t* loop, int signum, struct signal_ctx* ctx) { + ctx->ncalls = 0; + ctx->signum = signum; + ctx->stop_or_close = CLOSE; + ASSERT(0 == uv_signal_init(loop, &ctx->handle)); + ASSERT(0 == uv_signal_start(&ctx->handle, signal_cb, signum)); +} + + +static void start_timer(uv_loop_t* loop, int signum, struct timer_ctx* ctx) { + ctx->ncalls = 0; + ctx->signum = signum; + ASSERT(0 == uv_timer_init(loop, &ctx->handle)); + ASSERT(0 == uv_timer_start(&ctx->handle, timer_cb, 5, 5)); +} + + +TEST_IMPL(we_get_signal) { + struct signal_ctx sc; + struct timer_ctx tc; + uv_loop_t* loop; + + loop = uv_default_loop(); + start_timer(loop, SIGCHLD, &tc); + start_watcher(loop, SIGCHLD, &sc); + sc.stop_or_close = STOP; /* stop, don't close the signal handle */ + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(tc.ncalls == NSIGNALS); + ASSERT(sc.ncalls == NSIGNALS); + + start_timer(loop, SIGCHLD, &tc); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(tc.ncalls == NSIGNALS); + ASSERT(sc.ncalls == NSIGNALS); + + sc.ncalls = 0; + sc.stop_or_close = CLOSE; /* now close it when it's done */ + uv_signal_start(&sc.handle, signal_cb, SIGCHLD); + + start_timer(loop, SIGCHLD, &tc); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(tc.ncalls == NSIGNALS); + ASSERT(sc.ncalls == NSIGNALS); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(we_get_signals) { + struct signal_ctx sc[4]; + struct timer_ctx tc[2]; + uv_loop_t* loop; + unsigned int i; + + loop = uv_default_loop(); + start_watcher(loop, SIGUSR1, sc + 0); + start_watcher(loop, SIGUSR1, sc + 1); + start_watcher(loop, SIGUSR2, sc + 2); + start_watcher(loop, SIGUSR2, sc + 3); + start_timer(loop, SIGUSR1, tc + 0); + start_timer(loop, SIGUSR2, tc + 1); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + for (i = 0; i < ARRAY_SIZE(sc); i++) + ASSERT(sc[i].ncalls == NSIGNALS); + + for (i = 0; i < ARRAY_SIZE(tc); i++) + ASSERT(tc[i].ncalls == NSIGNALS); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* _WIN32 */ diff --git a/3rdparty/libuv/test/test-socket-buffer-size.c b/3rdparty/libuv/test/test-socket-buffer-size.c new file mode 100644 index 00000000000..72f8c2524c0 --- /dev/null +++ b/3rdparty/libuv/test/test-socket-buffer-size.c @@ -0,0 +1,77 @@ +/* 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 <string.h> + +static uv_udp_t udp; +static uv_tcp_t tcp; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void check_buffer_size(uv_handle_t* handle) { + int value; + + value = 0; + ASSERT(0 == uv_recv_buffer_size(handle, &value)); + ASSERT(value > 0); + + value = 10000; + ASSERT(0 == uv_recv_buffer_size(handle, &value)); + + value = 0; + ASSERT(0 == uv_recv_buffer_size(handle, &value)); + /* linux sets double the value */ + ASSERT(value == 10000 || value == 20000); +} + + +TEST_IMPL(socket_buffer_size) { + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &tcp)); + ASSERT(0 == uv_tcp_bind(&tcp, (struct sockaddr*) &addr, 0)); + check_buffer_size((uv_handle_t*) &tcp); + uv_close((uv_handle_t*) &tcp, close_cb); + + ASSERT(0 == uv_udp_init(uv_default_loop(), &udp)); + ASSERT(0 == uv_udp_bind(&udp, (struct sockaddr*) &addr, 0)); + check_buffer_size((uv_handle_t*) &udp); + uv_close((uv_handle_t*) &udp, close_cb); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-spawn.c b/3rdparty/libuv/test/test-spawn.c new file mode 100644 index 00000000000..eba54ae7054 --- /dev/null +++ b/3rdparty/libuv/test/test-spawn.c @@ -0,0 +1,1706 @@ +/* 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +# if defined(__MINGW32__) +# include <basetyps.h> +# endif +# include <shellapi.h> +# include <wchar.h> +#else +# include <unistd.h> +# include <sys/wait.h> +#endif + + +static int close_cb_called; +static int exit_cb_called; +static uv_process_t process; +static uv_timer_t timer; +static uv_process_options_t options; +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; +static int no_term_signal; +static int timer_counter; + +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + + +static void close_cb(uv_handle_t* handle) { + printf("close_cb\n"); + close_cb_called++; +} + +static void exit_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 1); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, close_cb); +} + + +static void fail_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + ASSERT(0 && "fail_cb called"); +} + + +static void kill_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + int err; + + printf("exit_cb\n"); + exit_cb_called++; +#ifdef _WIN32 + ASSERT(exit_status == 1); +#else + ASSERT(exit_status == 0); +#endif + ASSERT(no_term_signal || term_signal == 15); + uv_close((uv_handle_t*)process, close_cb); + + /* + * Sending signum == 0 should check if the + * child process is still alive, not kill it. + * This process should be dead. + */ + err = uv_kill(process->pid, 0); + ASSERT(err == UV_ESRCH); +} + +static void detach_failure_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + printf("detach_cb\n"); + exit_cb_called++; +} + +static void on_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = output + output_used; + buf->len = OUTPUT_SIZE - output_used; +} + + +static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + if (nread > 0) { + output_used += nread; + } else if (nread < 0) { + ASSERT(nread == UV_EOF); + uv_close((uv_handle_t*)tcp, close_cb); + } +} + + +static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + uv_read_stop(tcp); + on_read(tcp, nread, buf); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + uv_close((uv_handle_t*)req->handle, close_cb); +} + + +static void init_process_options(char* test, uv_exit_cb exit_cb) { + /* Note spawn_helper1 defined in test/run-tests.c */ + int r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = test; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; + options.flags = 0; +} + + +static void timer_cb(uv_timer_t* handle) { + uv_process_kill(&process, /* SIGTERM */ 15); + uv_close((uv_handle_t*)handle, close_cb); +} + + +static void timer_counter_cb(uv_timer_t* handle) { + ++timer_counter; +} + + +TEST_IMPL(spawn_fails) { + int r; + + init_process_options("", fail_cb); + options.file = options.args[0] = "program-that-had-better-not-exist"; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_ENOENT || r == UV_EACCES); + ASSERT(0 == uv_is_active((uv_handle_t*) &process)); + uv_close((uv_handle_t*) &process, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#ifndef _WIN32 +TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) { + int r; + int status; + int err; + + init_process_options("", fail_cb); + options.file = options.args[0] = "program-that-had-better-not-exist"; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_ENOENT || r == UV_EACCES); + ASSERT(0 == uv_is_active((uv_handle_t*) &process)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + /* verify the child is successfully cleaned up within libuv */ + do + err = waitpid(process.pid, &status, 0); + while (err == -1 && errno == EINTR); + + ASSERT(err == -1); + ASSERT(errno == ECHILD); + + uv_close((uv_handle_t*) &process, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + + +TEST_IMPL(spawn_exit_code) { + int r; + + init_process_options("spawn_helper1", exit_cb); + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_stdout) { + int r; + uv_pipe_t out; + uv_stdio_container_t stdio[2]; + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(uv_default_loop(), &out, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_stdout_to_file) { + int r; + uv_file file; + uv_fs_t fs_req; + uv_stdio_container_t stdio[2]; + uv_buf_t buf; + + /* Setup. */ + unlink("stdout_file"); + + init_process_options("spawn_helper2", exit_cb); + + r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + + file = r; + + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_INHERIT_FD; + options.stdio[1].data.fd = file; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + buf = uv_buf_init(output, sizeof(output)); + r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); + ASSERT(r == 12); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(NULL, &fs_req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0); + + /* Cleanup. */ + unlink("stdout_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_stdout_and_stderr_to_file) { + int r; + uv_file file; + uv_fs_t fs_req; + uv_stdio_container_t stdio[3]; + uv_buf_t buf; + + /* Setup. */ + unlink("stdout_file"); + + init_process_options("spawn_helper6", exit_cb); + + r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + + file = r; + + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_INHERIT_FD; + options.stdio[1].data.fd = file; + options.stdio[2].flags = UV_INHERIT_FD; + options.stdio[2].data.fd = file; + options.stdio_count = 3; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + buf = uv_buf_init(output, sizeof(output)); + r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); + ASSERT(r == 27); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(NULL, &fs_req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); + + /* Cleanup. */ + unlink("stdout_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_stdout_and_stderr_to_file2) { +#ifndef _WIN32 + int r; + uv_file file; + uv_fs_t fs_req; + uv_stdio_container_t stdio[3]; + uv_buf_t buf; + + /* Setup. */ + unlink("stdout_file"); + + init_process_options("spawn_helper6", exit_cb); + + /* Replace stderr with our file */ + r = uv_fs_open(NULL, + &fs_req, + "stdout_file", + O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR, + NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + file = dup2(r, STDERR_FILENO); + ASSERT(file != -1); + + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_INHERIT_FD; + options.stdio[1].data.fd = file; + options.stdio[2].flags = UV_INHERIT_FD; + options.stdio[2].data.fd = file; + options.stdio_count = 3; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + buf = uv_buf_init(output, sizeof(output)); + r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); + ASSERT(r == 27); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(NULL, &fs_req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); + + /* Cleanup. */ + unlink("stdout_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +#else + RETURN_SKIP("Unix only test"); +#endif +} + + +TEST_IMPL(spawn_stdout_and_stderr_to_file_swap) { +#ifndef _WIN32 + int r; + uv_file stdout_file; + uv_file stderr_file; + uv_fs_t fs_req; + uv_stdio_container_t stdio[3]; + uv_buf_t buf; + + /* Setup. */ + unlink("stdout_file"); + unlink("stderr_file"); + + init_process_options("spawn_helper6", exit_cb); + + /* open 'stdout_file' and replace STDOUT_FILENO with it */ + r = uv_fs_open(NULL, + &fs_req, + "stdout_file", + O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR, + NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + stdout_file = dup2(r, STDOUT_FILENO); + ASSERT(stdout_file != -1); + + /* open 'stderr_file' and replace STDERR_FILENO with it */ + r = uv_fs_open(NULL, &fs_req, "stderr_file", O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + stderr_file = dup2(r, STDERR_FILENO); + ASSERT(stderr_file != -1); + + /* now we're going to swap them: the child process' stdout will be our + * stderr_file and vice versa */ + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_INHERIT_FD; + options.stdio[1].data.fd = stderr_file; + options.stdio[2].flags = UV_INHERIT_FD; + options.stdio[2].data.fd = stdout_file; + options.stdio_count = 3; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + buf = uv_buf_init(output, sizeof(output)); + + /* check the content of stdout_file */ + r = uv_fs_read(NULL, &fs_req, stdout_file, &buf, 1, 0, NULL); + ASSERT(r >= 15); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(NULL, &fs_req, stdout_file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strncmp("hello errworld\n", output, 15) == 0); + + /* check the content of stderr_file */ + r = uv_fs_read(NULL, &fs_req, stderr_file, &buf, 1, 0, NULL); + ASSERT(r >= 12); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(NULL, &fs_req, stderr_file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strncmp("hello world\n", output, 12) == 0); + + /* Cleanup. */ + unlink("stdout_file"); + unlink("stderr_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +#else + RETURN_SKIP("Unix only test"); +#endif +} + + +TEST_IMPL(spawn_stdin) { + int r; + uv_pipe_t out; + uv_pipe_t in; + uv_write_t write_req; + uv_buf_t buf; + uv_stdio_container_t stdio[2]; + char buffer[] = "hello-from-spawn_stdin"; + + init_process_options("spawn_helper3", exit_cb); + + uv_pipe_init(uv_default_loop(), &out, 0); + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + buf.base = buffer; + buf.len = sizeof(buffer); + r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */ + ASSERT(strcmp(buffer, output) == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_stdio_greater_than_3) { + int r; + uv_pipe_t pipe; + uv_stdio_container_t stdio[4]; + + init_process_options("spawn_helper5", exit_cb); + + uv_pipe_init(uv_default_loop(), &pipe, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_IGNORE; + options.stdio[2].flags = UV_IGNORE; + options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[3].data.stream = (uv_stream_t*)&pipe; + options.stdio_count = 4; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output from stdio[3] is: %s", output); + ASSERT(strcmp("fourth stdio!\n", output) == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_ignored_stdio) { + int r; + + init_process_options("spawn_helper6", exit_cb); + + options.stdio = NULL; + options.stdio_count = 0; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_and_kill) { + int r; + + init_process_options("spawn_helper4", kill_cb); + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 500, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process and once for timer. */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_preserve_env) { + int r; + uv_pipe_t out; + uv_stdio_container_t stdio[2]; + + init_process_options("spawn_helper7", exit_cb); + + uv_pipe_init(uv_default_loop(), &out, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*) &out; + options.stdio_count = 2; + + r = putenv("ENV_TEST=testval"); + ASSERT(r == 0); + + /* Explicitly set options.env to NULL to test for env clobbering. */ + options.env = NULL; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); + + printf("output is: %s", output); + ASSERT(strcmp("testval", output) == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_detached) { + int r; + + init_process_options("spawn_helper4", detach_failure_cb); + + options.flags |= UV_PROCESS_DETACHED; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + uv_unref((uv_handle_t*)&process); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 0); + + r = uv_kill(process.pid, 0); + ASSERT(r == 0); + + r = uv_kill(process.pid, 15); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(spawn_and_kill_with_std) { + int r; + uv_pipe_t in, out, err; + uv_write_t write; + char message[] = "Nancy's joining me because the message this evening is " + "not my message but ours."; + uv_buf_t buf; + uv_stdio_container_t stdio[3]; + + init_process_options("spawn_helper4", kill_cb); + + r = uv_pipe_init(uv_default_loop(), &in, 0); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &out, 0); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &err, 0); + ASSERT(r == 0); + + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[2].data.stream = (uv_stream_t*)&err; + options.stdio_count = 3; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + buf = uv_buf_init(message, sizeof message); + r = uv_write(&write, (uv_stream_t*) &in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &err, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 500, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 5); /* process x 1, timer x 1, stdio x 3. */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_and_ping) { + uv_write_t write_req; + uv_pipe_t in, out; + uv_buf_t buf; + uv_stdio_container_t stdio[2]; + int r; + + init_process_options("spawn_helper3", exit_cb); + buf = uv_buf_init("TEST", 4); + + uv_pipe_init(uv_default_loop(), &out, 0); + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + /* Sending signum == 0 should check if the + * child process is still alive, not kill it. + */ + r = uv_process_kill(&process, 0); + ASSERT(r == 0); + + r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(strcmp(output, "TEST") == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_same_stdout_stderr) { + uv_write_t write_req; + uv_pipe_t in, out; + uv_buf_t buf; + uv_stdio_container_t stdio[3]; + int r; + + init_process_options("spawn_helper3", exit_cb); + buf = uv_buf_init("TEST", 4); + + uv_pipe_init(uv_default_loop(), &out, 0); + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + /* Sending signum == 0 should check if the + * child process is still alive, not kill it. + */ + r = uv_process_kill(&process, 0); + ASSERT(r == 0); + + r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(strcmp(output, "TEST") == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_closed_process_io) { + uv_pipe_t in; + uv_write_t write_req; + uv_buf_t buf; + uv_stdio_container_t stdio[2]; + static char buffer[] = "hello-from-spawn_stdin\n"; + + init_process_options("spawn_helper3", exit_cb); + + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*) ∈ + options.stdio_count = 1; + + close(0); /* Close process stdin. */ + + ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + + buf = uv_buf_init(buffer, sizeof(buffer)); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* process, child stdin */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(kill) { + int r; + +#ifdef _WIN32 + no_term_signal = 1; +#endif + + init_process_options("spawn_helper4", kill_cb); + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + /* Sending signum == 0 should check if the + * child process is still alive, not kill it. + */ + r = uv_kill(process.pid, 0); + ASSERT(r == 0); + + /* Kill the process. */ + r = uv_kill(process.pid, /* SIGTERM */ 15); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#ifdef _WIN32 +TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { + int r; + uv_pipe_t out; + char name[64]; + HANDLE pipe_handle; + uv_stdio_container_t stdio[2]; + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(uv_default_loop(), &out, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + /* Create a pipe that'll cause a collision. */ + snprintf(name, + sizeof(name), + "\\\\.\\pipe\\uv\\%p-%d", + &out, + GetCurrentProcessId()); + pipe_handle = CreateNamedPipeA(name, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 10, + 65536, + 65536, + 0, + NULL); + ASSERT(pipe_handle != INVALID_HANDLE_VALUE); + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#if !defined(USING_UV_SHARED) +int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr); +WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target); + +TEST_IMPL(argument_escaping) { + const WCHAR* test_str[] = { + L"", + L"HelloWorld", + L"Hello World", + L"Hello\"World", + L"Hello World\\", + L"Hello\\\"World", + L"Hello\\World", + L"Hello\\\\World", + L"Hello World\\", + L"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"" + }; + const int count = sizeof(test_str) / sizeof(*test_str); + WCHAR** test_output; + WCHAR* command_line; + WCHAR** cracked; + size_t total_size = 0; + int i; + int num_args; + int result; + + char* verbatim[] = { + "cmd.exe", + "/c", + "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"", + NULL + }; + WCHAR* verbatim_output; + WCHAR* non_verbatim_output; + + test_output = calloc(count, sizeof(WCHAR*)); + ASSERT(test_output != NULL); + for (i = 0; i < count; ++i) { + test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(WCHAR)); + quote_cmd_arg(test_str[i], test_output[i]); + wprintf(L"input : %s\n", test_str[i]); + wprintf(L"output: %s\n", test_output[i]); + total_size += wcslen(test_output[i]) + 1; + } + command_line = calloc(total_size + 1, sizeof(WCHAR)); + ASSERT(command_line != NULL); + for (i = 0; i < count; ++i) { + wcscat(command_line, test_output[i]); + wcscat(command_line, L" "); + } + command_line[total_size - 1] = L'\0'; + + wprintf(L"command_line: %s\n", command_line); + + cracked = CommandLineToArgvW(command_line, &num_args); + for (i = 0; i < num_args; ++i) { + wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]); + ASSERT(wcscmp(test_str[i], cracked[i]) == 0); + } + + LocalFree(cracked); + for (i = 0; i < count; ++i) { + free(test_output[i]); + } + + result = make_program_args(verbatim, 1, &verbatim_output); + ASSERT(result == 0); + result = make_program_args(verbatim, 0, &non_verbatim_output); + ASSERT(result == 0); + + wprintf(L" verbatim_output: %s\n", verbatim_output); + wprintf(L"non_verbatim_output: %s\n", non_verbatim_output); + + ASSERT(wcscmp(verbatim_output, + L"cmd.exe /c c:\\path\\to\\node.exe --eval " + L"\"require('c:\\\\path\\\\to\\\\test.js')\"") == 0); + ASSERT(wcscmp(non_verbatim_output, + L"cmd.exe /c \"c:\\path\\to\\node.exe --eval " + L"\\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0); + + free(verbatim_output); + free(non_verbatim_output); + + return 0; +} + +int make_program_env(char** env_block, WCHAR** dst_ptr); + +TEST_IMPL(environment_creation) { + int i; + char* environment[] = { + "FOO=BAR", + "SYSTEM=ROOT", /* substring of a supplied var name */ + "SYSTEMROOTED=OMG", /* supplied var name is a substring */ + "TEMP=C:\\Temp", + "INVALID", + "BAZ=QUX", + "B_Z=QUX", + "B\xe2\x82\xacZ=QUX", + "B\xf0\x90\x80\x82Z=QUX", + "B\xef\xbd\xa1Z=QUX", + "B\xf0\xa3\x91\x96Z=QUX", + "BAZ", /* repeat, invalid variable */ + NULL + }; + WCHAR* wenvironment[] = { + L"BAZ=QUX", + L"B_Z=QUX", + L"B\x20acZ=QUX", + L"B\xd800\xdc02Z=QUX", + L"B\xd84d\xdc56Z=QUX", + L"B\xff61Z=QUX", + L"FOO=BAR", + L"SYSTEM=ROOT", /* substring of a supplied var name */ + L"SYSTEMROOTED=OMG", /* supplied var name is a substring */ + L"TEMP=C:\\Temp", + }; + WCHAR* from_env[] = { + /* list should be kept in sync with list + * in process.c, minus variables in wenvironment */ + L"HOMEDRIVE", + L"HOMEPATH", + L"LOGONSERVER", + L"PATH", + L"USERDOMAIN", + L"USERNAME", + L"USERPROFILE", + L"SYSTEMDRIVE", + L"SYSTEMROOT", + L"WINDIR", + /* test for behavior in the absence of a + * required-environment variable: */ + L"ZTHIS_ENV_VARIABLE_DOES_NOT_EXIST", + }; + int found_in_loc_env[ARRAY_SIZE(wenvironment)] = {0}; + int found_in_usr_env[ARRAY_SIZE(from_env)] = {0}; + WCHAR *expected[ARRAY_SIZE(from_env)]; + int result; + WCHAR* str; + WCHAR* prev; + WCHAR* env; + + for (i = 0; i < ARRAY_SIZE(from_env); i++) { + /* copy expected additions to environment locally */ + size_t len = GetEnvironmentVariableW(from_env[i], NULL, 0); + if (len == 0) { + found_in_usr_env[i] = 1; + str = malloc(1 * sizeof(WCHAR)); + *str = 0; + expected[i] = str; + } else { + size_t name_len = wcslen(from_env[i]); + str = malloc((name_len+1+len) * sizeof(WCHAR)); + wmemcpy(str, from_env[i], name_len); + expected[i] = str; + str += name_len; + *str++ = L'='; + GetEnvironmentVariableW(from_env[i], str, len); + } + } + + result = make_program_env(environment, &env); + ASSERT(result == 0); + + for (str = env, prev = NULL; *str; prev = str, str += wcslen(str) + 1) { + int found = 0; +#if 0 + _cputws(str); + putchar('\n'); +#endif + for (i = 0; i < ARRAY_SIZE(wenvironment) && !found; i++) { + if (!wcscmp(str, wenvironment[i])) { + ASSERT(!found_in_loc_env[i]); + found_in_loc_env[i] = 1; + found = 1; + } + } + for (i = 0; i < ARRAY_SIZE(expected) && !found; i++) { + if (!wcscmp(str, expected[i])) { + ASSERT(!found_in_usr_env[i]); + found_in_usr_env[i] = 1; + found = 1; + } + } + if (prev) { /* verify sort order -- requires Vista */ +#if _WIN32_WINNT >= 0x0600 && \ + (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)) + ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1); +#endif + } + ASSERT(found); /* verify that we expected this variable */ + } + + /* verify that we found all expected variables */ + for (i = 0; i < ARRAY_SIZE(wenvironment); i++) { + ASSERT(found_in_loc_env[i]); + } + for (i = 0; i < ARRAY_SIZE(expected); i++) { + ASSERT(found_in_usr_env[i]); + } + + return 0; +} +#endif + +/* Regression test for issue #909 */ +TEST_IMPL(spawn_with_an_odd_path) { + int r; + + char newpath[2048]; + char *path = getenv("PATH"); + ASSERT(path != NULL); + snprintf(newpath, 2048, ";.;%s", path); + SetEnvironmentVariable("PATH", newpath); + + init_process_options("", exit_cb); + options.file = options.args[0] = "program-that-had-better-not-exist"; + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_ENOENT || r == UV_EACCES); + ASSERT(0 == uv_is_active((uv_handle_t*) &process)); + uv_close((uv_handle_t*) &process, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + +#ifndef _WIN32 +TEST_IMPL(spawn_setuid_setgid) { + int r; + struct passwd* pw; + + /* if not root, then this will fail. */ + uv_uid_t uid = getuid(); + if (uid != 0) { + fprintf(stderr, "spawn_setuid_setgid skipped: not root\n"); + return 0; + } + + init_process_options("spawn_helper1", exit_cb); + + /* become the "nobody" user. */ + pw = getpwnam("nobody"); + ASSERT(pw != NULL); + options.uid = pw->pw_uid; + options.gid = pw->pw_gid; + options.flags = UV_PROCESS_SETUID | UV_PROCESS_SETGID; + + r = uv_spawn(uv_default_loop(), &process, &options); + if (r == UV_EACCES) + RETURN_SKIP("user 'nobody' cannot access the test runner"); + + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + + +#ifndef _WIN32 +TEST_IMPL(spawn_setuid_fails) { + int r; + + /* if root, become nobody. */ + uv_uid_t uid = getuid(); + if (uid == 0) { + struct passwd* pw; + pw = getpwnam("nobody"); + ASSERT(pw != NULL); + ASSERT(0 == setgid(pw->pw_gid)); + ASSERT(0 == setuid(pw->pw_uid)); + } + + init_process_options("spawn_helper1", fail_cb); + + options.flags |= UV_PROCESS_SETUID; + options.uid = 0; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_EPERM); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_setgid_fails) { + int r; + + /* if root, become nobody. */ + uv_uid_t uid = getuid(); + if (uid == 0) { + struct passwd* pw; + pw = getpwnam("nobody"); + ASSERT(pw != NULL); + ASSERT(0 == setgid(pw->pw_gid)); + ASSERT(0 == setuid(pw->pw_uid)); + } + + init_process_options("spawn_helper1", fail_cb); + + options.flags |= UV_PROCESS_SETGID; + options.gid = 0; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_EPERM); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + + +#ifdef _WIN32 + +static void exit_cb_unexpected(uv_process_t* process, + int64_t exit_status, + int term_signal) { + ASSERT(0 && "should not have been called"); +} + + +TEST_IMPL(spawn_setuid_fails) { + int r; + + init_process_options("spawn_helper1", exit_cb_unexpected); + + options.flags |= UV_PROCESS_SETUID; + options.uid = (uv_uid_t) -42424242; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_ENOTSUP); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_setgid_fails) { + int r; + + init_process_options("spawn_helper1", exit_cb_unexpected); + + options.flags |= UV_PROCESS_SETGID; + options.gid = (uv_gid_t) -42424242; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_ENOTSUP); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + + +TEST_IMPL(spawn_auto_unref) { + init_process_options("spawn_helper1", NULL); + ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &process)); + uv_close((uv_handle_t*) &process, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(1 == uv_is_closing((uv_handle_t*) &process)); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +#ifndef _WIN32 +TEST_IMPL(spawn_fs_open) { + int fd; + uv_fs_t fs_req; + uv_pipe_t in; + uv_write_t write_req; + uv_buf_t buf; + uv_stdio_container_t stdio[1]; + + fd = uv_fs_open(NULL, &fs_req, "/dev/null", O_RDWR, 0, NULL); + ASSERT(fd >= 0); + uv_fs_req_cleanup(&fs_req); + + init_process_options("spawn_helper8", exit_cb); + + ASSERT(0 == uv_pipe_init(uv_default_loop(), &in, 0)); + + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*) ∈ + options.stdio_count = 1; + + ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + + buf = uv_buf_init((char*) &fd, sizeof(fd)); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(0 == uv_fs_close(NULL, &fs_req, fd, NULL)); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* One for `in`, one for process */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif /* !_WIN32 */ + + +#ifndef _WIN32 +TEST_IMPL(closed_fd_events) { + uv_stdio_container_t stdio[3]; + uv_pipe_t pipe_handle; + int fd[2]; + + /* create a pipe and share it with a child process */ + ASSERT(0 == pipe(fd)); + + /* spawn_helper4 blocks indefinitely. */ + init_process_options("spawn_helper4", exit_cb); + options.stdio_count = 3; + options.stdio = stdio; + options.stdio[0].flags = UV_INHERIT_FD; + options.stdio[0].data.fd = fd[0]; + options.stdio[1].flags = UV_IGNORE; + options.stdio[2].flags = UV_IGNORE; + + ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + uv_unref((uv_handle_t*) &process); + + /* read from the pipe with uv */ + ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + fd[0] = -1; + + ASSERT(0 == uv_read_start((uv_stream_t*) &pipe_handle, on_alloc, on_read_once)); + + ASSERT(1 == write(fd[1], "", 1)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + + /* should have received just one byte */ + ASSERT(output_used == 1); + + /* close the pipe and see if we still get events */ + uv_close((uv_handle_t*) &pipe_handle, close_cb); + + ASSERT(1 == write(fd[1], "", 1)); + + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_counter_cb, 10, 0)); + + /* see if any spurious events interrupt the timer */ + if (1 == uv_run(uv_default_loop(), UV_RUN_ONCE)) + /* have to run again to really trigger the timer */ + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + + ASSERT(timer_counter == 1); + + /* cleanup */ + ASSERT(0 == uv_process_kill(&process, /* SIGTERM */ 15)); + ASSERT(0 == close(fd[1])); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif /* !_WIN32 */ + +TEST_IMPL(spawn_reads_child_path) { + int r; + int len; + char file[64]; + char path[1024]; + char* env[3]; + + /* Need to carry over the dynamic linker path when the test runner is + * linked against libuv.so, see https://github.com/libuv/libuv/issues/85. + */ +#if defined(__APPLE__) + static const char dyld_path_var[] = "DYLD_LIBRARY_PATH"; +#else + static const char dyld_path_var[] = "LD_LIBRARY_PATH"; +#endif + + /* Set up the process, but make sure that the file to run is relative and */ + /* requires a lookup into PATH */ + init_process_options("spawn_helper1", exit_cb); + + /* Set up the PATH env variable */ + for (len = strlen(exepath); + exepath[len - 1] != '/' && exepath[len - 1] != '\\'; + len--); + strcpy(file, exepath + len); + exepath[len] = 0; + strcpy(path, "PATH="); + strcpy(path + 5, exepath); + + env[0] = path; + env[1] = getenv(dyld_path_var); + env[2] = NULL; + + if (env[1] != NULL) { + static char buf[1024 + sizeof(dyld_path_var)]; + snprintf(buf, sizeof(buf), "%s=%s", dyld_path_var, env[1]); + env[1] = buf; + } + + options.file = file; + options.args[0] = file; + options.env = env; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#ifndef _WIN32 +static int mpipe(int *fds) { + if (pipe(fds) == -1) + return -1; + if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { + close(fds[0]); + close(fds[1]); + return -1; + } + return 0; +} +#else +static int mpipe(int *fds) { + SECURITY_ATTRIBUTES attr; + HANDLE readh, writeh; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = FALSE; + if (!CreatePipe(&readh, &writeh, &attr, 0)) + return -1; + fds[0] = _open_osfhandle((intptr_t)readh, 0); + fds[1] = _open_osfhandle((intptr_t)writeh, 0); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(readh); + CloseHandle(writeh); + return -1; + } + return 0; +} +#endif /* !_WIN32 */ + +TEST_IMPL(spawn_inherit_streams) { + uv_process_t child_req; + uv_stdio_container_t child_stdio[2]; + int fds_stdin[2]; + int fds_stdout[2]; + uv_pipe_t pipe_stdin_child; + uv_pipe_t pipe_stdout_child; + uv_pipe_t pipe_stdin_parent; + uv_pipe_t pipe_stdout_parent; + unsigned char ubuf[OUTPUT_SIZE - 1]; + uv_buf_t buf; + unsigned int i; + int r; + uv_write_t write_req; + uv_loop_t* loop; + + init_process_options("spawn_helper9", exit_cb); + + loop = uv_default_loop(); + ASSERT(uv_pipe_init(loop, &pipe_stdin_child, 0) == 0); + ASSERT(uv_pipe_init(loop, &pipe_stdout_child, 0) == 0); + ASSERT(uv_pipe_init(loop, &pipe_stdin_parent, 0) == 0); + ASSERT(uv_pipe_init(loop, &pipe_stdout_parent, 0) == 0); + + ASSERT(mpipe(fds_stdin) != -1); + ASSERT(mpipe(fds_stdout) != -1); + + ASSERT(uv_pipe_open(&pipe_stdin_child, fds_stdin[0]) == 0); + ASSERT(uv_pipe_open(&pipe_stdout_child, fds_stdout[1]) == 0); + ASSERT(uv_pipe_open(&pipe_stdin_parent, fds_stdin[1]) == 0); + ASSERT(uv_pipe_open(&pipe_stdout_parent, fds_stdout[0]) == 0); + + child_stdio[0].flags = UV_INHERIT_STREAM; + child_stdio[0].data.stream = (uv_stream_t *)&pipe_stdin_child; + + child_stdio[1].flags = UV_INHERIT_STREAM; + child_stdio[1].data.stream = (uv_stream_t *)&pipe_stdout_child; + + options.stdio = child_stdio; + options.stdio_count = 2; + + ASSERT(uv_spawn(loop, &child_req, &options) == 0); + + uv_close((uv_handle_t*)&pipe_stdin_child, NULL); + uv_close((uv_handle_t*)&pipe_stdout_child, NULL); + + buf = uv_buf_init((char*)ubuf, sizeof ubuf); + for (i = 0; i < sizeof ubuf; ++i) + ubuf[i] = i & 255u; + memset(output, 0, sizeof ubuf); + + r = uv_write(&write_req, + (uv_stream_t*)&pipe_stdin_parent, + &buf, + 1, + write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&pipe_stdout_parent, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 3); + + r = memcmp(ubuf, output, sizeof ubuf); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +/* Helper for child process of spawn_inherit_streams */ +#ifndef _WIN32 +int spawn_stdin_stdout(void) { + char buf[1024]; + char* pbuf; + for (;;) { + ssize_t r, w, c; + do { + r = read(0, buf, sizeof buf); + } while (r == -1 && errno == EINTR); + if (r == 0) { + return 1; + } + ASSERT(r > 0); + c = r; + pbuf = buf; + while (c) { + do { + w = write(1, pbuf, (size_t)c); + } while (w == -1 && errno == EINTR); + ASSERT(w >= 0); + pbuf = pbuf + w; + c = c - w; + } + } + return 2; +} +#else +int spawn_stdin_stdout(void) { + char buf[1024]; + char* pbuf; + HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE); + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + ASSERT(h_stdin != INVALID_HANDLE_VALUE); + ASSERT(h_stdout != INVALID_HANDLE_VALUE); + for (;;) { + DWORD n_read; + DWORD n_written; + DWORD to_write; + if (!ReadFile(h_stdin, buf, sizeof buf, &n_read, NULL)) { + ASSERT(GetLastError() == ERROR_BROKEN_PIPE); + return 1; + } + to_write = n_read; + pbuf = buf; + while (to_write) { + ASSERT(WriteFile(h_stdout, pbuf, to_write, &n_written, NULL)); + to_write -= n_written; + pbuf += n_written; + } + } + return 2; +} +#endif /* !_WIN32 */ diff --git a/3rdparty/libuv/test/test-stdio-over-pipes.c b/3rdparty/libuv/test/test-stdio-over-pipes.c new file mode 100644 index 00000000000..15744761049 --- /dev/null +++ b/3rdparty/libuv/test/test-stdio-over-pipes.c @@ -0,0 +1,255 @@ +/* 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 <stdlib.h> +#include <string.h> + + +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; +static uv_process_options_t options; +static int close_cb_called; +static int exit_cb_called; +static int on_read_cb_called; +static int after_write_cb_called; +static uv_pipe_t in; +static uv_pipe_t out; +static uv_loop_t* loop; +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void exit_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 0); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, close_cb); + uv_close((uv_handle_t*)&in, close_cb); + uv_close((uv_handle_t*)&out, close_cb); +} + + +static void init_process_options(char* test, uv_exit_cb exit_cb) { + int r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = test; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; +} + + +static void on_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = output + output_used; + buf->len = OUTPUT_SIZE - output_used; +} + + +static void after_write(uv_write_t* req, int status) { + if (status) { + fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); + ASSERT(0); + } + + /* Free the read/write buffer and the request */ + free(req); + + after_write_cb_called++; +} + + +static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* rdbuf) { + uv_write_t* req; + uv_buf_t wrbuf; + int r; + + ASSERT(nread > 0 || nread == UV_EOF); + + if (nread > 0) { + output_used += nread; + if (output_used == 12) { + ASSERT(memcmp("hello world\n", output, 12) == 0); + wrbuf = uv_buf_init(output, output_used); + req = malloc(sizeof(*req)); + r = uv_write(req, (uv_stream_t*)&in, &wrbuf, 1, after_write); + ASSERT(r == 0); + } + } + + on_read_cb_called++; +} + + +TEST_IMPL(stdio_over_pipes) { + int r; + uv_process_t process; + uv_stdio_container_t stdio[2]; + + loop = uv_default_loop(); + + init_process_options("stdio_over_pipes_helper", exit_cb); + + uv_pipe_init(loop, &out, 0); + uv_pipe_init(loop, &in, 0); + + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + r = uv_spawn(loop, &process, &options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(on_read_cb_called > 1); + ASSERT(after_write_cb_called == 1); + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 3); + ASSERT(memcmp("hello world\n", output, 12) == 0); + ASSERT(output_used == 12); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +/* Everything here runs in a child process. */ + +static int on_pipe_read_called; +static int after_write_called; +static uv_pipe_t stdin_pipe; +static uv_pipe_t stdout_pipe; + +static void on_pipe_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + ASSERT(nread > 0); + ASSERT(memcmp("hello world\n", buf->base, nread) == 0); + on_pipe_read_called++; + + free(buf->base); + + uv_close((uv_handle_t*)&stdin_pipe, close_cb); + uv_close((uv_handle_t*)&stdout_pipe, close_cb); +} + + +static void after_pipe_write(uv_write_t* req, int status) { + ASSERT(status == 0); + after_write_called++; +} + + +static void on_read_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + + +int stdio_over_pipes_helper(void) { + /* Write several buffers to test that the write order is preserved. */ + char* buffers[] = { + "he", + "ll", + "o ", + "wo", + "rl", + "d", + "\n" + }; + + uv_write_t write_req[ARRAY_SIZE(buffers)]; + uv_buf_t buf[ARRAY_SIZE(buffers)]; + unsigned int i; + int r; + uv_loop_t* loop = uv_default_loop(); + + ASSERT(UV_NAMED_PIPE == uv_guess_handle(0)); + ASSERT(UV_NAMED_PIPE == uv_guess_handle(1)); + + r = uv_pipe_init(loop, &stdin_pipe, 0); + ASSERT(r == 0); + r = uv_pipe_init(loop, &stdout_pipe, 0); + ASSERT(r == 0); + + uv_pipe_open(&stdin_pipe, 0); + uv_pipe_open(&stdout_pipe, 1); + + /* Unref both stdio handles to make sure that all writes complete. */ + uv_unref((uv_handle_t*)&stdin_pipe); + uv_unref((uv_handle_t*)&stdout_pipe); + + for (i = 0; i < ARRAY_SIZE(buffers); i++) { + buf[i] = uv_buf_init((char*)buffers[i], strlen(buffers[i])); + } + + for (i = 0; i < ARRAY_SIZE(buffers); i++) { + r = uv_write(&write_req[i], (uv_stream_t*)&stdout_pipe, &buf[i], 1, + after_pipe_write); + ASSERT(r == 0); + } + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(after_write_called == 7); + ASSERT(on_pipe_read_called == 0); + ASSERT(close_cb_called == 0); + + uv_ref((uv_handle_t*)&stdout_pipe); + uv_ref((uv_handle_t*)&stdin_pipe); + + r = uv_read_start((uv_stream_t*)&stdin_pipe, on_read_alloc, on_pipe_read); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(after_write_called == 7); + ASSERT(on_pipe_read_called == 1); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-bind-error.c b/3rdparty/libuv/test/test-tcp-bind-error.c new file mode 100644 index 00000000000..10ed68e10ec --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-bind-error.c @@ -0,0 +1,216 @@ +/* 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 close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(tcp_bind_error_addrinuse) { + struct sockaddr_in addr; + uv_tcp_t server1, server2; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + r = uv_tcp_init(uv_default_loop(), &server1); + ASSERT(r == 0); + r = uv_tcp_bind(&server1, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_tcp_init(uv_default_loop(), &server2); + ASSERT(r == 0); + r = uv_tcp_bind(&server2, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server1, 128, NULL); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); + ASSERT(r == UV_EADDRINUSE); + + uv_close((uv_handle_t*)&server1, close_cb); + uv_close((uv_handle_t*)&server2, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_error_addrnotavail_1) { + struct sockaddr_in addr; + uv_tcp_t server; + int r; + + ASSERT(0 == uv_ip4_addr("127.255.255.255", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + /* It seems that Linux is broken here - bind succeeds. */ + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0 || r == UV_EADDRNOTAVAIL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_error_addrnotavail_2) { + struct sockaddr_in addr; + uv_tcp_t server; + int r; + + ASSERT(0 == uv_ip4_addr("4.4.4.4", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == UV_EADDRNOTAVAIL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_error_fault) { + char garbage[] = + "blah blah blah blah blah blah blah blah blah blah blah blah"; + struct sockaddr_in* garbage_addr; + uv_tcp_t server; + int r; + + garbage_addr = (struct sockaddr_in*) &garbage; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) garbage_addr, 0); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +/* Notes: On Linux uv_bind(server, NULL) will segfault the program. */ + +TEST_IMPL(tcp_bind_error_inval) { + struct sockaddr_in addr1; + struct sockaddr_in addr2; + uv_tcp_t server; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr1)); + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT_2, &addr2)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr1, 0); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr2, 0); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_localhost_ok) { + struct sockaddr_in addr; + uv_tcp_t server; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_invalid_flags) { + struct sockaddr_in addr; + uv_tcp_t server; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, UV_TCP_IPV6ONLY); + ASSERT(r == UV_EINVAL); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_listen_without_bind) { + int r; + uv_tcp_t server; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server, 128, NULL); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-bind6-error.c b/3rdparty/libuv/test/test-tcp-bind6-error.c new file mode 100644 index 00000000000..b762bcb3d1b --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-bind6-error.c @@ -0,0 +1,176 @@ +/* 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 close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(tcp_bind6_error_addrinuse) { + struct sockaddr_in6 addr; + uv_tcp_t server1, server2; + int r; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server1); + ASSERT(r == 0); + r = uv_tcp_bind(&server1, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_tcp_init(uv_default_loop(), &server2); + ASSERT(r == 0); + r = uv_tcp_bind(&server2, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&server1, 128, NULL); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); + ASSERT(r == UV_EADDRINUSE); + + uv_close((uv_handle_t*)&server1, close_cb); + uv_close((uv_handle_t*)&server2, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind6_error_addrnotavail) { + struct sockaddr_in6 addr; + uv_tcp_t server; + int r; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("4:4:4:4:4:4:4:4", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == UV_EADDRNOTAVAIL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind6_error_fault) { + char garbage[] = + "blah blah blah blah blah blah blah blah blah blah blah blah"; + struct sockaddr_in6* garbage_addr; + uv_tcp_t server; + int r; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + garbage_addr = (struct sockaddr_in6*) &garbage; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) garbage_addr, 0); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +/* Notes: On Linux uv_bind6(server, NULL) will segfault the program. */ + +TEST_IMPL(tcp_bind6_error_inval) { + struct sockaddr_in6 addr1; + struct sockaddr_in6 addr2; + uv_tcp_t server; + int r; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr1)); + ASSERT(0 == uv_ip6_addr("::", TEST_PORT_2, &addr2)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr1, 0); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr2, 0); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind6_localhost_ok) { + struct sockaddr_in6 addr; + uv_tcp_t server; + int r; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-close-accept.c b/3rdparty/libuv/test/test-tcp-close-accept.c new file mode 100644 index 00000000000..5517aaf99e6 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-close-accept.c @@ -0,0 +1,188 @@ +/* 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. + */ + +/* this test is Unix only */ +#ifndef _WIN32 + +#include "uv.h" +#include "task.h" + +#include <stdio.h> +#include <string.h> + +static struct sockaddr_in addr; +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_outgoing[2]; +static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; +static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; +static uv_tcp_t tcp_check; +static uv_connect_t tcp_check_req; +static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; +static unsigned int got_connections; +static unsigned int close_cb_called; +static unsigned int write_cb_called; +static unsigned int read_cb_called; + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + write_cb_called++; +} + +static void connect_cb(uv_connect_t* req, int status) { + unsigned int i; + uv_buf_t buf; + uv_stream_t* outgoing; + + if (req == &tcp_check_req) { + ASSERT(status != 0); + + /* Close check and incoming[0], time to finish test */ + uv_close((uv_handle_t*) &tcp_incoming[0], close_cb); + uv_close((uv_handle_t*) &tcp_check, close_cb); + return; + } + + ASSERT(status == 0); + ASSERT(connect_reqs <= req); + ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); + i = req - connect_reqs; + + buf = uv_buf_init("x", 1); + outgoing = (uv_stream_t*) &tcp_outgoing[i]; + ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); +} + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + static char slab[1]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + uv_loop_t* loop; + unsigned int i; + + /* Only first stream should receive read events */ + ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]); + ASSERT(0 == uv_read_stop(stream)); + ASSERT(1 == nread); + + loop = stream->loop; + read_cb_called++; + + /* Close all active incomings, except current one */ + for (i = 1; i < got_connections; i++) + uv_close((uv_handle_t*) &tcp_incoming[i], close_cb); + + /* Create new fd that should be one of the closed incomings */ + ASSERT(0 == uv_tcp_init(loop, &tcp_check)); + ASSERT(0 == uv_tcp_connect(&tcp_check_req, + &tcp_check, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); + + /* Close server, so no one will connect to it */ + uv_close((uv_handle_t*) &tcp_server, close_cb); +} + +static void connection_cb(uv_stream_t* server, int status) { + unsigned int i; + uv_tcp_t* incoming; + + ASSERT(server == (uv_stream_t*) &tcp_server); + + /* Ignore tcp_check connection */ + if (got_connections == ARRAY_SIZE(tcp_incoming)) + return; + + /* Accept everyone */ + incoming = &tcp_incoming[got_connections++]; + ASSERT(0 == uv_tcp_init(server->loop, incoming)); + ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); + + if (got_connections != ARRAY_SIZE(tcp_incoming)) + return; + + /* Once all clients are accepted - start reading */ + for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { + incoming = &tcp_incoming[i]; + ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); + } +} + +TEST_IMPL(tcp_close_accept) { + unsigned int i; + uv_loop_t* loop; + uv_tcp_t* client; + + /* + * A little explanation of what goes on below: + * + * We'll create server and connect to it using two clients, each writing one + * byte once connected. + * + * When all clients will be accepted by server - we'll start reading from them + * and, on first client's first byte, will close second client and server. + * After that, we'll immediately initiate new connection to server using + * tcp_check handle (thus, reusing fd from second client). + * + * In this situation uv__io_poll()'s event list should still contain read + * event for second client, and, if not cleaned up properly, `tcp_check` will + * receive stale event of second incoming and invoke `connect_cb` with zero + * status. + */ + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(loop, &tcp_server)); + ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, + ARRAY_SIZE(tcp_outgoing), + connection_cb)); + + for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { + client = tcp_outgoing + i; + + ASSERT(0 == uv_tcp_init(loop, client)); + ASSERT(0 == uv_tcp_connect(&connect_reqs[i], + client, + (const struct sockaddr*) &addr, + connect_cb)); + } + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); + ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); + ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); + ASSERT(1 == read_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* !_WIN32 */ diff --git a/3rdparty/libuv/test/test-tcp-close-while-connecting.c b/3rdparty/libuv/test/test-tcp-close-while-connecting.c new file mode 100644 index 00000000000..2c39b652b61 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-close-while-connecting.c @@ -0,0 +1,86 @@ +/* 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 timer1_handle; +static uv_timer_t timer2_handle; +static uv_tcp_t tcp_handle; + +static int connect_cb_called; +static int timer1_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(status == UV_ECANCELED); + uv_timer_stop(&timer2_handle); + connect_cb_called++; +} + + +static void timer1_cb(uv_timer_t* handle) { + uv_close((uv_handle_t*)handle, close_cb); + uv_close((uv_handle_t*)&tcp_handle, close_cb); + timer1_cb_called++; +} + + +static void timer2_cb(uv_timer_t* handle) { + ASSERT(0 && "should not be called"); +} + + +TEST_IMPL(tcp_close_while_connecting) { + uv_connect_t connect_req; + struct sockaddr_in addr; + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("1.2.3.4", TEST_PORT, &addr)); + ASSERT(0 == uv_tcp_init(loop, &tcp_handle)); + r = uv_tcp_connect(&connect_req, + &tcp_handle, + (const struct sockaddr*) &addr, + connect_cb); + if (r == UV_ENETUNREACH) + RETURN_SKIP("Network unreachable."); + ASSERT(r == 0); + ASSERT(0 == uv_timer_init(loop, &timer1_handle)); + ASSERT(0 == uv_timer_start(&timer1_handle, timer1_cb, 50, 0)); + ASSERT(0 == uv_timer_init(loop, &timer2_handle)); + ASSERT(0 == uv_timer_start(&timer2_handle, timer2_cb, 86400 * 1000, 0)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(connect_cb_called == 1); + ASSERT(timer1_cb_called == 1); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-close.c b/3rdparty/libuv/test/test-tcp-close.c new file mode 100644 index 00000000000..e65885aa556 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-close.c @@ -0,0 +1,136 @@ +/* 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 <errno.h> +#include <string.h> /* memset */ + +#define NUM_WRITE_REQS 32 + +static uv_tcp_t tcp_handle; +static uv_connect_t connect_req; + +static int write_cb_called; +static int close_cb_called; + +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void close_cb(uv_handle_t* handle); + + +static void connect_cb(uv_connect_t* conn_req, int status) { + uv_write_t* req; + uv_buf_t buf; + int i, r; + + buf = uv_buf_init("PING", 4); + for (i = 0; i < NUM_WRITE_REQS; i++) { + req = malloc(sizeof *req); + ASSERT(req != NULL); + + r = uv_write(req, (uv_stream_t*)&tcp_handle, &buf, 1, write_cb); + ASSERT(r == 0); + } + + uv_close((uv_handle_t*)&tcp_handle, close_cb); +} + + +static void write_cb(uv_write_t* req, int status) { + /* write callbacks should run before the close callback */ + ASSERT(close_cb_called == 0); + ASSERT(req->handle == (uv_stream_t*)&tcp_handle); + write_cb_called++; + free(req); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*)&tcp_handle); + close_cb_called++; +} + + +static void connection_cb(uv_stream_t* server, int status) { + ASSERT(status == 0); +} + + +static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(loop, handle); + ASSERT(r == 0); + + r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)handle, 128, connection_cb); + ASSERT(r == 0); + + uv_unref((uv_handle_t*)handle); +} + + +/* Check that pending write requests have their callbacks + * invoked when the handle is closed. + */ +TEST_IMPL(tcp_close) { + struct sockaddr_in addr; + uv_tcp_t tcp_server; + uv_loop_t* loop; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + loop = uv_default_loop(); + + /* We can't use the echo server, it doesn't handle ECONNRESET. */ + start_server(loop, &tcp_server); + + r = uv_tcp_init(loop, &tcp_handle); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_handle, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + ASSERT(write_cb_called == 0); + ASSERT(close_cb_called == 0); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + printf("%d of %d write reqs seen\n", write_cb_called, NUM_WRITE_REQS); + + ASSERT(write_cb_called == NUM_WRITE_REQS); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-connect-error-after-write.c b/3rdparty/libuv/test/test-tcp-connect-error-after-write.c new file mode 100644 index 00000000000..3f2e3572da9 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-connect-error-after-write.c @@ -0,0 +1,98 @@ +/* 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 <string.h> + +static int connect_cb_called; +static int write_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(status < 0); + connect_cb_called++; + uv_close((uv_handle_t*)req->handle, close_cb); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status < 0); + write_cb_called++; +} + + +/* + * Try to connect to an address on which nothing listens, get ECONNREFUSED + * (uv errno 12) and get connect_cb() called once with status != 0. + * Related issue: https://github.com/joyent/libuv/issues/443 + */ +TEST_IMPL(tcp_connect_error_after_write) { + uv_connect_t connect_req; + struct sockaddr_in addr; + uv_write_t write_req; + uv_tcp_t conn; + uv_buf_t buf; + int r; + +#ifdef _WIN32 + fprintf(stderr, "This test is disabled on Windows for now.\n"); + fprintf(stderr, "See https://github.com/joyent/libuv/issues/444\n"); + return 0; /* windows slackers... */ +#endif + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + buf = uv_buf_init("TEST", 4); + + r = uv_tcp_init(uv_default_loop(), &conn); + ASSERT(r == 0); + + r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); + ASSERT(r == UV_EBADF); + + r = uv_tcp_connect(&connect_req, + &conn, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-connect-error.c b/3rdparty/libuv/test/test-tcp-connect-error.c new file mode 100644 index 00000000000..eab1eeb2545 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-connect-error.c @@ -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" +#include <stdio.h> +#include <stdlib.h> + + +static int connect_cb_called = 0; +static int close_cb_called = 0; + + + +static void connect_cb(uv_connect_t* handle, int status) { + ASSERT(handle != NULL); + connect_cb_called++; +} + + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(tcp_connect_error_fault) { + const char garbage[] = + "blah blah blah blah blah blah blah blah blah blah blah blah"; + const struct sockaddr_in* garbage_addr; + uv_tcp_t server; + int r; + uv_connect_t req; + + garbage_addr = (const struct sockaddr_in*) &garbage; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_connect(&req, + &server, + (const struct sockaddr*) garbage_addr, + connect_cb); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(connect_cb_called == 0); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-connect-timeout.c b/3rdparty/libuv/test/test-tcp-connect-timeout.c new file mode 100644 index 00000000000..081424b8002 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-connect-timeout.c @@ -0,0 +1,91 @@ +/* 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); +static void close_cb(uv_handle_t* handle); + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req == &connect_req); + ASSERT(status == UV_ECANCELED); + connect_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + 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; + + ASSERT(0 == uv_ip4_addr("8.8.8.8", 9999, &addr)); + + 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, + (const struct sockaddr*) &addr, + connect_cb); + if (r == UV_ENETUNREACH) + RETURN_SKIP("Network unreachable."); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-connect6-error.c b/3rdparty/libuv/test/test-tcp-connect6-error.c new file mode 100644 index 00000000000..91ac0a3a101 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-connect6-error.c @@ -0,0 +1,71 @@ +/* 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 = 0; +static int close_cb_called = 0; + + +static void connect_cb(uv_connect_t* handle, int status) { + ASSERT(handle != NULL); + connect_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(tcp_connect6_error_fault) { + const char garbage[] = + "blah blah blah blah blah blah blah blah blah blah blah blah"; + const struct sockaddr_in6* garbage_addr; + uv_tcp_t server; + int r; + uv_connect_t req; + + garbage_addr = (const struct sockaddr_in6*) &garbage; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_tcp_connect(&req, + &server, + (const struct sockaddr*) garbage_addr, + connect_cb); + ASSERT(r == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(connect_cb_called == 0); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-create-socket-early.c b/3rdparty/libuv/test/test-tcp-create-socket-early.c new file mode 100644 index 00000000000..65650adcc27 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-create-socket-early.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>. + * 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 <string.h> + +#ifdef _WIN32 +# define INVALID_FD (INVALID_HANDLE_VALUE) +#else +# define INVALID_FD (-1) +#endif + + +static void on_connect(uv_connect_t* req, int status) { + ASSERT(status == 0); + uv_close((uv_handle_t*) req->handle, NULL); +} + + +static void on_connection(uv_stream_t* server, int status) { + uv_tcp_t* handle; + int r; + + ASSERT(status == 0); + + handle = malloc(sizeof(*handle)); + ASSERT(handle != NULL); + + r = uv_tcp_init_ex(server->loop, handle, AF_INET); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)handle); + ASSERT(r == UV_EBUSY); + + uv_close((uv_handle_t*) server, NULL); + uv_close((uv_handle_t*) handle, (uv_close_cb)free); +} + + +static void tcp_listener(uv_loop_t* loop, uv_tcp_t* server) { + struct sockaddr_in addr; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_tcp_init(loop, server); + ASSERT(r == 0); + + r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*) server, 128, on_connection); + ASSERT(r == 0); +} + + +static void tcp_connector(uv_loop_t* loop, uv_tcp_t* client, uv_connect_t* req) { + struct sockaddr_in server_addr; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + + r = uv_tcp_init(loop, client); + ASSERT(r == 0); + + r = uv_tcp_connect(req, + client, + (const struct sockaddr*) &server_addr, + on_connect); + ASSERT(r == 0); +} + + +TEST_IMPL(tcp_create_early) { + struct sockaddr_in addr; + struct sockaddr_in sockname; + uv_tcp_t client; + uv_os_fd_t fd; + int r, namelen; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init_ex(uv_default_loop(), &client, AF_INET); + ASSERT(r == 0); + + r = uv_fileno((const uv_handle_t*) &client, &fd); + ASSERT(r == 0); + ASSERT(fd != INVALID_FD); + + /* Windows returns WSAEINVAL if the socket is not bound */ +#ifndef _WIN32 + namelen = sizeof sockname; + r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); + ASSERT(r == 0); + ASSERT(sockname.sin_family == AF_INET); +#endif + + r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); + ASSERT(r == 0); + ASSERT(memcmp(&addr.sin_addr, + &sockname.sin_addr, + sizeof(addr.sin_addr)) == 0); + + uv_close((uv_handle_t*) &client, NULL); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_create_early_bad_bind) { + struct sockaddr_in addr; + uv_tcp_t client; + uv_os_fd_t fd; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init_ex(uv_default_loop(), &client, AF_INET6); + ASSERT(r == 0); + + r = uv_fileno((const uv_handle_t*) &client, &fd); + ASSERT(r == 0); + ASSERT(fd != INVALID_FD); + + /* Windows returns WSAEINVAL if the socket is not bound */ +#ifndef _WIN32 + { + int namelen; + struct sockaddr_in6 sockname; + namelen = sizeof sockname; + r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); + ASSERT(r == 0); + ASSERT(sockname.sin6_family == AF_INET6); + } +#endif + + r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); +#ifndef _WIN32 + ASSERT(r == UV_EINVAL); +#else + ASSERT(r == UV_EFAULT); +#endif + + uv_close((uv_handle_t*) &client, NULL); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_create_early_bad_domain) { + uv_tcp_t client; + int r; + + r = uv_tcp_init_ex(uv_default_loop(), &client, 47); + ASSERT(r == UV_EINVAL); + + r = uv_tcp_init_ex(uv_default_loop(), &client, 1024); + ASSERT(r == UV_EINVAL); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_create_early_accept) { + uv_tcp_t client, server; + uv_connect_t connect_req; + + tcp_listener(uv_default_loop(), &server); + tcp_connector(uv_default_loop(), &client, &connect_req); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-flags.c b/3rdparty/libuv/test/test-tcp-flags.c new file mode 100644 index 00000000000..68afb39f456 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-flags.c @@ -0,0 +1,52 @@ +/* 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> + + +TEST_IMPL(tcp_flags) { + uv_loop_t* loop; + uv_tcp_t handle; + int r; + + loop = uv_default_loop(); + + r = uv_tcp_init(loop, &handle); + ASSERT(r == 0); + + r = uv_tcp_nodelay(&handle, 1); + ASSERT(r == 0); + + r = uv_tcp_keepalive(&handle, 1, 60); + ASSERT(r == 0); + + uv_close((uv_handle_t*)&handle, NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-oob.c b/3rdparty/libuv/test/test-tcp-oob.c new file mode 100644 index 00000000000..fc011ee495f --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-oob.c @@ -0,0 +1,128 @@ +/* Copyright Fedor Indutny. 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. + */ + +#if !defined(_WIN32) + +#include "uv.h" +#include "task.h" + +#include <errno.h> +#include <sys/socket.h> +#include <unistd.h> + +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; +static uv_tcp_t peer_handle; +static uv_idle_t idle; +static uv_connect_t connect_req; +static int ticks; +static const int kMaxTicks = 10; + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char storage[1024]; + *buf = uv_buf_init(storage, sizeof(storage)); +} + + +static void idle_cb(uv_idle_t* idle) { + if (++ticks < kMaxTicks) + return; + + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); + uv_close((uv_handle_t*) &peer_handle, NULL); + uv_close((uv_handle_t*) idle, NULL); +} + + +static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { + ASSERT(nread > 0); + ASSERT(0 == uv_idle_start(&idle, idle_cb)); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &client_handle); + ASSERT(0 == status); +} + + +static void connection_cb(uv_stream_t* handle, int status) { + int r; + uv_os_fd_t fd; + + ASSERT(0 == status); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + + /* Send some OOB data */ + ASSERT(0 == uv_fileno((uv_handle_t*) &client_handle, &fd)); + + ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &client_handle, 1)); + + /* The problem triggers only on a second message, it seem that xnu is not + * triggering `kevent()` for the first one + */ + do { + r = send(fd, "hello", 5, MSG_OOB); + } while (r < 0 && errno == EINTR); + ASSERT(5 == r); + + do { + r = send(fd, "hello", 5, MSG_OOB); + } while (r < 0 && errno == EINTR); + ASSERT(5 == r); + + ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &client_handle, 0)); +} + + +TEST_IMPL(tcp_oob) { + struct sockaddr_in addr; + uv_loop_t* loop; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + loop = uv_default_loop(); + + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_init(loop, &peer_handle)); + ASSERT(0 == uv_idle_init(loop, &idle)); + ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + + /* Ensure two separate packets */ + ASSERT(0 == uv_tcp_nodelay(&client_handle, 1)); + + ASSERT(0 == uv_tcp_connect(&connect_req, + &client_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(ticks == kMaxTicks); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif diff --git a/3rdparty/libuv/test/test-tcp-open.c b/3rdparty/libuv/test/test-tcp-open.c new file mode 100644 index 00000000000..6c8d43d0009 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-open.c @@ -0,0 +1,220 @@ +/* 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 <string.h> + +#ifndef _WIN32 +# include <unistd.h> +#endif + +static int shutdown_cb_called = 0; +static int connect_cb_called = 0; +static int write_cb_called = 0; +static int close_cb_called = 0; + +static uv_connect_t connect_req; +static uv_shutdown_t shutdown_req; +static uv_write_t write_req; + + +static void startup(void) { +#ifdef _WIN32 + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); +#endif +} + + +static uv_os_sock_t create_tcp_socket(void) { + uv_os_sock_t sock; + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); +#ifdef _WIN32 + ASSERT(sock != INVALID_SOCKET); +#else + ASSERT(sock >= 0); +#endif + +#ifndef _WIN32 + { + /* Allow reuse of the port. */ + int yes = 1; + int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + ASSERT(r == 0); + } +#endif + + 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 void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &shutdown_req); + ASSERT(status == 0); + + /* Now we wait for the EOF */ + shutdown_cb_called++; +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + ASSERT(tcp != NULL); + + if (nread >= 0) { + ASSERT(nread == 4); + ASSERT(memcmp("PING", buf->base, nread) == 0); + } + else { + ASSERT(nread == UV_EOF); + printf("GOT EOF\n"); + uv_close((uv_handle_t*)tcp, close_cb); + } +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req != NULL); + + if (status) { + fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); + ASSERT(0); + } + + write_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t buf = uv_buf_init("PING", 4); + uv_stream_t* stream; + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + stream = req->handle; + connect_cb_called++; + + r = uv_write(&write_req, stream, &buf, 1, write_cb); + ASSERT(r == 0); + + /* Shutdown on drain. */ + r = uv_shutdown(&shutdown_req, stream, shutdown_cb); + ASSERT(r == 0); + + /* Start reading */ + r = uv_read_start(stream, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +TEST_IMPL(tcp_open) { + struct sockaddr_in addr; + uv_tcp_t client; + uv_os_sock_t sock; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + startup(); + sock = create_tcp_socket(); + + r = uv_tcp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_tcp_open(&client, sock); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(shutdown_cb_called == 1); + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_open_twice) { + uv_tcp_t client; + uv_os_sock_t sock1, sock2; + int r; + + startup(); + sock1 = create_tcp_socket(); + sock2 = create_tcp_socket(); + + r = uv_tcp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_tcp_open(&client, sock1); + ASSERT(r == 0); + + r = uv_tcp_open(&client, sock2); + ASSERT(r == UV_EBUSY); + close_socket(sock2); + + uv_close((uv_handle_t*) &client, NULL); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-read-stop.c b/3rdparty/libuv/test/test-tcp-read-stop.c new file mode 100644 index 00000000000..488e8fb49a9 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-read-stop.c @@ -0,0 +1,76 @@ +/* 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) { + 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; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + 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, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(); + + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-shutdown-after-write.c b/3rdparty/libuv/test/test-tcp-shutdown-after-write.c new file mode 100644 index 00000000000..463b4b0d79c --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-shutdown-after-write.c @@ -0,0 +1,138 @@ +/* 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 void write_cb(uv_write_t* req, int status); +static void shutdown_cb(uv_shutdown_t* req, int status); + +static uv_tcp_t conn; +static uv_timer_t timer; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; + +static int connect_cb_called; +static int write_cb_called; +static int shutdown_cb_called; + +static int conn_close_cb_called; +static int timer_close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + if (handle == (uv_handle_t*)&conn) + conn_close_cb_called++; + else if (handle == (uv_handle_t*)&timer) + timer_close_cb_called++; + else + ASSERT(0 && "bad handle in close_cb"); +} + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[64]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void timer_cb(uv_timer_t* handle) { + uv_buf_t buf; + int r; + + uv_close((uv_handle_t*)handle, close_cb); + + buf = uv_buf_init("TEST", 4); + r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_shutdown(&shutdown_req, (uv_stream_t*)&conn, shutdown_cb); + ASSERT(r == 0); +} + + +static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { +} + + +static void connect_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(status == 0); + connect_cb_called++; + + r = uv_read_start((uv_stream_t*)&conn, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + write_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(status == 0); + shutdown_cb_called++; + uv_close((uv_handle_t*)&conn, close_cb); +} + + +TEST_IMPL(tcp_shutdown_after_write) { + struct sockaddr_in addr; + uv_loop_t* loop; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + loop = uv_default_loop(); + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 125, 0); + ASSERT(r == 0); + + r = uv_tcp_init(loop, &conn); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &conn, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + ASSERT(conn_close_cb_called == 1); + ASSERT(timer_close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-try-write.c b/3rdparty/libuv/test/test-tcp-try-write.c new file mode 100644 index 00000000000..97a1d6e3d57 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-try-write.c @@ -0,0 +1,135 @@ +/* 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 <string.h> + +#define MAX_BYTES 1024 * 1024 + +static uv_tcp_t server; +static uv_tcp_t client; +static uv_tcp_t incoming; +static int connect_cb_called; +static int close_cb_called; +static int connection_cb_called; +static int bytes_read; +static int bytes_written; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + int r; + uv_buf_t buf; + ASSERT(status == 0); + connect_cb_called++; + + do { + buf = uv_buf_init("PING", 4); + r = uv_try_write((uv_stream_t*) &client, &buf, 1); + ASSERT(r > 0 || r == UV_EAGAIN); + if (r > 0) { + bytes_written += r; + break; + } + } while (1); + + do { + buf = uv_buf_init("", 0); + r = uv_try_write((uv_stream_t*) &client, &buf, 1); + } while (r != 0); + uv_close((uv_handle_t*) &client, close_cb); +} + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + static char base[1024]; + + buf->base = base; + buf->len = sizeof(base); +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + if (nread < 0) { + uv_close((uv_handle_t*) tcp, close_cb); + uv_close((uv_handle_t*) &server, close_cb); + return; + } + + bytes_read += nread; +} + + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT(status == 0); + + ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); + ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); + + connection_cb_called++; + ASSERT(0 == uv_read_start((uv_stream_t*) &incoming, alloc_cb, read_cb)); +} + + +static void start_server(void) { + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); + ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); +} + + +TEST_IMPL(tcp_try_write) { + uv_connect_t connect_req; + struct sockaddr_in addr; + + start_server(); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); + ASSERT(0 == uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(connect_cb_called == 1); + ASSERT(close_cb_called == 3); + ASSERT(connection_cb_called == 1); + ASSERT(bytes_read == bytes_written); + ASSERT(bytes_written > 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-unexpected-read.c b/3rdparty/libuv/test/test-tcp-unexpected-read.c new file mode 100644 index 00000000000..c7b981456be --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-unexpected-read.c @@ -0,0 +1,117 @@ +/* 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_check_t check_handle; +static uv_timer_t timer_handle; +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; +static uv_tcp_t peer_handle; +static uv_write_t write_req; +static uv_connect_t connect_req; + +static unsigned long ticks; /* event loop ticks */ + + +static void check_cb(uv_check_t* handle) { + ticks++; +} + + +static void timer_cb(uv_timer_t* handle) { + uv_close((uv_handle_t*) &check_handle, NULL); + uv_close((uv_handle_t*) &timer_handle, NULL); + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); + uv_close((uv_handle_t*) &peer_handle, NULL); +} + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + ASSERT(0 && "alloc_cb should not have been called"); +} + + +static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { + ASSERT(0 && "read_cb should not have been called"); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &client_handle); + ASSERT(0 == status); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &peer_handle); + ASSERT(0 == status); +} + + +static void connection_cb(uv_stream_t* handle, int status) { + uv_buf_t buf; + + buf = uv_buf_init("PING", 4); + + ASSERT(0 == status); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &peer_handle, + &buf, 1, write_cb)); +} + + +TEST_IMPL(tcp_unexpected_read) { + struct sockaddr_in addr; + uv_loop_t* loop; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + loop = uv_default_loop(); + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1000, 0)); + ASSERT(0 == uv_check_init(loop, &check_handle)); + ASSERT(0 == uv_check_start(&check_handle, check_cb)); + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_init(loop, &peer_handle)); + ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + ASSERT(0 == uv_tcp_connect(&connect_req, + &client_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + /* This is somewhat inexact but the idea is that the event loop should not + * start busy looping when the server sends a message and the client isn't + * reading. + */ + ASSERT(ticks <= 20); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-write-after-connect.c b/3rdparty/libuv/test/test-tcp-write-after-connect.c new file mode 100644 index 00000000000..aa03228f134 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-write-after-connect.c @@ -0,0 +1,68 @@ +/* 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. + */ + +#ifndef _WIN32 + +#include "uv.h" +#include "task.h" + +uv_loop_t loop; +uv_tcp_t tcp_client; +uv_connect_t connection_request; +uv_write_t write_request; +uv_buf_t buf = { "HELLO", 4 }; + + +static void write_cb(uv_write_t *req, int status) { + ASSERT(status == UV_ECANCELED); + uv_close((uv_handle_t*) req->handle, NULL); +} + + +static void connect_cb(uv_connect_t *req, int status) { + ASSERT(status == UV_ECONNREFUSED); +} + + +TEST_IMPL(tcp_write_after_connect) { + struct sockaddr_in sa; + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); + ASSERT(0 == uv_loop_init(&loop)); + ASSERT(0 == uv_tcp_init(&loop, &tcp_client)); + + ASSERT(0 == uv_tcp_connect(&connection_request, + &tcp_client, + (const struct sockaddr *) + &sa, + connect_cb)); + + ASSERT(0 == uv_write(&write_request, + (uv_stream_t *)&tcp_client, + &buf, 1, + write_cb)); + + uv_run(&loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif diff --git a/3rdparty/libuv/test/test-tcp-write-fail.c b/3rdparty/libuv/test/test-tcp-write-fail.c new file mode 100644 index 00000000000..5256a9f4a79 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-write-fail.c @@ -0,0 +1,115 @@ +/* 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> +#ifndef _WIN32 +# include <unistd.h> +#endif + + +static int connect_cb_called = 0; +static int write_cb_called = 0; +static int close_cb_called = 0; + +static uv_connect_t connect_req; +static uv_write_t write_req; + + +static void close_socket(uv_tcp_t* sock) { + uv_os_fd_t fd; + int r; + + r = uv_fileno((uv_handle_t*)sock, &fd); + ASSERT(r == 0); +#ifdef _WIN32 + r = closesocket((uv_os_sock_t)fd); +#else + r = close(fd); +#endif + ASSERT(r == 0); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req != NULL); + + ASSERT(status != 0); + fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); + write_cb_called++; + + uv_close((uv_handle_t*)(req->handle), close_cb); +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t buf; + uv_stream_t* stream; + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + stream = req->handle; + connect_cb_called++; + + /* close the socket, the hard way */ + close_socket((uv_tcp_t*)stream); + + buf = uv_buf_init("hello\n", 6); + r = uv_write(&write_req, stream, &buf, 1, write_cb); + ASSERT(r == 0); +} + + +TEST_IMPL(tcp_write_fail) { + struct sockaddr_in addr; + uv_tcp_t client; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-write-queue-order.c b/3rdparty/libuv/test/test-tcp-write-queue-order.c new file mode 100644 index 00000000000..aa4d2acc24a --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-write-queue-order.c @@ -0,0 +1,137 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "uv.h" +#include "task.h" + +#define REQ_COUNT 10000 + +static uv_timer_t timer; +static uv_tcp_t server; +static uv_tcp_t client; +static uv_tcp_t incoming; +static int connect_cb_called; +static int close_cb_called; +static int connection_cb_called; +static int write_callbacks; +static int write_cancelled_callbacks; +static int write_error_callbacks; + +static uv_write_t write_requests[REQ_COUNT]; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +void timer_cb(uv_timer_t* handle) { + uv_close((uv_handle_t*) &client, close_cb); + uv_close((uv_handle_t*) &server, close_cb); + uv_close((uv_handle_t*) &incoming, close_cb); +} + +void write_cb(uv_write_t* req, int status) { + if (status == 0) + write_callbacks++; + else if (status == UV_ECANCELED) + write_cancelled_callbacks++; + else + write_error_callbacks++; +} + +static void connect_cb(uv_connect_t* req, int status) { + static char base[1024]; + int r; + int i; + uv_buf_t buf; + + ASSERT(status == 0); + connect_cb_called++; + + buf = uv_buf_init(base, sizeof(base)); + + for (i = 0; i < REQ_COUNT; i++) { + r = uv_write(&write_requests[i], + req->handle, + &buf, + 1, + write_cb); + ASSERT(r == 0); + } +} + + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT(status == 0); + + ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); + ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); + + connection_cb_called++; +} + + +static void start_server(void) { + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); + ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); +} + + +TEST_IMPL(tcp_write_queue_order) { + uv_connect_t connect_req; + struct sockaddr_in addr; + + start_server(); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); + ASSERT(0 == uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); + + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_cb, 100, 0)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(connect_cb_called == 1); + ASSERT(connection_cb_called == 1); + ASSERT(write_callbacks > 0); + ASSERT(write_cancelled_callbacks > 0); + ASSERT(write_callbacks + + write_error_callbacks + + write_cancelled_callbacks == REQ_COUNT); + ASSERT(close_cb_called == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-write-to-half-open-connection.c b/3rdparty/libuv/test/test-tcp-write-to-half-open-connection.c new file mode 100644 index 00000000000..2fa2ae72253 --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-write-to-half-open-connection.c @@ -0,0 +1,141 @@ +/* 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 <string.h> + +static void connection_cb(uv_stream_t* server, int status); +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); +static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); + +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_client; +static uv_tcp_t tcp_peer; /* client socket as accept()-ed by server */ +static uv_connect_t connect_req; +static uv_write_t write_req; + +static int write_cb_called; +static int read_cb_called; + +static void connection_cb(uv_stream_t* server, int status) { + int r; + uv_buf_t buf; + + ASSERT(server == (uv_stream_t*)&tcp_server); + ASSERT(status == 0); + + r = uv_tcp_init(server->loop, &tcp_peer); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)&tcp_peer); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&tcp_peer, alloc_cb, read_cb); + ASSERT(r == 0); + + buf.base = "hello\n"; + buf.len = 6; + + r = uv_write(&write_req, (uv_stream_t*)&tcp_peer, &buf, 1, write_cb); + ASSERT(r == 0); +} + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[1024]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + if (nread < 0) { + fprintf(stderr, "read_cb error: %s\n", uv_err_name(nread)); + ASSERT(nread == UV_ECONNRESET || nread == UV_EOF); + + uv_close((uv_handle_t*)&tcp_server, NULL); + uv_close((uv_handle_t*)&tcp_peer, NULL); + } + + read_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req == &connect_req); + ASSERT(status == 0); + + /* Close the client. */ + uv_close((uv_handle_t*)&tcp_client, NULL); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + write_cb_called++; +} + + +TEST_IMPL(tcp_write_to_half_open_connection) { + struct sockaddr_in addr; + uv_loop_t* loop; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + r = uv_tcp_init(loop, &tcp_server); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, 1, connection_cb); + ASSERT(r == 0); + + r = uv_tcp_init(loop, &tcp_client); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(write_cb_called > 0); + ASSERT(read_cb_called > 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tcp-writealot.c b/3rdparty/libuv/test/test-tcp-writealot.c new file mode 100644 index 00000000000..6cfe2ebb18d --- /dev/null +++ b/3rdparty/libuv/test/test-tcp-writealot.c @@ -0,0 +1,176 @@ +/* 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> + + +#define WRITES 3 +#define CHUNKS_PER_WRITE 4096 +#define CHUNK_SIZE 10024 /* 10 kb */ + +#define TOTAL_BYTES (WRITES * CHUNKS_PER_WRITE * CHUNK_SIZE) + +static char* send_buffer; + +static int shutdown_cb_called = 0; +static int connect_cb_called = 0; +static int write_cb_called = 0; +static int close_cb_called = 0; +static size_t bytes_sent = 0; +static size_t bytes_sent_done = 0; +static size_t bytes_received_done = 0; + +static uv_connect_t connect_req; +static uv_shutdown_t shutdown_req; +static uv_write_t write_reqs[WRITES]; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + uv_tcp_t* tcp; + + ASSERT(req == &shutdown_req); + ASSERT(status == 0); + + tcp = (uv_tcp_t*)(req->handle); + + /* The write buffer should be empty by now. */ + ASSERT(tcp->write_queue_size == 0); + + /* Now we wait for the EOF */ + shutdown_cb_called++; + + /* We should have had all the writes called already. */ + ASSERT(write_cb_called == WRITES); +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { + ASSERT(tcp != NULL); + + if (nread >= 0) { + bytes_received_done += nread; + } + else { + ASSERT(nread == UV_EOF); + printf("GOT EOF\n"); + uv_close((uv_handle_t*)tcp, close_cb); + } + + free(buf->base); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req != NULL); + + if (status) { + fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); + ASSERT(0); + } + + bytes_sent_done += CHUNKS_PER_WRITE * CHUNK_SIZE; + write_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t send_bufs[CHUNKS_PER_WRITE]; + uv_stream_t* stream; + int i, j, r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + stream = req->handle; + connect_cb_called++; + + /* Write a lot of data */ + for (i = 0; i < WRITES; i++) { + uv_write_t* write_req = write_reqs + i; + + for (j = 0; j < CHUNKS_PER_WRITE; j++) { + send_bufs[j] = uv_buf_init(send_buffer + bytes_sent, CHUNK_SIZE); + bytes_sent += CHUNK_SIZE; + } + + r = uv_write(write_req, stream, send_bufs, CHUNKS_PER_WRITE, write_cb); + ASSERT(r == 0); + } + + /* Shutdown on drain. */ + r = uv_shutdown(&shutdown_req, stream, shutdown_cb); + ASSERT(r == 0); + + /* Start reading */ + r = uv_read_start(stream, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +TEST_IMPL(tcp_writealot) { + struct sockaddr_in addr; + uv_tcp_t client; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + send_buffer = calloc(1, TOTAL_BYTES); + ASSERT(send_buffer != NULL); + + r = uv_tcp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(shutdown_cb_called == 1); + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == WRITES); + ASSERT(close_cb_called == 1); + ASSERT(bytes_sent == TOTAL_BYTES); + ASSERT(bytes_sent_done == TOTAL_BYTES); + ASSERT(bytes_received_done == TOTAL_BYTES); + + free(send_buffer); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-thread-equal.c b/3rdparty/libuv/test/test-thread-equal.c new file mode 100644 index 00000000000..27c07ee2c7d --- /dev/null +++ b/3rdparty/libuv/test/test-thread-equal.c @@ -0,0 +1,45 @@ +/* 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" + +uv_thread_t main_thread_id; +uv_thread_t subthreads[2]; + +static void check_thread(void* arg) { + uv_thread_t *thread_id = arg; + uv_thread_t self_id = uv_thread_self(); + ASSERT(uv_thread_equal(&main_thread_id, &self_id) == 0); + *thread_id = uv_thread_self(); +} + +TEST_IMPL(thread_equal) { + uv_thread_t threads[2]; + main_thread_id = uv_thread_self(); + ASSERT(0 != uv_thread_equal(&main_thread_id, &main_thread_id)); + ASSERT(0 == uv_thread_create(threads + 0, check_thread, subthreads + 0)); + ASSERT(0 == uv_thread_create(threads + 1, check_thread, subthreads + 1)); + ASSERT(0 == uv_thread_join(threads + 0)); + ASSERT(0 == uv_thread_join(threads + 1)); + ASSERT(0 == uv_thread_equal(subthreads + 0, subthreads + 1)); + return 0; +} diff --git a/3rdparty/libuv/test/test-thread.c b/3rdparty/libuv/test/test-thread.c new file mode 100644 index 00000000000..7f3321aa06d --- /dev/null +++ b/3rdparty/libuv/test/test-thread.c @@ -0,0 +1,211 @@ +/* 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 <string.h> /* memset */ + +struct getaddrinfo_req { + uv_thread_t thread_id; + unsigned int counter; + uv_loop_t* loop; + uv_getaddrinfo_t handle; +}; + + +struct fs_req { + uv_thread_t thread_id; + unsigned int counter; + uv_loop_t* loop; + uv_fs_t handle; +}; + + +struct test_thread { + uv_thread_t thread_id; + volatile int thread_called; +}; + +static void getaddrinfo_do(struct getaddrinfo_req* req); +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res); +static void fs_do(struct fs_req* req); +static void fs_cb(uv_fs_t* handle); + +static volatile int thread_called; +static uv_key_t tls_key; + + +static void getaddrinfo_do(struct getaddrinfo_req* req) { + int r; + + r = uv_getaddrinfo(req->loop, + &req->handle, + getaddrinfo_cb, + "localhost", + NULL, + NULL); + ASSERT(r == 0); +} + + +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res) { + struct getaddrinfo_req* req; + + ASSERT(status == 0); + + req = container_of(handle, struct getaddrinfo_req, handle); + uv_freeaddrinfo(res); + + if (--req->counter) + getaddrinfo_do(req); +} + + +static void fs_do(struct fs_req* req) { + int r; + + r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb); + ASSERT(r == 0); +} + + +static void fs_cb(uv_fs_t* handle) { + struct fs_req* req = container_of(handle, struct fs_req, handle); + + uv_fs_req_cleanup(handle); + + if (--req->counter) + fs_do(req); +} + + +static void do_work(void* arg) { + struct getaddrinfo_req getaddrinfo_reqs[16]; + struct fs_req fs_reqs[16]; + uv_loop_t* loop; + size_t i; + int r; + struct test_thread* thread = arg; + + loop = malloc(sizeof *loop); + ASSERT(loop != NULL); + ASSERT(0 == uv_loop_init(loop)); + + for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) { + struct getaddrinfo_req* req = getaddrinfo_reqs + i; + req->counter = 16; + req->loop = loop; + getaddrinfo_do(req); + } + + for (i = 0; i < ARRAY_SIZE(fs_reqs); i++) { + struct fs_req* req = fs_reqs + i; + req->counter = 16; + req->loop = loop; + fs_do(req); + } + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(0 == uv_loop_close(loop)); + free(loop); + thread->thread_called = 1; +} + + +static void thread_entry(void* arg) { + ASSERT(arg == (void *) 42); + thread_called++; +} + + +TEST_IMPL(thread_create) { + uv_thread_t tid; + int r; + + r = uv_thread_create(&tid, thread_entry, (void *) 42); + ASSERT(r == 0); + + r = uv_thread_join(&tid); + ASSERT(r == 0); + + ASSERT(thread_called == 1); + + return 0; +} + + +/* Hilariously bad test name. Run a lot of tasks in the thread pool and verify + * that each "finished" callback is run in its originating thread. + */ +TEST_IMPL(threadpool_multiple_event_loops) { + struct test_thread threads[8]; + size_t i; + int r; + + memset(threads, 0, sizeof(threads)); + + for (i = 0; i < ARRAY_SIZE(threads); i++) { + r = uv_thread_create(&threads[i].thread_id, do_work, &threads[i]); + ASSERT(r == 0); + } + + for (i = 0; i < ARRAY_SIZE(threads); i++) { + r = uv_thread_join(&threads[i].thread_id); + ASSERT(r == 0); + ASSERT(threads[i].thread_called); + } + + return 0; +} + + +static void tls_thread(void* arg) { + ASSERT(NULL == uv_key_get(&tls_key)); + uv_key_set(&tls_key, arg); + ASSERT(arg == uv_key_get(&tls_key)); + uv_key_set(&tls_key, NULL); + ASSERT(NULL == uv_key_get(&tls_key)); +} + + +TEST_IMPL(thread_local_storage) { + char name[] = "main"; + uv_thread_t threads[2]; + ASSERT(0 == uv_key_create(&tls_key)); + ASSERT(NULL == uv_key_get(&tls_key)); + uv_key_set(&tls_key, name); + ASSERT(name == uv_key_get(&tls_key)); + ASSERT(0 == uv_thread_create(threads + 0, tls_thread, threads + 0)); + ASSERT(0 == uv_thread_create(threads + 1, tls_thread, threads + 1)); + ASSERT(0 == uv_thread_join(threads + 0)); + ASSERT(0 == uv_thread_join(threads + 1)); + uv_key_delete(&tls_key); + return 0; +} diff --git a/3rdparty/libuv/test/test-threadpool-cancel.c b/3rdparty/libuv/test/test-threadpool-cancel.c new file mode 100644 index 00000000000..784c1739f6d --- /dev/null +++ b/3rdparty/libuv/test/test-threadpool-cancel.c @@ -0,0 +1,362 @@ +/* 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 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, + (uint64_t) (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->result == UV_ECANCELED); + uv_fs_req_cleanup(req); + fs_cb_called++; +} + + +static void getaddrinfo_cb(uv_getaddrinfo_t* req, + int status, + struct addrinfo* res) { + ASSERT(status == UV_EAI_CANCELED); + ASSERT(res == NULL); + uv_freeaddrinfo(res); /* Should not crash. */ +} + + +static void getnameinfo_cb(uv_getnameinfo_t* handle, + int status, + const char* hostname, + const char* service) { + ASSERT(status == UV_EAI_CANCELED); + ASSERT(hostname == NULL); + ASSERT(service == NULL); +} + + +static void work2_cb(uv_work_t* req) { + ASSERT(0 && "work2_cb called"); +} + + +static void done2_cb(uv_work_t* req, int status) { + ASSERT(status == UV_ECANCELED); + done2_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + 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++; +} + + +static void nop_work_cb(uv_work_t* req) { +} + + +static void nop_done_cb(uv_work_t* req, int status) { + req->data = "OK"; +} + + +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, UV_RUN_DEFAULT)); + ASSERT(1 == timer_cb_called); + + cleanup_threadpool(); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(threadpool_cancel_getnameinfo) { + uv_getnameinfo_t reqs[4]; + struct sockaddr_in addr4; + struct cancel_info ci; + uv_loop_t* loop; + int r; + + r = uv_ip4_addr("127.0.0.1", 80, &addr4); + ASSERT(r == 0); + + INIT_CANCEL_INFO(&ci, reqs); + loop = uv_default_loop(); + saturate_threadpool(); + + r = uv_getnameinfo(loop, reqs + 0, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); + ASSERT(r == 0); + + r = uv_getnameinfo(loop, reqs + 1, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); + ASSERT(r == 0); + + r = uv_getnameinfo(loop, reqs + 2, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); + ASSERT(r == 0); + + r = uv_getnameinfo(loop, reqs + 3, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); + 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, UV_RUN_DEFAULT)); + ASSERT(1 == timer_cb_called); + + cleanup_threadpool(); + + MAKE_VALGRIND_HAPPY(); + 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, UV_RUN_DEFAULT)); + ASSERT(1 == timer_cb_called); + ASSERT(ARRAY_SIZE(reqs) == done2_cb_called); + + cleanup_threadpool(); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(threadpool_cancel_fs) { + struct cancel_info ci; + uv_fs_t reqs[26]; + uv_loop_t* loop; + unsigned n; + uv_buf_t iov; + + INIT_CANCEL_INFO(&ci, reqs); + loop = uv_default_loop(); + saturate_threadpool(); + iov = uv_buf_init(NULL, 0); + + /* 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, &iov, 1, 0, fs_cb)); + ASSERT(0 == uv_fs_scandir(loop, reqs + n++, "/", 0, fs_cb)); + ASSERT(0 == uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); + ASSERT(0 == uv_fs_realpath(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, &iov, 1, 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, UV_RUN_DEFAULT)); + ASSERT(n == fs_cb_called); + ASSERT(1 == timer_cb_called); + + cleanup_threadpool(); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(threadpool_cancel_single) { + uv_loop_t* loop; + uv_work_t req; + int cancelled; + int i; + + loop = uv_default_loop(); + for (i = 0; i < 5000; i++) { + req.data = NULL; + ASSERT(0 == uv_queue_work(loop, &req, nop_work_cb, nop_done_cb)); + + cancelled = uv_cancel((uv_req_t*) &req); + if (cancelled == 0) + break; + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + } + + if (cancelled != 0) { + fputs("Failed to cancel a work req in 5,000 iterations, giving up.\n", + stderr); + return 1; + } + + ASSERT(req.data == NULL); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(req.data != NULL); /* Should have been updated by nop_done_cb(). */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-threadpool.c b/3rdparty/libuv/test/test-threadpool.c new file mode 100644 index 00000000000..e3d17d7546f --- /dev/null +++ b/3rdparty/libuv/test/test-threadpool.c @@ -0,0 +1,76 @@ +/* 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 int work_cb_count; +static int after_work_cb_count; +static uv_work_t work_req; +static char data; + + +static void work_cb(uv_work_t* req) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + work_cb_count++; +} + + +static void after_work_cb(uv_work_t* req, int status) { + ASSERT(status == 0); + ASSERT(req == &work_req); + ASSERT(req->data == &data); + after_work_cb_count++; +} + + +TEST_IMPL(threadpool_queue_work_simple) { + int r; + + work_req.data = &data; + r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); + ASSERT(r == 0); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(work_cb_count == 1); + ASSERT(after_work_cb_count == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(threadpool_queue_work_einval) { + int r; + + work_req.data = &data; + r = uv_queue_work(uv_default_loop(), &work_req, NULL, after_work_cb); + ASSERT(r == UV_EINVAL); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(work_cb_count == 0); + ASSERT(after_work_cb_count == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-timer-again.c b/3rdparty/libuv/test/test-timer-again.c new file mode 100644 index 00000000000..f93c509be5d --- /dev/null +++ b/3rdparty/libuv/test/test-timer-again.c @@ -0,0 +1,141 @@ +/* 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 int close_cb_called = 0; +static int repeat_1_cb_called = 0; +static int repeat_2_cb_called = 0; + +static int repeat_2_cb_allowed = 0; + +static uv_timer_t dummy, repeat_1, repeat_2; + +static uint64_t start_time; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + + close_cb_called++; +} + + +static void repeat_1_cb(uv_timer_t* handle) { + int r; + + ASSERT(handle == &repeat_1); + ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50); + + fprintf(stderr, "repeat_1_cb called after %ld ms\n", + (long int)(uv_now(uv_default_loop()) - start_time)); + fflush(stderr); + + repeat_1_cb_called++; + + r = uv_timer_again(&repeat_2); + ASSERT(r == 0); + + if (repeat_1_cb_called == 10) { + uv_close((uv_handle_t*)handle, close_cb); + /* We're not calling uv_timer_again on repeat_2 any more, so after this */ + /* timer_2_cb is expected. */ + repeat_2_cb_allowed = 1; + return; + } +} + + +static void repeat_2_cb(uv_timer_t* handle) { + ASSERT(handle == &repeat_2); + ASSERT(repeat_2_cb_allowed); + + fprintf(stderr, "repeat_2_cb called after %ld ms\n", + (long int)(uv_now(uv_default_loop()) - start_time)); + fflush(stderr); + + repeat_2_cb_called++; + + if (uv_timer_get_repeat(&repeat_2) == 0) { + ASSERT(0 == uv_is_active((uv_handle_t*) handle)); + uv_close((uv_handle_t*)handle, close_cb); + return; + } + + fprintf(stderr, "uv_timer_get_repeat %ld ms\n", + (long int)uv_timer_get_repeat(&repeat_2)); + fflush(stderr); + ASSERT(uv_timer_get_repeat(&repeat_2) == 100); + + /* This shouldn't take effect immediately. */ + uv_timer_set_repeat(&repeat_2, 0); +} + + +TEST_IMPL(timer_again) { + int r; + + start_time = uv_now(uv_default_loop()); + ASSERT(0 < start_time); + + /* Verify that it is not possible to uv_timer_again a never-started timer. */ + r = uv_timer_init(uv_default_loop(), &dummy); + ASSERT(r == 0); + r = uv_timer_again(&dummy); + ASSERT(r == UV_EINVAL); + uv_unref((uv_handle_t*)&dummy); + + /* Start timer repeat_1. */ + r = uv_timer_init(uv_default_loop(), &repeat_1); + ASSERT(r == 0); + r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0); + ASSERT(r == 0); + ASSERT(uv_timer_get_repeat(&repeat_1) == 0); + + /* Actually make repeat_1 repeating. */ + uv_timer_set_repeat(&repeat_1, 50); + ASSERT(uv_timer_get_repeat(&repeat_1) == 50); + + /* + * Start another repeating timer. It'll be again()ed by the repeat_1 so + * it should not time out until repeat_1 stops. + */ + r = uv_timer_init(uv_default_loop(), &repeat_2); + ASSERT(r == 0); + r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100); + ASSERT(r == 0); + ASSERT(uv_timer_get_repeat(&repeat_2) == 100); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(repeat_1_cb_called == 10); + ASSERT(repeat_2_cb_called == 2); + ASSERT(close_cb_called == 2); + + fprintf(stderr, "Test took %ld ms (expected ~700 ms)\n", + (long int)(uv_now(uv_default_loop()) - start_time)); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-timer-from-check.c b/3rdparty/libuv/test/test-timer-from-check.c new file mode 100644 index 00000000000..a18c7e1fb99 --- /dev/null +++ b/3rdparty/libuv/test/test-timer-from-check.c @@ -0,0 +1,80 @@ +/* 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_prepare_t prepare_handle; +static uv_check_t check_handle; +static uv_timer_t timer_handle; + +static int prepare_cb_called; +static int check_cb_called; +static int timer_cb_called; + + +static void prepare_cb(uv_prepare_t* handle) { + ASSERT(0 == uv_prepare_stop(&prepare_handle)); + ASSERT(0 == prepare_cb_called); + ASSERT(1 == check_cb_called); + ASSERT(0 == timer_cb_called); + prepare_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(0 == uv_timer_stop(&timer_handle)); + ASSERT(1 == prepare_cb_called); + ASSERT(1 == check_cb_called); + ASSERT(0 == timer_cb_called); + timer_cb_called++; +} + + +static void check_cb(uv_check_t* handle) { + ASSERT(0 == uv_check_stop(&check_handle)); + ASSERT(0 == uv_timer_stop(&timer_handle)); /* Runs before timer_cb. */ + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); + ASSERT(0 == uv_prepare_start(&prepare_handle, prepare_cb)); + ASSERT(0 == prepare_cb_called); + ASSERT(0 == check_cb_called); + ASSERT(0 == timer_cb_called); + check_cb_called++; +} + + +TEST_IMPL(timer_from_check) { + ASSERT(0 == uv_prepare_init(uv_default_loop(), &prepare_handle)); + ASSERT(0 == uv_check_init(uv_default_loop(), &check_handle)); + ASSERT(0 == uv_check_start(&check_handle, check_cb)); + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT(1 == prepare_cb_called); + ASSERT(1 == check_cb_called); + ASSERT(1 == timer_cb_called); + uv_close((uv_handle_t*) &prepare_handle, NULL); + uv_close((uv_handle_t*) &check_handle, NULL); + uv_close((uv_handle_t*) &timer_handle, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-timer.c b/3rdparty/libuv/test/test-timer.c new file mode 100644 index 00000000000..aba050fd64c --- /dev/null +++ b/3rdparty/libuv/test/test-timer.c @@ -0,0 +1,303 @@ +/* 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 int once_cb_called = 0; +static int once_close_cb_called = 0; +static int repeat_cb_called = 0; +static int repeat_close_cb_called = 0; +static int order_cb_called = 0; +static uint64_t start_time; +static uv_timer_t tiny_timer; +static uv_timer_t huge_timer1; +static uv_timer_t huge_timer2; + + +static void once_close_cb(uv_handle_t* handle) { + printf("ONCE_CLOSE_CB\n"); + + ASSERT(handle != NULL); + ASSERT(0 == uv_is_active(handle)); + + once_close_cb_called++; +} + + +static void once_cb(uv_timer_t* handle) { + printf("ONCE_CB %d\n", once_cb_called); + + ASSERT(handle != NULL); + ASSERT(0 == uv_is_active((uv_handle_t*) handle)); + + once_cb_called++; + + uv_close((uv_handle_t*)handle, once_close_cb); + + /* Just call this randomly for the code coverage. */ + uv_update_time(uv_default_loop()); +} + + +static void repeat_close_cb(uv_handle_t* handle) { + printf("REPEAT_CLOSE_CB\n"); + + ASSERT(handle != NULL); + + repeat_close_cb_called++; +} + + +static void repeat_cb(uv_timer_t* handle) { + printf("REPEAT_CB\n"); + + ASSERT(handle != NULL); + ASSERT(1 == uv_is_active((uv_handle_t*) handle)); + + repeat_cb_called++; + + if (repeat_cb_called == 5) { + uv_close((uv_handle_t*)handle, repeat_close_cb); + } +} + + +static void never_cb(uv_timer_t* handle) { + FATAL("never_cb should never be called"); +} + + +TEST_IMPL(timer) { + uv_timer_t once_timers[10]; + uv_timer_t *once; + uv_timer_t repeat, never; + unsigned int i; + int r; + + start_time = uv_now(uv_default_loop()); + ASSERT(0 < start_time); + + /* Let 10 timers time out in 500 ms total. */ + for (i = 0; i < ARRAY_SIZE(once_timers); i++) { + once = once_timers + i; + r = uv_timer_init(uv_default_loop(), once); + ASSERT(r == 0); + r = uv_timer_start(once, once_cb, i * 50, 0); + ASSERT(r == 0); + } + + /* The 11th timer is a repeating timer that runs 4 times */ + r = uv_timer_init(uv_default_loop(), &repeat); + ASSERT(r == 0); + r = uv_timer_start(&repeat, repeat_cb, 100, 100); + ASSERT(r == 0); + + /* The 12th timer should not do anything. */ + r = uv_timer_init(uv_default_loop(), &never); + ASSERT(r == 0); + r = uv_timer_start(&never, never_cb, 100, 100); + ASSERT(r == 0); + r = uv_timer_stop(&never); + ASSERT(r == 0); + uv_unref((uv_handle_t*)&never); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(once_cb_called == 10); + ASSERT(once_close_cb_called == 10); + printf("repeat_cb_called %d\n", repeat_cb_called); + ASSERT(repeat_cb_called == 5); + ASSERT(repeat_close_cb_called == 1); + + ASSERT(500 <= uv_now(uv_default_loop()) - start_time); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(timer_start_twice) { + uv_timer_t once; + int r; + + r = uv_timer_init(uv_default_loop(), &once); + ASSERT(r == 0); + r = uv_timer_start(&once, never_cb, 86400 * 1000, 0); + ASSERT(r == 0); + r = uv_timer_start(&once, once_cb, 10, 0); + ASSERT(r == 0); + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(once_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(timer_init) { + uv_timer_t handle; + + ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); + ASSERT(0 == uv_timer_get_repeat(&handle)); + ASSERT(0 == uv_is_active((uv_handle_t*) &handle)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void order_cb_a(uv_timer_t *handle) { + ASSERT(order_cb_called++ == *(int*)handle->data); +} + + +static void order_cb_b(uv_timer_t *handle) { + ASSERT(order_cb_called++ == *(int*)handle->data); +} + + +TEST_IMPL(timer_order) { + int first; + int second; + uv_timer_t handle_a; + uv_timer_t handle_b; + + first = 0; + second = 1; + ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_a)); + ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_b)); + + /* Test for starting handle_a then handle_b */ + handle_a.data = &first; + ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0)); + handle_b.data = &second; + ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(order_cb_called == 2); + + ASSERT(0 == uv_timer_stop(&handle_a)); + ASSERT(0 == uv_timer_stop(&handle_b)); + + /* Test for starting handle_b then handle_a */ + order_cb_called = 0; + handle_b.data = &first; + ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0)); + + handle_a.data = &second; + ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(order_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void tiny_timer_cb(uv_timer_t* handle) { + ASSERT(handle == &tiny_timer); + uv_close((uv_handle_t*) &tiny_timer, NULL); + uv_close((uv_handle_t*) &huge_timer1, NULL); + uv_close((uv_handle_t*) &huge_timer2, NULL); +} + + +TEST_IMPL(timer_huge_timeout) { + ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer)); + ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1)); + ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer2)); + ASSERT(0 == uv_timer_start(&tiny_timer, tiny_timer_cb, 1, 0)); + ASSERT(0 == uv_timer_start(&huge_timer1, tiny_timer_cb, 0xffffffffffffLL, 0)); + ASSERT(0 == uv_timer_start(&huge_timer2, tiny_timer_cb, (uint64_t) -1, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void huge_repeat_cb(uv_timer_t* handle) { + static int ncalls; + + if (ncalls == 0) + ASSERT(handle == &huge_timer1); + else + ASSERT(handle == &tiny_timer); + + if (++ncalls == 10) { + uv_close((uv_handle_t*) &tiny_timer, NULL); + uv_close((uv_handle_t*) &huge_timer1, NULL); + } +} + + +TEST_IMPL(timer_huge_repeat) { + ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer)); + ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1)); + ASSERT(0 == uv_timer_start(&tiny_timer, huge_repeat_cb, 2, 2)); + ASSERT(0 == uv_timer_start(&huge_timer1, huge_repeat_cb, 1, (uint64_t) -1)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static unsigned int timer_run_once_timer_cb_called; + + +static void timer_run_once_timer_cb(uv_timer_t* handle) { + timer_run_once_timer_cb_called++; +} + + +TEST_IMPL(timer_run_once) { + uv_timer_t timer_handle; + + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 0, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT(1 == timer_run_once_timer_cb_called); + + ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 1, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT(2 == timer_run_once_timer_cb_called); + + uv_close((uv_handle_t*) &timer_handle, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(timer_null_callback) { + uv_timer_t handle; + + ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); + ASSERT(UV_EINVAL == uv_timer_start(&handle, NULL, 100, 100)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-tty.c b/3rdparty/libuv/test/test-tty.c new file mode 100644 index 00000000000..b844959d526 --- /dev/null +++ b/3rdparty/libuv/test/test-tty.c @@ -0,0 +1,184 @@ +/* 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" + +#ifdef _WIN32 +# include <io.h> +# include <windows.h> +#else /* Unix */ +# include <fcntl.h> +# include <unistd.h> +#endif + +#include <string.h> +#include <errno.h> + + +TEST_IMPL(tty) { + int r, width, height; + int ttyin_fd, ttyout_fd; + uv_tty_t tty_in, tty_out; + uv_loop_t* loop = uv_default_loop(); + + /* Make sure we have an FD that refers to a tty */ +#ifdef _WIN32 + HANDLE handle; + handle = CreateFileA("conin$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + ASSERT(handle != INVALID_HANDLE_VALUE); + ttyin_fd = _open_osfhandle((intptr_t) handle, 0); + + handle = CreateFileA("conout$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + ASSERT(handle != INVALID_HANDLE_VALUE); + ttyout_fd = _open_osfhandle((intptr_t) handle, 0); + +#else /* unix */ + ttyin_fd = open("/dev/tty", O_RDONLY, 0); + if (ttyin_fd < 0) { + fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); + fflush(stderr); + return TEST_SKIP; + } + + ttyout_fd = open("/dev/tty", O_WRONLY, 0); + if (ttyout_fd < 0) { + fprintf(stderr, "Cannot open /dev/tty as write-only: %s\n", strerror(errno)); + fflush(stderr); + return TEST_SKIP; + } +#endif + + ASSERT(ttyin_fd >= 0); + ASSERT(ttyout_fd >= 0); + + ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1)); + + ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); + + r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ + ASSERT(r == 0); + + r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ + ASSERT(r == 0); + + r = uv_tty_get_winsize(&tty_out, &width, &height); + ASSERT(r == 0); + + printf("width=%d height=%d\n", width, height); + + if (width == 0 && height == 0) { + /* Some environments such as containers or Jenkins behave like this + * sometimes */ + MAKE_VALGRIND_HAPPY(); + return TEST_SKIP; + } + + /* + * Is it a safe assumption that most people have terminals larger than + * 10x10? + */ + ASSERT(width > 10); + ASSERT(height > 10); + + /* Turn on raw mode. */ + r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); + ASSERT(r == 0); + + /* Turn off raw mode. */ + r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_NORMAL); + ASSERT(r == 0); + + /* Calling uv_tty_reset_mode() repeatedly should not clobber errno. */ + errno = 0; + ASSERT(0 == uv_tty_reset_mode()); + ASSERT(0 == uv_tty_reset_mode()); + ASSERT(0 == uv_tty_reset_mode()); + ASSERT(0 == errno); + + /* TODO check the actual mode! */ + + uv_close((uv_handle_t*) &tty_in, NULL); + uv_close((uv_handle_t*) &tty_out, NULL); + + uv_run(loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tty_file) { +#ifndef _WIN32 + uv_loop_t loop; + uv_tty_t tty; + int fd; + + ASSERT(0 == uv_loop_init(&loop)); + + fd = open("test/fixtures/empty_file", O_RDONLY); + if (fd != -1) { + ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); + ASSERT(0 == close(fd)); + } + +/* Bug on AIX where '/dev/random' returns 1 from isatty() */ +#ifndef _AIX + fd = open("/dev/random", O_RDONLY); + if (fd != -1) { + ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); + ASSERT(0 == close(fd)); + } +#endif /* _AIX */ + + fd = open("/dev/zero", O_RDONLY); + if (fd != -1) { + ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); + ASSERT(0 == close(fd)); + } + + fd = open("/dev/tty", O_RDONLY); + if (fd != -1) { + ASSERT(0 == uv_tty_init(&loop, &tty, fd, 1)); + ASSERT(0 == close(fd)); + uv_close((uv_handle_t*) &tty, NULL); + } + + ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT(0 == uv_loop_close(&loop)); + + MAKE_VALGRIND_HAPPY(); +#endif + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-bind.c b/3rdparty/libuv/test/test-udp-bind.c new file mode 100644 index 00000000000..a1e080ee70c --- /dev/null +++ b/3rdparty/libuv/test/test-udp-bind.c @@ -0,0 +1,93 @@ +/* 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 <string.h> + + +TEST_IMPL(udp_bind) { + struct sockaddr_in addr; + uv_loop_t* loop; + uv_udp_t h1, h2; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + loop = uv_default_loop(); + + r = uv_udp_init(loop, &h1); + ASSERT(r == 0); + + r = uv_udp_init(loop, &h2); + ASSERT(r == 0); + + r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, 0); + ASSERT(r == UV_EADDRINUSE); + + uv_close((uv_handle_t*) &h1, NULL); + uv_close((uv_handle_t*) &h2, NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_bind_reuseaddr) { + struct sockaddr_in addr; + uv_loop_t* loop; + uv_udp_t h1, h2; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + loop = uv_default_loop(); + + r = uv_udp_init(loop, &h1); + ASSERT(r == 0); + + r = uv_udp_init(loop, &h2); + ASSERT(r == 0); + + r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR); + ASSERT(r == 0); + + r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR); + ASSERT(r == 0); + + uv_close((uv_handle_t*) &h1, NULL); + uv_close((uv_handle_t*) &h2, NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-create-socket-early.c b/3rdparty/libuv/test/test-udp-create-socket-early.c new file mode 100644 index 00000000000..3d0152428b8 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-create-socket-early.c @@ -0,0 +1,132 @@ +/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>. + * 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 <string.h> + +#ifdef _WIN32 +# define INVALID_FD (INVALID_HANDLE_VALUE) +#else +# define INVALID_FD (-1) +#endif + + +TEST_IMPL(udp_create_early) { + struct sockaddr_in addr; + struct sockaddr_in sockname; + uv_udp_t client; + uv_os_fd_t fd; + int r, namelen; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init_ex(uv_default_loop(), &client, AF_INET); + ASSERT(r == 0); + + r = uv_fileno((const uv_handle_t*) &client, &fd); + ASSERT(r == 0); + ASSERT(fd != INVALID_FD); + + /* Windows returns WSAEINVAL if the socket is not bound */ +#ifndef _WIN32 + namelen = sizeof sockname; + r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); + ASSERT(r == 0); + ASSERT(sockname.sin_family == AF_INET); +#endif + + r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + namelen = sizeof sockname; + r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); + ASSERT(r == 0); + ASSERT(memcmp(&addr.sin_addr, + &sockname.sin_addr, + sizeof(addr.sin_addr)) == 0); + + uv_close((uv_handle_t*) &client, NULL); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_create_early_bad_bind) { + struct sockaddr_in addr; + uv_udp_t client; + uv_os_fd_t fd; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init_ex(uv_default_loop(), &client, AF_INET6); + ASSERT(r == 0); + + r = uv_fileno((const uv_handle_t*) &client, &fd); + ASSERT(r == 0); + ASSERT(fd != INVALID_FD); + + /* Windows returns WSAEINVAL if the socket is not bound */ +#ifndef _WIN32 + { + int namelen; + struct sockaddr_in6 sockname; + namelen = sizeof sockname; + r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); + ASSERT(r == 0); + ASSERT(sockname.sin6_family == AF_INET6); + } +#endif + + r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); +#ifndef _WIN32 + ASSERT(r == UV_EINVAL); +#else + ASSERT(r == UV_EFAULT); +#endif + + uv_close((uv_handle_t*) &client, NULL); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_create_early_bad_domain) { + uv_udp_t client; + int r; + + r = uv_udp_init_ex(uv_default_loop(), &client, 47); + ASSERT(r == UV_EINVAL); + + r = uv_udp_init_ex(uv_default_loop(), &client, 1024); + ASSERT(r == UV_EINVAL); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-dgram-too-big.c b/3rdparty/libuv/test/test-udp-dgram-too-big.c new file mode 100644 index 00000000000..bd44c425287 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-dgram-too-big.c @@ -0,0 +1,91 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &handle_) + +#define CHECK_REQ(req) \ + ASSERT((req) == &req_); + +static uv_udp_t handle_; +static uv_udp_send_t req_; + +static int send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + CHECK_REQ(req); + CHECK_HANDLE(req->handle); + + ASSERT(status == UV_EMSGSIZE); + + uv_close((uv_handle_t*)req->handle, close_cb); + send_cb_called++; +} + + +TEST_IMPL(udp_dgram_too_big) { + char dgram[65536]; /* 64K MTU is unlikely, even on localhost */ + struct sockaddr_in addr; + uv_buf_t buf; + int r; + + memset(dgram, 42, sizeof dgram); /* silence valgrind */ + + r = uv_udp_init(uv_default_loop(), &handle_); + ASSERT(r == 0); + + buf = uv_buf_init(dgram, sizeof dgram); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_send(&req_, + &handle_, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(send_cb_called == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(send_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-ipv6.c b/3rdparty/libuv/test/test-udp-ipv6.c new file mode 100644 index 00000000000..1b0db78b8ef --- /dev/null +++ b/3rdparty/libuv/test/test-udp-ipv6.c @@ -0,0 +1,193 @@ +/* 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 <string.h> + +#ifdef __FreeBSD__ +#include <sys/sysctl.h> +#endif + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server \ + || (uv_udp_t*)(handle) == &client \ + || (uv_timer_t*)(handle) == &timeout) + +#define CHECK_REQ(req) \ + ASSERT((req) == &req_); + +static uv_udp_t client; +static uv_udp_t server; +static uv_udp_send_t req_; +static uv_timer_t timeout; + +static int send_cb_called; +static int recv_cb_called; +static int close_cb_called; + +#ifdef __FreeBSD__ +static int can_ipv6_ipv4_dual() { + int v6only; + size_t size = sizeof(int); + + if (sysctlbyname("net.inet6.ip6.v6only", &v6only, &size, NULL, 0)) + return 0; + + return v6only != 1; +} +#endif + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + CHECK_REQ(req); + CHECK_HANDLE(req->handle); + ASSERT(status == 0); + send_cb_called++; +} + + +static void ipv6_recv_fail(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + ASSERT(0 && "this function should not have been called"); +} + + +static void ipv6_recv_ok(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(nread >= 0); + + if (nread) + recv_cb_called++; +} + + +static void timeout_cb(uv_timer_t* timer) { + uv_close((uv_handle_t*)&server, close_cb); + uv_close((uv_handle_t*)&client, close_cb); + uv_close((uv_handle_t*)&timeout, close_cb); +} + + +static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { + struct sockaddr_in6 addr6; + struct sockaddr_in addr; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip6_addr("::0", TEST_PORT, &addr6)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &addr6, bind_flags); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, recv_cb); + ASSERT(r == 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + buf = uv_buf_init("PING", 4); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_send(&req_, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timeout); + ASSERT(r == 0); + + r = uv_timer_start(&timeout, timeout_cb, 500, 0); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(send_cb_called == 0); + ASSERT(recv_cb_called == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 3); + + MAKE_VALGRIND_HAPPY(); +} + + +TEST_IMPL(udp_dual_stack) { + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + +#ifdef __FreeBSD__ + if (!can_ipv6_ipv4_dual()) + RETURN_SKIP("IPv6-IPv4 dual stack not supported"); +#endif + + do_test(ipv6_recv_ok, 0); + + ASSERT(recv_cb_called == 1); + ASSERT(send_cb_called == 1); + + return 0; +} + + +TEST_IMPL(udp_ipv6_only) { + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); + + ASSERT(recv_cb_called == 0); + ASSERT(send_cb_called == 1); + + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-multicast-interface.c b/3rdparty/libuv/test/test-udp-multicast-interface.c new file mode 100644 index 00000000000..71001a77e03 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-multicast-interface.c @@ -0,0 +1,99 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int sv_send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0 || status == UV_ENETUNREACH); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +TEST_IMPL(udp_multicast_interface) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in addr; + struct sockaddr_in baddr; + + ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &baddr)); + r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); + ASSERT(r == 0); + + r = uv_udp_set_multicast_interface(&server, "0.0.0.0"); + ASSERT(r == 0); + + /* server sends "PING" */ + buf = uv_buf_init("PING", 4); + r = uv_udp_send(&req, + &server, + &buf, + 1, + (const struct sockaddr*)&addr, + sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 1); + + ASSERT(client.send_queue_size == 0); + ASSERT(server.send_queue_size == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-multicast-interface6.c b/3rdparty/libuv/test/test-udp-multicast-interface6.c new file mode 100644 index 00000000000..d3881e83bb1 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-multicast-interface6.c @@ -0,0 +1,103 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int sv_send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +TEST_IMPL(udp_multicast_interface6) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in6 addr; + struct sockaddr_in6 baddr; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + ASSERT(0 == uv_ip6_addr("::", 0, &baddr)); + r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); + ASSERT(r == 0); + +#if defined(__APPLE__) || defined(__FreeBSD__) + r = uv_udp_set_multicast_interface(&server, "::1%lo0"); +#else + r = uv_udp_set_multicast_interface(&server, NULL); +#endif + ASSERT(r == 0); + + /* server sends "PING" */ + buf = uv_buf_init("PING", 4); + r = uv_udp_send(&req, + &server, + &buf, + 1, + (const struct sockaddr*)&addr, + sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-multicast-join.c b/3rdparty/libuv/test/test-udp-multicast-join.c new file mode 100644 index 00000000000..6110a8d922a --- /dev/null +++ b/3rdparty/libuv/test/test-udp-multicast-join.c @@ -0,0 +1,150 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_recv_cb_called; + +static int sv_send_cb_called; + +static int close_cb_called; + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +static void cl_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + cl_recv_cb_called++; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards cl_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PING", buf->base, nread)); + + /* we are done with the client handle, we can close it */ + uv_close((uv_handle_t*) &client, close_cb); +} + + +TEST_IMPL(udp_multicast_join) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* bind to the desired port */ + r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + /* join the multicast channel */ + r = uv_udp_set_membership(&client, "239.255.0.1", NULL, UV_JOIN_GROUP); + if (r == UV_ENODEV) + RETURN_SKIP("No multicast support."); + ASSERT(r == 0); + + r = uv_udp_recv_start(&client, alloc_cb, cl_recv_cb); + ASSERT(r == 0); + + buf = uv_buf_init("PING", 4); + + /* server sends "PING" */ + r = uv_udp_send(&req, + &server, + &buf, + 1, + (const struct sockaddr*) &addr, + sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(cl_recv_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(cl_recv_cb_called == 1); + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-multicast-join6.c b/3rdparty/libuv/test/test-udp-multicast-join6.c new file mode 100644 index 00000000000..f635bdb9e14 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-multicast-join6.c @@ -0,0 +1,161 @@ +/* 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 <string.h> + + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_recv_cb_called; + +static int sv_send_cb_called; + +static int close_cb_called; + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +static void cl_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + cl_recv_cb_called++; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards cl_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PING", buf->base, nread)); + + /* we are done with the client handle, we can close it */ + uv_close((uv_handle_t*) &client, close_cb); +} + + +TEST_IMPL(udp_multicast_join6) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in6 addr; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* bind to the desired port */ + r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + /* join the multicast channel */ +#if defined(__APPLE__) || defined(_AIX) + r = uv_udp_set_membership(&client, "ff02::1", "::1%lo0", UV_JOIN_GROUP); +#else + r = uv_udp_set_membership(&client, "ff02::1", NULL, UV_JOIN_GROUP); +#endif + if (r == UV_ENODEV) { + MAKE_VALGRIND_HAPPY(); + RETURN_SKIP("No ipv6 multicast route"); + } + + ASSERT(r == 0); + + r = uv_udp_recv_start(&client, alloc_cb, cl_recv_cb); + ASSERT(r == 0); + + buf = uv_buf_init("PING", 4); + + /* server sends "PING" */ + r = uv_udp_send(&req, + &server, + &buf, + 1, + (const struct sockaddr*) &addr, + sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(cl_recv_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(cl_recv_cb_called == 1); + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-multicast-ttl.c b/3rdparty/libuv/test/test-udp-multicast-ttl.c new file mode 100644 index 00000000000..7f1af9b9dd9 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-multicast-ttl.c @@ -0,0 +1,94 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int sv_send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0 || status == UV_ENETUNREACH); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +TEST_IMPL(udp_multicast_ttl) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in addr; + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &addr)); + r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_set_multicast_ttl(&server, 32); + ASSERT(r == 0); + + /* server sends "PING" */ + buf = uv_buf_init("PING", 4); + ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); + r = uv_udp_send(&req, + &server, + &buf, + 1, + (const struct sockaddr*) &addr, + sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-open.c b/3rdparty/libuv/test/test-udp-open.c new file mode 100644 index 00000000000..4d77f45d367 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-open.c @@ -0,0 +1,204 @@ +/* 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 <string.h> + +#ifndef _WIN32 +# include <unistd.h> +#endif + +static int send_cb_called = 0; +static int close_cb_called = 0; + +static uv_udp_send_t send_req; + + +static void startup(void) { +#ifdef _WIN32 + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); +#endif +} + + +static uv_os_sock_t create_udp_socket(void) { + uv_os_sock_t sock; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); +#ifdef _WIN32 + ASSERT(sock != INVALID_SOCKET); +#else + ASSERT(sock >= 0); +#endif + +#ifndef _WIN32 + { + /* Allow reuse of the port. */ + int yes = 1; + int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + ASSERT(r == 0); + } +#endif + + 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 void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + int r; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards sv_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(flags == 0); + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(memcmp("PING", buf->base, nread) == 0); + + r = uv_udp_recv_stop(handle); + ASSERT(r == 0); + + uv_close((uv_handle_t*) handle, close_cb); +} + + +static void send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + + send_cb_called++; +} + + +TEST_IMPL(udp_open) { + struct sockaddr_in addr; + uv_buf_t buf = uv_buf_init("PING", 4); + uv_udp_t client; + uv_os_sock_t sock; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + startup(); + sock = create_udp_socket(); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_udp_open(&client, sock); + ASSERT(r == 0); + + r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&client, alloc_cb, recv_cb); + ASSERT(r == 0); + + r = uv_udp_send(&send_req, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(send_cb_called == 1); + ASSERT(close_cb_called == 1); + + ASSERT(client.send_queue_size == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_open_twice) { + uv_udp_t client; + uv_os_sock_t sock1, sock2; + int r; + + startup(); + sock1 = create_udp_socket(); + sock2 = create_udp_socket(); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_udp_open(&client, sock1); + ASSERT(r == 0); + + r = uv_udp_open(&client, sock2); + ASSERT(r == UV_EBUSY); + close_socket(sock2); + + uv_close((uv_handle_t*) &client, NULL); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-options.c b/3rdparty/libuv/test/test-udp-options.c new file mode 100644 index 00000000000..0da1786f506 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-options.c @@ -0,0 +1,126 @@ +/* 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 <string.h> + + +static int udp_options_test(const struct sockaddr* addr) { + static int invalid_ttls[] = { -1, 0, 256 }; + uv_loop_t* loop; + uv_udp_t h; + int i, r; + + loop = uv_default_loop(); + + r = uv_udp_init(loop, &h); + ASSERT(r == 0); + + uv_unref((uv_handle_t*)&h); /* don't keep the loop alive */ + + r = uv_udp_bind(&h, addr, 0); + ASSERT(r == 0); + + r = uv_udp_set_broadcast(&h, 1); + r |= uv_udp_set_broadcast(&h, 1); + r |= uv_udp_set_broadcast(&h, 0); + r |= uv_udp_set_broadcast(&h, 0); + ASSERT(r == 0); + + /* values 1-255 should work */ + for (i = 1; i <= 255; i++) { + r = uv_udp_set_ttl(&h, i); + ASSERT(r == 0); + } + + for (i = 0; i < (int) ARRAY_SIZE(invalid_ttls); i++) { + r = uv_udp_set_ttl(&h, invalid_ttls[i]); + ASSERT(r == UV_EINVAL); + } + + r = uv_udp_set_multicast_loop(&h, 1); + r |= uv_udp_set_multicast_loop(&h, 1); + r |= uv_udp_set_multicast_loop(&h, 0); + r |= uv_udp_set_multicast_loop(&h, 0); + ASSERT(r == 0); + + /* values 0-255 should work */ + for (i = 0; i <= 255; i++) { + r = uv_udp_set_multicast_ttl(&h, i); + ASSERT(r == 0); + } + + /* anything >255 should fail */ + r = uv_udp_set_multicast_ttl(&h, 256); + ASSERT(r == UV_EINVAL); + /* don't test ttl=-1, it's a valid value on some platforms */ + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(udp_options) { + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + return udp_options_test((const struct sockaddr*) &addr); +} + + +TEST_IMPL(udp_options6) { + struct sockaddr_in6 addr; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); + return udp_options_test((const struct sockaddr*) &addr); +} + + +TEST_IMPL(udp_no_autobind) { + uv_loop_t* loop; + uv_udp_t h; + + loop = uv_default_loop(); + + ASSERT(0 == uv_udp_init(loop, &h)); + ASSERT(UV_EBADF == uv_udp_set_multicast_ttl(&h, 32)); + ASSERT(UV_EBADF == uv_udp_set_broadcast(&h, 1)); + ASSERT(UV_EBADF == uv_udp_set_ttl(&h, 1)); + ASSERT(UV_EBADF == uv_udp_set_multicast_loop(&h, 1)); + ASSERT(UV_EBADF == uv_udp_set_multicast_interface(&h, "0.0.0.0")); + + uv_close((uv_handle_t*) &h, NULL); + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-send-and-recv.c b/3rdparty/libuv/test/test-udp-send-and-recv.c new file mode 100644 index 00000000000..633a16727b2 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-send-and-recv.c @@ -0,0 +1,214 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_send_cb_called; +static int cl_recv_cb_called; + +static int sv_send_cb_called; +static int sv_recv_cb_called; + +static int close_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(1 == uv_is_closing(handle)); + close_cb_called++; +} + + +static void cl_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards cl_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PONG", buf->base, nread)); + + cl_recv_cb_called++; + + uv_close((uv_handle_t*) handle, close_cb); +} + + +static void cl_send_cb(uv_udp_send_t* req, int status) { + int r; + + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + r = uv_udp_recv_start(req->handle, alloc_cb, cl_recv_cb); + ASSERT(r == 0); + + cl_send_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + uv_close((uv_handle_t*) req->handle, close_cb); + free(req); + + sv_send_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + uv_udp_send_t* req; + uv_buf_t sndbuf; + int r; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards sv_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PING", rcvbuf->base, nread)); + + /* FIXME? `uv_udp_recv_stop` does what it says: recv_cb is not called + * anymore. That's problematic because the read buffer won't be returned + * either... Not sure I like that but it's consistent with `uv_read_stop`. + */ + r = uv_udp_recv_stop(handle); + ASSERT(r == 0); + + req = malloc(sizeof *req); + ASSERT(req != NULL); + + sndbuf = uv_buf_init("PONG", 4); + r = uv_udp_send(req, handle, &sndbuf, 1, addr, sv_send_cb); + ASSERT(r == 0); + + sv_recv_cb_called++; +} + + +TEST_IMPL(udp_send_and_recv) { + struct sockaddr_in addr; + uv_udp_send_t req; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* client sends "PING", expects "PONG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + cl_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(cl_send_cb_called == 0); + ASSERT(cl_recv_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + ASSERT(sv_recv_cb_called == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(cl_send_cb_called == 1); + ASSERT(cl_recv_cb_called == 1); + ASSERT(sv_send_cb_called == 1); + ASSERT(sv_recv_cb_called == 1); + ASSERT(close_cb_called == 2); + + ASSERT(client.send_queue_size == 0); + ASSERT(server.send_queue_size == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-send-immediate.c b/3rdparty/libuv/test/test-udp-send-immediate.c new file mode 100644 index 00000000000..0999f6b3425 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-send-immediate.c @@ -0,0 +1,148 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_send_cb_called; +static int sv_recv_cb_called; +static int close_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(1 == uv_is_closing(handle)); + close_cb_called++; +} + + +static void cl_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + cl_send_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards sv_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(memcmp("PING", rcvbuf->base, nread) == 0 || + memcmp("PANG", rcvbuf->base, nread) == 0); + + if (++sv_recv_cb_called == 2) { + uv_close((uv_handle_t*) &server, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + } +} + + +TEST_IMPL(udp_send_immediate) { + struct sockaddr_in addr; + uv_udp_send_t req1, req2; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* client sends "PING", then "PANG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req1, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + cl_send_cb); + ASSERT(r == 0); + + buf = uv_buf_init("PANG", 4); + + r = uv_udp_send(&req2, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + cl_send_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(cl_send_cb_called == 2); + ASSERT(sv_recv_cb_called == 2); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-send-unreachable.c b/3rdparty/libuv/test/test-udp-send-unreachable.c new file mode 100644 index 00000000000..c6500320d78 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-send-unreachable.c @@ -0,0 +1,150 @@ +/* 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 <string.h> + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &client) + +static uv_udp_t client; +static uv_timer_t timer; + +static int send_cb_called; +static int recv_cb_called; +static int close_cb_called; +static int alloc_cb_called; +static int timer_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); + alloc_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(1 == uv_is_closing(handle)); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + send_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + recv_cb_called++; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } else if (nread == 0) { + /* Returning unused buffer */ + ASSERT(addr == NULL); + } else { + ASSERT(addr != NULL); + } +} + + +static void timer_cb(uv_timer_t* h) { + ASSERT(h == &timer); + timer_cb_called++; + uv_close((uv_handle_t*) &client, close_cb); + uv_close((uv_handle_t*) h, close_cb); +} + + +TEST_IMPL(udp_send_unreachable) { + struct sockaddr_in addr; + struct sockaddr_in addr2; + uv_udp_send_t req1, req2; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2)); + + r = uv_timer_init( uv_default_loop(), &timer ); + ASSERT(r == 0); + + r = uv_timer_start( &timer, timer_cb, 1000, 0 ); + ASSERT(r == 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_udp_bind(&client, (const struct sockaddr*) &addr2, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&client, alloc_cb, recv_cb); + ASSERT(r == 0); + + /* client sends "PING", then "PANG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req1, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb); + ASSERT(r == 0); + + buf = uv_buf_init("PANG", 4); + + r = uv_udp_send(&req2, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(send_cb_called == 2); + ASSERT(recv_cb_called == alloc_cb_called); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-udp-try-send.c b/3rdparty/libuv/test/test-udp-try-send.c new file mode 100644 index 00000000000..7b6de365487 --- /dev/null +++ b/3rdparty/libuv/test/test-udp-try-send.c @@ -0,0 +1,133 @@ +/* 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 <string.h> + +#ifdef _WIN32 + +TEST_IMPL(udp_try_send) { + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#else /* !_WIN32 */ + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int sv_recv_cb_called; + +static int close_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(uv_is_closing(handle)); + close_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + ASSERT(nread > 0); + + if (nread == 0) { + ASSERT(addr == NULL); + return; + } + + ASSERT(nread == 4); + ASSERT(addr != NULL); + + ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); + uv_close((uv_handle_t*) handle, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + + sv_recv_cb_called++; +} + + +TEST_IMPL(udp_try_send) { + struct sockaddr_in addr; + static char buffer[64 * 1024]; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + buf = uv_buf_init(buffer, sizeof(buffer)); + r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); + ASSERT(r == UV_EMSGSIZE); + + buf = uv_buf_init("EXIT", 4); + r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); + ASSERT(r == 4); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + ASSERT(sv_recv_cb_called == 1); + + ASSERT(client.send_queue_size == 0); + ASSERT(server.send_queue_size == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* !_WIN32 */ diff --git a/3rdparty/libuv/test/test-walk-handles.c b/3rdparty/libuv/test/test-walk-handles.c new file mode 100644 index 00000000000..4b0ca6ebc55 --- /dev/null +++ b/3rdparty/libuv/test/test-walk-handles.c @@ -0,0 +1,77 @@ +/* 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 char magic_cookie[] = "magic cookie"; +static int seen_timer_handle; +static uv_timer_t timer; + + +static void walk_cb(uv_handle_t* handle, void* arg) { + ASSERT(arg == (void*)magic_cookie); + + if (handle == (uv_handle_t*)&timer) { + seen_timer_handle++; + } else { + ASSERT(0 && "unexpected handle"); + } +} + + +static void timer_cb(uv_timer_t* handle) { + ASSERT(handle == &timer); + + uv_walk(handle->loop, walk_cb, magic_cookie); + uv_close((uv_handle_t*)handle, NULL); +} + + +TEST_IMPL(walk_handles) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 1, 0); + ASSERT(r == 0); + + /* Start event loop, expect to see the timer handle in walk_cb. */ + ASSERT(seen_timer_handle == 0); + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(seen_timer_handle == 1); + + /* Loop is finished, walk_cb should not see our timer handle. */ + seen_timer_handle = 0; + uv_walk(loop, walk_cb, magic_cookie); + ASSERT(seen_timer_handle == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/3rdparty/libuv/test/test-watcher-cross-stop.c b/3rdparty/libuv/test/test-watcher-cross-stop.c new file mode 100644 index 00000000000..910ed0fb613 --- /dev/null +++ b/3rdparty/libuv/test/test-watcher-cross-stop.c @@ -0,0 +1,103 @@ +/* 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 <string.h> +#include <errno.h> + +/* NOTE: Number should be big enough to trigger this problem */ +static uv_udp_t sockets[2500]; +static uv_udp_send_t reqs[ARRAY_SIZE(sockets)]; +static char slab[1]; +static unsigned int recv_cb_called; +static unsigned int send_cb_called; +static unsigned int close_cb_called; + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + recv_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + send_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +TEST_IMPL(watcher_cross_stop) { + uv_loop_t* loop = uv_default_loop(); + unsigned int i; + struct sockaddr_in addr; + uv_buf_t buf; + char big_string[1024]; + + TEST_FILE_LIMIT(ARRAY_SIZE(sockets) + 32); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + memset(big_string, 'A', sizeof(big_string)); + buf = uv_buf_init(big_string, sizeof(big_string)); + + for (i = 0; i < ARRAY_SIZE(sockets); i++) { + ASSERT(0 == uv_udp_init(loop, &sockets[i])); + ASSERT(0 == uv_udp_bind(&sockets[i], + (const struct sockaddr*) &addr, + UV_UDP_REUSEADDR)); + ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); + ASSERT(0 == uv_udp_send(&reqs[i], + &sockets[i], + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb)); + } + + while (recv_cb_called == 0) + uv_run(loop, UV_RUN_ONCE); + + for (i = 0; i < ARRAY_SIZE(sockets); i++) + uv_close((uv_handle_t*) &sockets[i], close_cb); + + ASSERT(recv_cb_called > 0); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(ARRAY_SIZE(sockets) == send_cb_called); + ASSERT(ARRAY_SIZE(sockets) == close_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} |