From daf8a45d44bed63fec14a68d798786d76c73f4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 19 Aug 2016 13:42:43 +0100 Subject: [PATCH] win: return IOCP HANDLE in uv_backend_fd Change the return type from int to uv_os_fd_t so we can return an int on Unix and a HANDLE on Windows. Applications can use this to poll the libuv event loop from another thread as is now possible on Unix. The test implementation is based on what Electron does: https://github.com/electron/electron/blob/master/atom/common/node_bindings_win.cc Refs: https://github.com/libuv/libuv/issues/965 PR-URL: https://github.com/libuv/libuv/pull/1007 Reviewed-By: Colin Ihrig Reviewed-By: Imran Iqbal --- docs/src/loop.rst | 9 ++++--- include/uv.h | 2 +- src/unix/core.c | 2 +- src/win/core.c | 4 +-- test/test-embed.c | 67 +++++++++++++++++++++++++++++++++++------------ 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/docs/src/loop.rst b/docs/src/loop.rst index 1f504cb3..400d8c05 100644 --- a/docs/src/loop.rst +++ b/docs/src/loop.rst @@ -121,10 +121,10 @@ API Returns the size of the `uv_loop_t` structure. Useful for FFI binding writers who don't want to know the structure layout. -.. c:function:: int uv_backend_fd(const uv_loop_t* loop) +.. c:function:: uv_os_fd_t uv_backend_fd(const uv_loop_t* loop) - Get backend file descriptor. Only kqueue, epoll and event ports are - supported. + Get backend file descriptor. Returns the epoll / kqueue / event ports file + descriptor on Unix and the IOCP `HANDLE` on Windows. This can be used in conjunction with `uv_run(loop, UV_RUN_NOWAIT)` to poll in one thread and run the event loop's callbacks in another see @@ -134,6 +134,9 @@ API Embedding a kqueue fd in another kqueue pollset doesn't work on all platforms. It's not an error to add the fd but it never generates events. + .. versionchanged:: 2.0.0: added support for Windows and changed return type + to ``uv_os_fd_t``. + .. c:function:: int uv_backend_timeout(const uv_loop_t* loop) Get the poll timeout. The return value is in milliseconds, or -1 for no diff --git a/include/uv.h b/include/uv.h index cc385d01..99ed9771 100644 --- a/include/uv.h +++ b/include/uv.h @@ -280,7 +280,7 @@ UV_EXTERN int uv_has_ref(const uv_handle_t*); UV_EXTERN void uv_update_time(uv_loop_t*); UV_EXTERN uint64_t uv_now(const uv_loop_t*); -UV_EXTERN int uv_backend_fd(const uv_loop_t*); +UV_EXTERN uv_os_fd_t uv_backend_fd(const uv_loop_t*); UV_EXTERN int uv_backend_timeout(const uv_loop_t*); typedef void (*uv_alloc_cb)(uv_handle_t* handle, diff --git a/src/unix/core.c b/src/unix/core.c index 965aae79..5be878dd 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -283,7 +283,7 @@ int uv_is_closing(const uv_handle_t* handle) { } -int uv_backend_fd(const uv_loop_t* loop) { +uv_os_fd_t uv_backend_fd(const uv_loop_t* loop) { return loop->backend_fd; } diff --git a/src/win/core.c b/src/win/core.c index 26ad3929..ca9f2cc5 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -264,8 +264,8 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { } -int uv_backend_fd(const uv_loop_t* loop) { - return -1; +uv_os_fd_t uv_backend_fd(const uv_loop_t* loop) { + return loop->iocp; } diff --git a/test/test-embed.c b/test/test-embed.c index 06137456..145819a6 100644 --- a/test/test-embed.c +++ b/test/test-embed.c @@ -41,7 +41,7 @@ # endif #endif -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) +#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) || defined(_WIN32) #if defined(HAVE_KQUEUE) # include @@ -62,28 +62,59 @@ static volatile int embed_closed; static int embed_timer_called; -static void embed_thread_runner(void* arg) { +#if defined(_WIN32) +static void embed_thread_poll_win(HANDLE iocp, int timeout) { + DWORD bytes; + ULONG_PTR key; + OVERLAPPED* overlapped; + + GetQueuedCompletionStatus(iocp, + &bytes, + &key, + &overlapped, + timeout); + + /* Give the event back so the loop can deal with it. */ + if (overlapped != NULL) + PostQueuedCompletionStatus(iocp, + bytes, + key, + overlapped); +} +#else +static void embed_thread_poll_unix(int fd, int timeout) { int r; - int fd; + 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); +} +#endif /* !_WIN32 */ + + +static void embed_thread_runner(void* arg) { + uv_os_fd_t 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); - } +#if defined(_WIN32) + embed_thread_poll_win(fd, timeout); +#else + embed_thread_poll_unix(fd, timeout); #endif - } while (r == -1 && errno == EINTR); + uv_async_send(&embed_async); uv_sem_wait(&embed_sem); } @@ -107,7 +138,7 @@ static void embed_timer_cb(uv_timer_t* timer) { TEST_IMPL(embed) { -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) +#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) || defined(_WIN32) uv_loop_t external; ASSERT(0 == uv_loop_init(&external)); @@ -132,7 +163,9 @@ TEST_IMPL(embed) { uv_loop_close(&external); ASSERT(embed_timer_called == 1); -#endif return 0; +#else + RETURN_SKIP("Not supported in the current platform."); +#endif }