diff --git a/include/uv-private/ev.h b/include/uv-private/ev.h index 11e81cda5e..710f334e9f 100644 --- a/include/uv-private/ev.h +++ b/include/uv-private/ev.h @@ -640,8 +640,10 @@ void ev_unref (EV_P); */ void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg); -# if EV_FEATURE_API unsigned int ev_iteration (EV_P); /* number of loop iterations */ +void ev_tick_me_off (EV_P); // a way to increment the tick count for things + // like next tick +# if EV_FEATURE_API unsigned int ev_depth (EV_P); /* #ev_loop enters - #ev_loop leaves */ void ev_verify (EV_P); /* abort if loop data corrupted */ diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index baeb3e3627..c02ad74031 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -204,7 +204,9 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* Counter to keep track of active tcp streams */ \ unsigned int active_tcp_streams; \ /* Counter to keep track of active udp streams */ \ - unsigned int active_udp_streams; + unsigned int active_udp_streams; \ + /* Counter to keep track of the turns of the event loop */ \ + unsigned int loop_count; #define UV_HANDLE_TYPE_PRIVATE \ UV_ARES_EVENT, diff --git a/include/uv.h b/include/uv.h index 0ede3083d3..aa7c5f6749 100644 --- a/include/uv.h +++ b/include/uv.h @@ -248,6 +248,16 @@ UV_EXTERN void uv_unref(uv_loop_t*); UV_EXTERN void uv_update_time(uv_loop_t*); UV_EXTERN int64_t uv_now(uv_loop_t*); +/* + * Obtain the current tick count. + */ +UV_EXTERN unsigned int uv_current_tick(uv_loop_t*); + +/* + * A way to increment the tick count for things that are not going + * to have blocking io. Like node's proccess.nextTick() + */ +UV_EXTERN void uv_tick_me_off (uv_loop_t*); /* * Should return a buffer that libuv can use to read data into. diff --git a/src/unix/core.c b/src/unix/core.c index 5d5048ad3b..793bf03041 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -547,6 +547,14 @@ int uv__dup(int fd) { return fd; } +unsigned int uv_current_tick (uv_loop_t* loop) { + return ev_iteration(loop->ev); +} + +void uv_tick_me_off (uv_loop_t* loop) { + ev_tick_me_off(loop->ev); +} + /* TODO move to uv-common.c? */ size_t uv__strlcpy(char* dst, const char* src, size_t size) { diff --git a/src/unix/ev/ev.c b/src/unix/ev/ev.c index a432bfbf6b..cf18716581 100644 --- a/src/unix/ev/ev.c +++ b/src/unix/ev/ev.c @@ -1625,13 +1625,20 @@ ev_backend (EV_P) return backend; } -#if EV_FEATURE_API + unsigned int ev_iteration (EV_P) { return loop_count; } +void +ev_tick_me_off(EV_P) +{ + ++loop_count; +} + +#if EV_FEATURE_API unsigned int ev_depth (EV_P) { @@ -2494,9 +2501,9 @@ ev_run (EV_P_ int flags) } } -#if EV_FEATURE_API + ++loop_count; -#endif + assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */ backend_poll (EV_A_ waittime); assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */ diff --git a/src/win/core.c b/src/win/core.c index 1983361bad..558d170788 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -147,6 +147,13 @@ void uv_unref(uv_loop_t* loop) { loop->refs--; } +unsigned int uv_current_tick (uv_loop_t* loop) { + return loop->loop_count; +} + +void uv_tick_me_off (uv_loop_t* loop) { + ++(loop->loop_count); +} static void uv_poll(uv_loop_t* loop, int block) { BOOL success; @@ -233,7 +240,7 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { } \ \ uv_prepare_invoke((loop)); \ - \ + ++((loop)->loop_count); \ poll((loop), (loop)->idle_handles == NULL && \ (loop)->pending_reqs_tail == NULL && \ (loop)->endgame_handles == NULL && \ diff --git a/test/test-current-tick.c b/test/test-current-tick.c new file mode 100644 index 0000000000..5be2f9233e --- /dev/null +++ b/test/test-current-tick.c @@ -0,0 +1,142 @@ +/* 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 +#include + +/* this is a copy of test-async. this is just so that I can + * build up a few requests and test that uv_current_tick will + * return the proper value + */ + +static uv_prepare_t prepare_handle; + +static uv_async_t async1_handle; + +static int prepare_cb_called = 0; + +static volatile int async1_cb_called = 0; +static int async1_closed = 0; + +static int close_cb_called = 0; + +static uintptr_t thread1_id = 0; + + +/* Thread 1 makes sure that async1_cb_called reaches 3 before exiting. */ +void thread1_entry(void *arg) { + uv_sleep(50); + + while (1) { + switch (async1_cb_called) { + case 0: + uv_async_send(&async1_handle); + break; + + case 1: + uv_async_send(&async1_handle); + break; + + case 2: + uv_async_send(&async1_handle); + break; + + default: + return; + } + } +} + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void async1_cb(uv_async_t* handle, int status) { + ASSERT(handle == &async1_handle); + ASSERT(status == 0); + + async1_cb_called++; + printf("async1_cb #%d\n", async1_cb_called); + + if (async1_cb_called > 2 && !async1_closed) { + async1_closed = 1; + uv_close((uv_handle_t*)handle, close_cb); + } +} + + +static void prepare_cb(uv_prepare_t* handle, int status) { + ASSERT(handle == &prepare_handle); + ASSERT(status == 0); + + switch (prepare_cb_called) { + case 0: + thread1_id = uv_create_thread(thread1_entry, NULL); + ASSERT(thread1_id != 0); + break; + + case 1: + uv_close((uv_handle_t*)handle, close_cb); + break; + + default: + FATAL("Should never get here"); + } + + prepare_cb_called++; +} + + +TEST_IMPL(async) { + int r; + + 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_async_init(uv_default_loop(), &async1_handle, async1_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + r = uv_wait_thread(thread1_id); + ASSERT(r == 0); + + ASSERT(prepare_cb_called == 2); + ASSERT(async1_cb_called > 2); + ASSERT(close_cb_called == 2); + + // here is the business end of the this test + ASSERT(3 == uv_current_tick(uv_default_loop())); + + uv_tick_me_off(uv_default_loop()); + + ASSERT(4 == uv_current_tick(uv_default_loop())); + // end the business + + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index 5743243bcc..17282655de 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -98,6 +98,7 @@ TEST_DECLARE (pipe_ref4) TEST_DECLARE (pipe_ref5) TEST_DECLARE (process_ref) TEST_DECLARE (async) +TEST_DECLARE (current_tick) TEST_DECLARE (get_currentexe) TEST_DECLARE (process_title) TEST_DECLARE (cwd_and_chdir) @@ -285,6 +286,8 @@ TASK_LIST_START TEST_ENTRY (async) + TEST_ENTRY (current_tick) + TEST_ENTRY (get_currentexe) TEST_ENTRY (process_title)