OBJS += src/unix/pipe.o
OBJS += src/unix/poll.o
OBJS += src/unix/process.o
+OBJS += src/unix/signal.o
OBJS += src/unix/stream.o
OBJS += src/unix/tcp.o
OBJS += src/unix/thread.o
proc = subprocess.Popen(CC.split() + ['--version'], stdout=subprocess.PIPE)
is_clang = 'clang' in proc.communicate()[0].split('\n')[0]
proc = subprocess.Popen(CC.split() + ['-dumpversion'], stdout=subprocess.PIPE)
- version = tuple(map(int, proc.communicate()[0].split('.')))
+ version = proc.communicate()[0].split('.')
+ version = map(int, version[:2])
+ version = tuple(version)
return (version, is_clang)
eio_channel *channel; /* data used to direct poll callbacks arising from this req */
-#if __i386 || __amd64
+#if defined(__i386) || defined(__amd64)
unsigned char cancelled;
#else
sig_atomic_t cancelled;
(q)->prev
-#if (NGX_DEBUG)
+#if defined(NGX_DEBUG)
#define ngx_queue_remove(x) \
(x)->next->prev = (x)->prev; \
--- /dev/null
+/* 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 UV_BSD_H
+#define UV_BSD_H
+
+#define UV_PLATFORM_FS_EVENT_FIELDS \
+ uv__io_t event_watcher; \
+ int fflags; \
+ int fd; \
+
+#endif /* UV_BSD_H */
--- /dev/null
+/* 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 UV_DARWIN_H
+#define UV_DARWIN_H
+
+#if defined(__APPLE__) && defined(__MACH__)
+# include <mach/mach.h>
+# include <mach/task.h>
+# include <mach/semaphore.h>
+# define UV_PLATFORM_SEM_T semaphore_t
+#endif
+
+#define UV_PLATFORM_FS_EVENT_FIELDS \
+ ev_io event_watcher; \
+ int fflags; \
+ int fd; \
+
+#endif /* UV_DARWIN_H */
--- /dev/null
+/* 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 UV_LINUX_H
+#define UV_LINUX_H
+
+#define UV_PLATFORM_LOOP_FIELDS \
+ uv__io_t inotify_read_watcher; \
+ void* inotify_watchers; \
+ int inotify_fd; \
+
+#define UV_PLATFORM_FS_EVENT_FIELDS \
+ ngx_queue_t watchers; \
+ int wd; \
+
+#endif /* UV_LINUX_H */
--- /dev/null
+/* 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 UV_SUNOS_H
+#define UV_SUNOS_H
+
+#include <sys/port.h>
+#include <port.h>
+
+#if defined(PORT_SOURCE_FILE)
+
+# define UV_PLATFORM_LOOP_FIELDS \
+ uv__io_t fs_event_watcher; \
+ int fs_fd; \
+
+# define UV_PLATFORM_FS_EVENT_FIELDS \
+ file_obj_t fo; \
+ int fd; \
+
+#endif /* defined(PORT_SOURCE_FILE) */
+
+#endif /* UV_SUNOS_H */
#include <termios.h>
#include <pwd.h>
+#include <semaphore.h>
#include <pthread.h>
#include <signal.h>
-#if defined(__APPLE__) && defined(__MACH__)
-# include <mach/mach.h>
-# include <mach/task.h>
-# include <mach/semaphore.h>
-#else
-# include <semaphore.h>
+struct uv__io_s;
+struct uv_loop_s;
+
+typedef struct uv__io_s uv__io_t;
+typedef void (*uv__io_cb)(struct uv_loop_s* loop, uv__io_t* handle, int events);
+
+struct uv__io_s {
+ ev_io io_watcher;
+};
+
+#if defined(__linux__)
+# include "uv-linux.h"
+#elif defined(__sun)
+# include "uv-sunos.h"
+#elif defined(__APPLE__)
+# include "uv-darwin.h"
+#elif defined(__DragonFly__) || \
+ defined(__FreeBSD__) || \
+ defined(__OpenBSD__) || \
+ defined(__NetBSD__)
+# include "uv-bsd.h"
+#endif
+
+#ifndef UV_PLATFORM_SEM_T
+# define UV_PLATFORM_SEM_T sem_t
+#endif
+
+#ifndef UV_PLATFORM_LOOP_FIELDS
+# define UV_PLATFORM_LOOP_FIELDS /* empty */
#endif
-#if __sun
-# include <sys/port.h>
-# include <port.h>
+#ifndef UV_PLATFORM_FS_EVENT_FIELDS
+# define UV_PLATFORM_FS_EVENT_FIELDS /* empty */
#endif
/* Note: May be cast to struct iovec. See writev(2). */
} uv_buf_t;
typedef int uv_file;
-
typedef int uv_os_sock_t;
-
typedef struct stat uv_statbuf_t;
#define UV_ONCE_INIT PTHREAD_ONCE_INIT
typedef pthread_t uv_thread_t;
typedef pthread_mutex_t uv_mutex_t;
typedef pthread_rwlock_t uv_rwlock_t;
-#if defined(__APPLE__) && defined(__MACH__)
-typedef semaphore_t uv_sem_t;
-#else
-typedef sem_t uv_sem_t;
-#endif
+typedef UV_PLATFORM_SEM_T uv_sem_t;
/* Platform-specific definitions for uv_spawn support. */
typedef gid_t uv_gid_t;
/* Platform-specific definitions for uv_dlopen support. */
#define UV_DYNAMIC /* empty */
+
typedef struct {
void* handle;
char* errmsg;
} uv_lib_t;
-struct uv__io_s;
-struct uv_loop_s;
-
-typedef struct uv__io_s uv__io_t;
-typedef void (*uv__io_cb)(struct uv_loop_s* loop, uv__io_t* handle, int events);
-
-struct uv__io_s {
- ev_io io_watcher;
-};
-
-#define UV_REQ_TYPE_PRIVATE /* empty */
-
-#if __linux__
-# define UV_LOOP_PRIVATE_PLATFORM_FIELDS \
- uv__io_t inotify_read_watcher; \
- void* inotify_watchers; \
- int inotify_fd;
-#elif defined(PORT_SOURCE_FILE)
-# define UV_LOOP_PRIVATE_PLATFORM_FIELDS \
- uv__io_t fs_event_watcher; \
- int fs_fd;
-#else
-# define UV_LOOP_PRIVATE_PLATFORM_FIELDS
-#endif
-
#define UV_LOOP_PRIVATE_FIELDS \
+ unsigned long flags; \
/* Poll result queue */ \
eio_channel uv_eio_channel; \
struct ev_loop* ev; \
uv_async_t uv_eio_done_poll_notifier; \
uv_idle_t uv_eio_poller; \
uv_handle_t* closing_handles; \
+ ngx_queue_t process_handles[1]; \
ngx_queue_t prepare_handles; \
ngx_queue_t check_handles; \
ngx_queue_t idle_handles; \
uv__io_t async_watcher; \
int async_pipefd[2]; \
/* RB_HEAD(uv__timers, uv_timer_s) */ \
- struct uv__timers { struct uv_timer_s* rbh_root; } timer_handles; \
+ struct uv__timers { \
+ struct uv_timer_s* rbh_root; \
+ } timer_handles; \
uint64_t time; \
- UV_LOOP_PRIVATE_PLATFORM_FIELDS
+ void* signal_ctx; \
+ uv_signal_t child_watcher; \
+ UV_PLATFORM_LOOP_FIELDS \
-#define UV_REQ_BUFSML_SIZE (4)
+#define UV_REQ_TYPE_PRIVATE /* empty */
#define UV_REQ_PRIVATE_FIELDS /* empty */
-#define UV_WRITE_PRIVATE_FIELDS \
- ngx_queue_t queue; \
- int write_index; \
- uv_buf_t* bufs; \
- int bufcnt; \
- int error; \
- uv_buf_t bufsml[UV_REQ_BUFSML_SIZE];
-
-#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */
-
-#define UV_CONNECT_PRIVATE_FIELDS \
- ngx_queue_t queue;
-
-#define UV_UDP_SEND_PRIVATE_FIELDS \
- ngx_queue_t queue; \
- struct sockaddr_in6 addr; \
- int bufcnt; \
- uv_buf_t* bufs; \
- ssize_t status; \
- uv_udp_send_cb send_cb; \
- uv_buf_t bufsml[UV_REQ_BUFSML_SIZE]; \
-
#define UV_PRIVATE_REQ_TYPES /* empty */
+#define UV_WRITE_PRIVATE_FIELDS \
+ ngx_queue_t queue; \
+ int write_index; \
+ uv_buf_t* bufs; \
+ int bufcnt; \
+ int error; \
+ uv_buf_t bufsml[4]; \
-/* TODO: union or classes please! */
-#define UV_HANDLE_PRIVATE_FIELDS \
- int flags; \
- uv_handle_t* next_closing; \
-
-
-#define UV_STREAM_PRIVATE_FIELDS \
- uv_connect_t *connect_req; \
- uv_shutdown_t *shutdown_req; \
- uv__io_t read_watcher; \
- uv__io_t write_watcher; \
- ngx_queue_t write_queue; \
- ngx_queue_t write_completed_queue; \
- uv_connection_cb connection_cb; \
- int delayed_error; \
- int accepted_fd; \
- int fd; \
-
-
-/* UV_TCP, idle_handle is for UV_TCP_SINGLE_ACCEPT handles */
-#define UV_TCP_PRIVATE_FIELDS \
- uv_idle_t* idle_handle; \
-
-
-/* UV_UDP */
-#define UV_UDP_PRIVATE_FIELDS \
- int fd; \
- uv_alloc_cb alloc_cb; \
- uv_udp_recv_cb recv_cb; \
- uv__io_t read_watcher; \
- uv__io_t write_watcher; \
- ngx_queue_t write_queue; \
- ngx_queue_t write_completed_queue; \
+#define UV_CONNECT_PRIVATE_FIELDS \
+ ngx_queue_t queue; \
+#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */
-/* UV_NAMED_PIPE */
-#define UV_PIPE_PRIVATE_FIELDS \
+#define UV_UDP_SEND_PRIVATE_FIELDS \
+ ngx_queue_t queue; \
+ struct sockaddr_in6 addr; \
+ int bufcnt; \
+ uv_buf_t* bufs; \
+ ssize_t status; \
+ uv_udp_send_cb send_cb; \
+ uv_buf_t bufsml[4]; \
+
+#define UV_HANDLE_PRIVATE_FIELDS \
+ int flags; \
+ uv_handle_t* next_closing; \
+
+#define UV_STREAM_PRIVATE_FIELDS \
+ uv_connect_t *connect_req; \
+ uv_shutdown_t *shutdown_req; \
+ uv__io_t read_watcher; \
+ uv__io_t write_watcher; \
+ ngx_queue_t write_queue; \
+ ngx_queue_t write_completed_queue; \
+ uv_connection_cb connection_cb; \
+ int delayed_error; \
+ int accepted_fd; \
+ int fd; \
+
+#define UV_TCP_PRIVATE_FIELDS \
+ uv_idle_t* idle_handle; /* for UV_TCP_SINGLE_ACCEPT handles */ \
+
+#define UV_UDP_PRIVATE_FIELDS \
+ int fd; \
+ uv_alloc_cb alloc_cb; \
+ uv_udp_recv_cb recv_cb; \
+ uv__io_t read_watcher; \
+ uv__io_t write_watcher; \
+ ngx_queue_t write_queue; \
+ ngx_queue_t write_completed_queue; \
+
+#define UV_PIPE_PRIVATE_FIELDS \
const char* pipe_fname; /* strdup'ed */
-
-/* UV_POLL */
-#define UV_POLL_PRIVATE_FIELDS \
- int fd; \
+#define UV_POLL_PRIVATE_FIELDS \
+ int fd; \
uv__io_t io_watcher;
-
-/* UV_PREPARE */
-#define UV_PREPARE_PRIVATE_FIELDS \
- uv_prepare_cb prepare_cb; \
+#define UV_PREPARE_PRIVATE_FIELDS \
+ uv_prepare_cb prepare_cb; \
ngx_queue_t queue;
-
-/* UV_CHECK */
-#define UV_CHECK_PRIVATE_FIELDS \
- uv_check_cb check_cb; \
+#define UV_CHECK_PRIVATE_FIELDS \
+ uv_check_cb check_cb; \
ngx_queue_t queue;
-
-/* UV_IDLE */
-#define UV_IDLE_PRIVATE_FIELDS \
- uv_idle_cb idle_cb; \
+#define UV_IDLE_PRIVATE_FIELDS \
+ uv_idle_cb idle_cb; \
ngx_queue_t queue;
-
-/* UV_ASYNC */
#define UV_ASYNC_PRIVATE_FIELDS \
volatile sig_atomic_t pending; \
uv_async_cb async_cb; \
ngx_queue_t queue;
-
-/* UV_TIMER */
#define UV_TIMER_PRIVATE_FIELDS \
/* RB_ENTRY(uv_timer_s) node; */ \
struct { \
uint64_t timeout; \
uint64_t repeat;
-#define UV_GETADDRINFO_PRIVATE_FIELDS \
- uv_getaddrinfo_cb cb; \
- struct addrinfo* hints; \
- char* hostname; \
- char* service; \
- struct addrinfo* res; \
+#define UV_GETADDRINFO_PRIVATE_FIELDS \
+ uv_getaddrinfo_cb cb; \
+ struct addrinfo* hints; \
+ char* hostname; \
+ char* service; \
+ struct addrinfo* res; \
int retcode;
-#define UV_PROCESS_PRIVATE_FIELDS \
- ev_child child_watcher;
+#define UV_PROCESS_PRIVATE_FIELDS \
+ ngx_queue_t queue; \
+ int errorno; \
-#define UV_FS_PRIVATE_FIELDS \
- struct stat statbuf; \
- eio_req* eio;
+#define UV_FS_PRIVATE_FIELDS \
+ struct stat statbuf; \
+ uv_file file; \
+ eio_req* eio; \
-#define UV_WORK_PRIVATE_FIELDS \
+#define UV_WORK_PRIVATE_FIELDS \
eio_req* eio;
-#define UV_TTY_PRIVATE_FIELDS \
- struct termios orig_termios; \
+#define UV_TTY_PRIVATE_FIELDS \
+ struct termios orig_termios; \
int mode;
-/* UV_FS_EVENT_PRIVATE_FIELDS */
-#if defined(__linux__)
-
-#define UV_FS_EVENT_PRIVATE_FIELDS \
- ngx_queue_t watchers; \
- uv_fs_event_cb cb; \
- int wd; \
- void* pad0; \
- void* pad1; \
-
-#elif defined(__APPLE__) \
- || defined(__FreeBSD__) \
- || defined(__DragonFly__) \
- || defined(__OpenBSD__) \
- || defined(__NetBSD__)
-
-#define UV_FS_EVENT_PRIVATE_FIELDS \
- ev_io event_watcher; \
- uv_fs_event_cb cb; \
- int fflags; \
- int fd;
-
-#elif defined(__sun)
-
-#ifdef PORT_SOURCE_FILE
-# define UV_FS_EVENT_PRIVATE_FIELDS \
- uv_fs_event_cb cb; \
- file_obj_t fo; \
- int fd;
-#else /* !PORT_SOURCE_FILE */
-# define UV_FS_EVENT_PRIVATE_FIELDS
-#endif
-
-#else
-
-/* Stub for platforms where the file watcher isn't implemented yet. */
-#define UV_FS_EVENT_PRIVATE_FIELDS
+#define UV_SIGNAL_PRIVATE_FIELDS \
+ ngx_queue_t queue;
-#endif
+#define UV_FS_EVENT_PRIVATE_FIELDS \
+ uv_fs_event_cb cb; \
+ UV_PLATFORM_FS_EVENT_FIELDS \
#endif /* UV_UNIX_H */
# define _SSIZE_T_DEFINED
#endif
-#include <process.h>
-#include <stdint.h>
#include <winsock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <windows.h>
+
+#include <process.h>
+#include <signal.h>
+#include <stdint.h>
#include <sys/stat.h>
#include "tree.h"
#define MAX_PIPENAME_LEN 256
#ifndef S_IFLNK
-# define S_IFLNK 0xA000
+# define S_IFLNK 0xA000
#endif
+/* Additional signals supported by uv_signal and or uv_kill. The CRT defines
+ * the following signals already:
+ *
+ * #define SIGINT 2
+ * #define SIGILL 4
+ * #define SIGABRT_COMPAT 6
+ * #define SIGFPE 8
+ * #define SIGSEGV 11
+ * #define SIGTERM 15
+ * #define SIGBREAK 21
+ * #define SIGABRT 22
+ *
+ * The additional signals have values that are common on other Unix
+ * variants (Linux and Darwin)
+ */
+#define SIGHUP 1
+#define SIGKILL 9
+
/*
* Guids and typedefs for winsock extension functions
* Mingw32 doesn't have these :-(
*/
#ifndef WSAID_ACCEPTEX
-# define WSAID_ACCEPTEX \
- {0xb5367df1, 0xcbac, 0x11cf, \
+# define WSAID_ACCEPTEX \
+ {0xb5367df1, 0xcbac, 0x11cf, \
{0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
-# define WSAID_CONNECTEX \
- {0x25a207b9, 0xddf3, 0x4660, \
+# define WSAID_CONNECTEX \
+ {0x25a207b9, 0xddf3, 0x4660, \
{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}}
-# define WSAID_GETACCEPTEXSOCKADDRS \
- {0xb5367df2, 0xcbac, 0x11cf, \
+# define WSAID_GETACCEPTEXSOCKADDRS \
+ {0xb5367df2, 0xcbac, 0x11cf, \
{0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
-# define WSAID_DISCONNECTEX \
- {0x7fda2e11, 0x8630, 0x436f, \
+# define WSAID_DISCONNECTEX \
+ {0x7fda2e11, 0x8630, 0x436f, \
{0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}}
-# define WSAID_TRANSMITFILE \
- {0xb5367df0, 0xcbac, 0x11cf, \
+# define WSAID_TRANSMITFILE \
+ {0xb5367df0, 0xcbac, 0x11cf, \
{0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
typedef BOOL PASCAL (*LPFN_ACCEPTEX)
/* Counter to keep track of active udp streams */ \
unsigned int active_udp_streams;
-#define UV_REQ_TYPE_PRIVATE \
- /* TODO: remove the req suffix */ \
- UV_ACCEPT, \
- UV_FS_EVENT_REQ, \
- UV_POLL_REQ, \
- UV_PROCESS_EXIT, \
- UV_PROCESS_CLOSE, \
- UV_READ, \
- UV_UDP_RECV, \
- UV_WAKEUP,
-
-#define UV_REQ_PRIVATE_FIELDS \
- union { \
- /* Used by I/O operations */ \
- struct { \
- OVERLAPPED overlapped; \
- size_t queued_bytes; \
- }; \
- }; \
+#define UV_REQ_TYPE_PRIVATE \
+ /* TODO: remove the req suffix */ \
+ UV_ACCEPT, \
+ UV_FS_EVENT_REQ, \
+ UV_POLL_REQ, \
+ UV_PROCESS_EXIT, \
+ UV_READ, \
+ UV_UDP_RECV, \
+ UV_WAKEUP, \
+ UV_SIGNAL_REQ,
+
+#define UV_REQ_PRIVATE_FIELDS \
+ union { \
+ /* Used by I/O operations */ \
+ struct { \
+ OVERLAPPED overlapped; \
+ size_t queued_bytes; \
+ }; \
+ }; \
struct uv_req_s* next_req;
-#define UV_WRITE_PRIVATE_FIELDS \
- int ipc_header; \
- uv_buf_t write_buffer; \
- HANDLE event_handle; \
+#define UV_WRITE_PRIVATE_FIELDS \
+ int ipc_header; \
+ uv_buf_t write_buffer; \
+ HANDLE event_handle; \
HANDLE wait_handle;
-#define UV_CONNECT_PRIVATE_FIELDS \
+#define UV_CONNECT_PRIVATE_FIELDS \
/* empty */
-#define UV_SHUTDOWN_PRIVATE_FIELDS \
+#define UV_SHUTDOWN_PRIVATE_FIELDS \
/* empty */
-#define UV_UDP_SEND_PRIVATE_FIELDS \
+#define UV_UDP_SEND_PRIVATE_FIELDS \
/* empty */
-#define UV_PRIVATE_REQ_TYPES \
- typedef struct uv_pipe_accept_s { \
- UV_REQ_FIELDS \
- HANDLE pipeHandle; \
- struct uv_pipe_accept_s* next_pending; \
- } uv_pipe_accept_t; \
- \
- typedef struct uv_tcp_accept_s { \
- UV_REQ_FIELDS \
- SOCKET accept_socket; \
- char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; \
- HANDLE event_handle; \
- HANDLE wait_handle; \
- struct uv_tcp_accept_s* next_pending; \
- } uv_tcp_accept_t; \
- \
- typedef struct uv_read_s { \
- UV_REQ_FIELDS \
- HANDLE event_handle; \
- HANDLE wait_handle; \
+#define UV_PRIVATE_REQ_TYPES \
+ typedef struct uv_pipe_accept_s { \
+ UV_REQ_FIELDS \
+ HANDLE pipeHandle; \
+ struct uv_pipe_accept_s* next_pending; \
+ } uv_pipe_accept_t; \
+ \
+ typedef struct uv_tcp_accept_s { \
+ UV_REQ_FIELDS \
+ SOCKET accept_socket; \
+ char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; \
+ HANDLE event_handle; \
+ HANDLE wait_handle; \
+ struct uv_tcp_accept_s* next_pending; \
+ } uv_tcp_accept_t; \
+ \
+ typedef struct uv_read_s { \
+ UV_REQ_FIELDS \
+ HANDLE event_handle; \
+ HANDLE wait_handle; \
} uv_read_t;
-#define uv_stream_connection_fields \
- unsigned int write_reqs_pending; \
+#define uv_stream_connection_fields \
+ unsigned int write_reqs_pending; \
uv_shutdown_t* shutdown_req;
-#define uv_stream_server_fields \
+#define uv_stream_server_fields \
uv_connection_cb connection_cb;
-#define UV_STREAM_PRIVATE_FIELDS \
- unsigned int reqs_pending; \
- int activecnt; \
- uv_read_t read_req; \
- union { \
- struct { uv_stream_connection_fields }; \
- struct { uv_stream_server_fields }; \
+#define UV_STREAM_PRIVATE_FIELDS \
+ unsigned int reqs_pending; \
+ int activecnt; \
+ uv_read_t read_req; \
+ union { \
+ struct { uv_stream_connection_fields }; \
+ struct { uv_stream_server_fields }; \
};
-#define uv_tcp_server_fields \
- uv_tcp_accept_t* accept_reqs; \
- unsigned int processed_accepts; \
- uv_tcp_accept_t* pending_accepts; \
+#define uv_tcp_server_fields \
+ uv_tcp_accept_t* accept_reqs; \
+ unsigned int processed_accepts; \
+ uv_tcp_accept_t* pending_accepts; \
LPFN_ACCEPTEX func_acceptex;
-#define uv_tcp_connection_fields \
- uv_buf_t read_buffer; \
+#define uv_tcp_connection_fields \
+ uv_buf_t read_buffer; \
LPFN_CONNECTEX func_connectex;
-#define UV_TCP_PRIVATE_FIELDS \
- SOCKET socket; \
- int bind_error; \
- union { \
- struct { uv_tcp_server_fields }; \
- struct { uv_tcp_connection_fields }; \
+#define UV_TCP_PRIVATE_FIELDS \
+ SOCKET socket; \
+ int bind_error; \
+ union { \
+ struct { uv_tcp_server_fields }; \
+ struct { uv_tcp_connection_fields }; \
};
-#define UV_UDP_PRIVATE_FIELDS \
- SOCKET socket; \
- unsigned int reqs_pending; \
- int activecnt; \
- uv_req_t recv_req; \
- uv_buf_t recv_buffer; \
- struct sockaddr_storage recv_from; \
- int recv_from_len; \
- uv_udp_recv_cb recv_cb; \
- uv_alloc_cb alloc_cb; \
- LPFN_WSARECV func_wsarecv; \
+#define UV_UDP_PRIVATE_FIELDS \
+ SOCKET socket; \
+ unsigned int reqs_pending; \
+ int activecnt; \
+ uv_req_t recv_req; \
+ uv_buf_t recv_buffer; \
+ struct sockaddr_storage recv_from; \
+ int recv_from_len; \
+ uv_udp_recv_cb recv_cb; \
+ uv_alloc_cb alloc_cb; \
+ LPFN_WSARECV func_wsarecv; \
LPFN_WSARECVFROM func_wsarecvfrom;
-#define uv_pipe_server_fields \
- int pending_instances; \
- uv_pipe_accept_t* accept_reqs; \
+#define uv_pipe_server_fields \
+ int pending_instances; \
+ uv_pipe_accept_t* accept_reqs; \
uv_pipe_accept_t* pending_accepts;
-#define uv_pipe_connection_fields \
- uv_timer_t* eof_timer; \
- uv_write_t ipc_header_write_req; \
- int ipc_pid; \
- uint64_t remaining_ipc_rawdata_bytes; \
- unsigned char reserved[sizeof(void*)]; \
- struct { \
- WSAPROTOCOL_INFOW* socket_info; \
- int tcp_connection; \
- } pending_ipc_info; \
+#define uv_pipe_connection_fields \
+ uv_timer_t* eof_timer; \
+ uv_write_t ipc_header_write_req; \
+ int ipc_pid; \
+ uint64_t remaining_ipc_rawdata_bytes; \
+ unsigned char reserved[sizeof(void*)]; \
+ struct { \
+ WSAPROTOCOL_INFOW* socket_info; \
+ int tcp_connection; \
+ } pending_ipc_info; \
uv_write_t* non_overlapped_writes_tail;
-#define UV_PIPE_PRIVATE_FIELDS \
- HANDLE handle; \
- wchar_t* name; \
- union { \
- struct { uv_pipe_server_fields }; \
- struct { uv_pipe_connection_fields }; \
+#define UV_PIPE_PRIVATE_FIELDS \
+ HANDLE handle; \
+ WCHAR* name; \
+ union { \
+ struct { uv_pipe_server_fields }; \
+ struct { uv_pipe_connection_fields }; \
};
/* TODO: put the parser states in an union - TTY handles are always */
/* half-duplex so read-state can safely overlap write-state. */
-#define UV_TTY_PRIVATE_FIELDS \
- HANDLE handle; \
- HANDLE read_line_handle; \
- uv_buf_t read_line_buffer; \
- HANDLE read_raw_wait; \
- DWORD original_console_mode; \
- /* Fields used for translating win */ \
- /* keystrokes into vt100 characters */ \
- char last_key[8]; \
- unsigned char last_key_offset; \
- unsigned char last_key_len; \
- INPUT_RECORD last_input_record; \
- WCHAR last_utf16_high_surrogate; \
- /* utf8-to-utf16 conversion state */ \
- unsigned char utf8_bytes_left; \
- unsigned int utf8_codepoint; \
- /* eol conversion state */ \
- unsigned char previous_eol; \
- /* ansi parser state */ \
- unsigned char ansi_parser_state; \
- unsigned char ansi_csi_argc; \
- unsigned short ansi_csi_argv[4]; \
- COORD saved_position; \
- WORD saved_attributes;
-
-#define UV_POLL_PRIVATE_FIELDS \
- SOCKET socket; \
- /* Used in fast mode */ \
- SOCKET peer_socket; \
- AFD_POLL_INFO afd_poll_info_1; \
- AFD_POLL_INFO afd_poll_info_2; \
- /* Used in fast and slow mode. */ \
- uv_req_t poll_req_1; \
- uv_req_t poll_req_2; \
- unsigned char submitted_events_1; \
- unsigned char submitted_events_2; \
- unsigned char mask_events_1; \
- unsigned char mask_events_2; \
+#define UV_TTY_PRIVATE_FIELDS \
+ HANDLE handle; \
+ union { \
+ struct { \
+ /* Used for readable TTY handles */ \
+ HANDLE read_line_handle; \
+ uv_buf_t read_line_buffer; \
+ HANDLE read_raw_wait; \
+ DWORD original_console_mode; \
+ /* Fields used for translating win keystrokes into vt100 characters */ \
+ char last_key[8]; \
+ unsigned char last_key_offset; \
+ unsigned char last_key_len; \
+ WCHAR last_utf16_high_surrogate; \
+ INPUT_RECORD last_input_record; \
+ }; \
+ struct { \
+ /* Used for writable TTY handles */ \
+ /* utf8-to-utf16 conversion state */ \
+ unsigned int utf8_codepoint; \
+ unsigned char utf8_bytes_left; \
+ /* eol conversion state */ \
+ unsigned char previous_eol; \
+ /* ansi parser state */ \
+ unsigned char ansi_parser_state; \
+ unsigned char ansi_csi_argc; \
+ unsigned short ansi_csi_argv[4]; \
+ COORD saved_position; \
+ WORD saved_attributes; \
+ }; \
+ };
+
+#define UV_POLL_PRIVATE_FIELDS \
+ SOCKET socket; \
+ /* Used in fast mode */ \
+ SOCKET peer_socket; \
+ AFD_POLL_INFO afd_poll_info_1; \
+ AFD_POLL_INFO afd_poll_info_2; \
+ /* Used in fast and slow mode. */ \
+ uv_req_t poll_req_1; \
+ uv_req_t poll_req_2; \
+ unsigned char submitted_events_1; \
+ unsigned char submitted_events_2; \
+ unsigned char mask_events_1; \
+ unsigned char mask_events_2; \
unsigned char events;
-#define UV_TIMER_PRIVATE_FIELDS \
- RB_ENTRY(uv_timer_s) tree_entry; \
- int64_t due; \
- int64_t repeat; \
+#define UV_TIMER_PRIVATE_FIELDS \
+ RB_ENTRY(uv_timer_s) tree_entry; \
+ int64_t due; \
+ int64_t repeat; \
uv_timer_cb timer_cb;
-#define UV_ASYNC_PRIVATE_FIELDS \
- struct uv_req_s async_req; \
- uv_async_cb async_cb; \
- /* char to avoid alignment issues */ \
+#define UV_ASYNC_PRIVATE_FIELDS \
+ struct uv_req_s async_req; \
+ uv_async_cb async_cb; \
+ /* char to avoid alignment issues */ \
char volatile async_sent;
-#define UV_PREPARE_PRIVATE_FIELDS \
- uv_prepare_t* prepare_prev; \
- uv_prepare_t* prepare_next; \
+#define UV_PREPARE_PRIVATE_FIELDS \
+ uv_prepare_t* prepare_prev; \
+ uv_prepare_t* prepare_next; \
uv_prepare_cb prepare_cb;
-#define UV_CHECK_PRIVATE_FIELDS \
- uv_check_t* check_prev; \
- uv_check_t* check_next; \
+#define UV_CHECK_PRIVATE_FIELDS \
+ uv_check_t* check_prev; \
+ uv_check_t* check_next; \
uv_check_cb check_cb;
-#define UV_IDLE_PRIVATE_FIELDS \
- uv_idle_t* idle_prev; \
- uv_idle_t* idle_next; \
+#define UV_IDLE_PRIVATE_FIELDS \
+ uv_idle_t* idle_prev; \
+ uv_idle_t* idle_next; \
uv_idle_cb idle_cb;
-#define UV_HANDLE_PRIVATE_FIELDS \
- uv_handle_t* endgame_next; \
+#define UV_HANDLE_PRIVATE_FIELDS \
+ uv_handle_t* endgame_next; \
unsigned int flags;
-#define UV_GETADDRINFO_PRIVATE_FIELDS \
- uv_getaddrinfo_cb getaddrinfo_cb; \
- void* alloc; \
- wchar_t* node; \
- wchar_t* service; \
- struct addrinfoW* hints; \
- struct addrinfoW* res; \
+#define UV_GETADDRINFO_PRIVATE_FIELDS \
+ uv_getaddrinfo_cb getaddrinfo_cb; \
+ void* alloc; \
+ WCHAR* node; \
+ WCHAR* service; \
+ struct addrinfoW* hints; \
+ struct addrinfoW* res; \
int retcode;
-#define UV_PROCESS_PRIVATE_FIELDS \
- struct uv_process_exit_s { \
- UV_REQ_FIELDS \
- } exit_req; \
- struct uv_process_close_s { \
- UV_REQ_FIELDS \
- } close_req; \
- BYTE* child_stdio_buffer; \
- int exit_signal; \
- DWORD spawn_errno; \
- HANDLE wait_handle; \
- HANDLE process_handle; \
- HANDLE close_handle;
-
-#define UV_FS_PRIVATE_FIELDS \
- int flags; \
- DWORD sys_errno_; \
- union { \
- /* TODO: remove me in 0.9. */ \
- WCHAR* pathw; \
- int fd; \
- }; \
- union { \
- struct { \
- int mode; \
- WCHAR* new_pathw; \
- int file_flags; \
- int fd_out; \
- void* buf; \
- size_t length; \
- int64_t offset; \
- }; \
- struct _stati64 stat; \
- struct { \
- double atime; \
- double mtime; \
- }; \
+#define UV_PROCESS_PRIVATE_FIELDS \
+ struct uv_process_exit_s { \
+ UV_REQ_FIELDS \
+ } exit_req; \
+ BYTE* child_stdio_buffer; \
+ uv_err_t spawn_error; \
+ int exit_signal; \
+ HANDLE wait_handle; \
+ HANDLE process_handle; \
+ volatile char exit_cb_pending;
+
+#define UV_FS_PRIVATE_FIELDS \
+ int flags; \
+ DWORD sys_errno_; \
+ union { \
+ /* TODO: remove me in 0.9. */ \
+ WCHAR* pathw; \
+ int fd; \
+ }; \
+ union { \
+ struct { \
+ int mode; \
+ WCHAR* new_pathw; \
+ int file_flags; \
+ int fd_out; \
+ void* buf; \
+ size_t length; \
+ int64_t offset; \
+ }; \
+ struct _stati64 stat; \
+ struct { \
+ double atime; \
+ double mtime; \
+ }; \
};
-#define UV_WORK_PRIVATE_FIELDS \
-
-#define UV_FS_EVENT_PRIVATE_FIELDS \
- struct uv_fs_event_req_s { \
- UV_REQ_FIELDS \
- } req; \
- HANDLE dir_handle; \
- int req_pending; \
- uv_fs_event_cb cb; \
- wchar_t* filew; \
- wchar_t* short_filew; \
- wchar_t* dirw; \
+#define UV_WORK_PRIVATE_FIELDS \
+
+#define UV_FS_EVENT_PRIVATE_FIELDS \
+ struct uv_fs_event_req_s { \
+ UV_REQ_FIELDS \
+ } req; \
+ HANDLE dir_handle; \
+ int req_pending; \
+ uv_fs_event_cb cb; \
+ WCHAR* filew; \
+ WCHAR* short_filew; \
+ WCHAR* dirw; \
char* buffer;
-int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size,
+#define UV_SIGNAL_PRIVATE_FIELDS \
+ RB_ENTRY(uv_signal_s) tree_entry; \
+ struct uv_req_s signal_req; \
+ unsigned long pending_signum;
+
+int uv_utf16_to_utf8(const WCHAR* utf16Buffer, size_t utf16Size,
char* utf8Buffer, size_t utf8Size);
-int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer,
+int uv_utf8_to_utf16(const char* utf8Buffer, WCHAR* utf16Buffer,
size_t utf16Size);
* IN THE SOFTWARE.
*/
-/* See uv_loop_new for an introduction. */
+/* See http://nikhilm.github.com/uvbook/ for an introduction. */
#ifndef UV_H
#define UV_H
#include <stdint.h> /* int64_t */
#include <sys/types.h> /* size_t */
+#if defined(__SVR4) && !defined(__unix__)
+# define __unix__
+#endif
+
#if defined(__unix__) || defined(__POSIX__) || defined(__APPLE__)
# include "uv-private/uv-unix.h"
#else
#endif
/* Expand this list if necessary. */
-#define UV_ERRNO_MAP(XX) \
- XX( -1, UNKNOWN, "unknown error") \
- XX( 0, OK, "success") \
- XX( 1, EOF, "end of file") \
- XX( 2, EADDRINFO, "getaddrinfo error") \
- XX( 3, EACCES, "permission denied") \
- XX( 4, EAGAIN, "no more processes") \
- XX( 5, EADDRINUSE, "address already in use") \
- XX( 6, EADDRNOTAVAIL, "") \
- XX( 7, EAFNOSUPPORT, "") \
- XX( 8, EALREADY, "") \
- XX( 9, EBADF, "bad file descriptor") \
- XX( 10, EBUSY, "resource busy or locked") \
- XX( 11, ECONNABORTED, "software caused connection abort") \
- XX( 12, ECONNREFUSED, "connection refused") \
- XX( 13, ECONNRESET, "connection reset by peer") \
- XX( 14, EDESTADDRREQ, "destination address required") \
- XX( 15, EFAULT, "bad address in system call argument") \
- XX( 16, EHOSTUNREACH, "host is unreachable") \
- XX( 17, EINTR, "interrupted system call") \
- XX( 18, EINVAL, "invalid argument") \
- XX( 19, EISCONN, "socket is already connected") \
- XX( 20, EMFILE, "too many open files") \
- XX( 21, EMSGSIZE, "message too long") \
- XX( 22, ENETDOWN, "network is down") \
- XX( 23, ENETUNREACH, "network is unreachable") \
- XX( 24, ENFILE, "file table overflow") \
- XX( 25, ENOBUFS, "no buffer space available") \
- XX( 26, ENOMEM, "not enough memory") \
- XX( 27, ENOTDIR, "not a directory") \
- XX( 28, EISDIR, "illegal operation on a directory") \
- XX( 29, ENONET, "machine is not on the network") \
- XX( 31, ENOTCONN, "socket is not connected") \
- XX( 32, ENOTSOCK, "socket operation on non-socket") \
- XX( 33, ENOTSUP, "operation not supported on socket") \
- XX( 34, ENOENT, "no such file or directory") \
- XX( 35, ENOSYS, "function not implemented") \
- XX( 36, EPIPE, "broken pipe") \
- XX( 37, EPROTO, "protocol error") \
- XX( 38, EPROTONOSUPPORT, "protocol not supported") \
- XX( 39, EPROTOTYPE, "protocol wrong type for socket") \
- XX( 40, ETIMEDOUT, "connection timed out") \
- XX( 41, ECHARSET, "") \
- XX( 42, EAIFAMNOSUPPORT, "") \
- XX( 44, EAISERVICE, "") \
- XX( 45, EAISOCKTYPE, "") \
- XX( 46, ESHUTDOWN, "") \
- XX( 47, EEXIST, "file already exists") \
- XX( 48, ESRCH, "no such process") \
- XX( 49, ENAMETOOLONG, "name too long") \
- XX( 50, EPERM, "operation not permitted") \
- XX( 51, ELOOP, "too many symbolic links encountered") \
- XX( 52, EXDEV, "cross-device link not permitted") \
- XX( 53, ENOTEMPTY, "directory not empty") \
- XX( 54, ENOSPC, "no space left on device") \
- XX( 55, EIO, "i/o error") \
- XX( 56, EROFS, "read-only file system" ) \
- XX( 57, ENODEV, "no such device" ) \
+#define UV_ERRNO_MAP(XX) \
+ XX( -1, UNKNOWN, "unknown error") \
+ XX( 0, OK, "success") \
+ XX( 1, EOF, "end of file") \
+ XX( 2, EADDRINFO, "getaddrinfo error") \
+ XX( 3, EACCES, "permission denied") \
+ XX( 4, EAGAIN, "no more processes") \
+ XX( 5, EADDRINUSE, "address already in use") \
+ XX( 6, EADDRNOTAVAIL, "") \
+ XX( 7, EAFNOSUPPORT, "") \
+ XX( 8, EALREADY, "") \
+ XX( 9, EBADF, "bad file descriptor") \
+ XX( 10, EBUSY, "resource busy or locked") \
+ XX( 11, ECONNABORTED, "software caused connection abort") \
+ XX( 12, ECONNREFUSED, "connection refused") \
+ XX( 13, ECONNRESET, "connection reset by peer") \
+ XX( 14, EDESTADDRREQ, "destination address required") \
+ XX( 15, EFAULT, "bad address in system call argument") \
+ XX( 16, EHOSTUNREACH, "host is unreachable") \
+ XX( 17, EINTR, "interrupted system call") \
+ XX( 18, EINVAL, "invalid argument") \
+ XX( 19, EISCONN, "socket is already connected") \
+ XX( 20, EMFILE, "too many open files") \
+ XX( 21, EMSGSIZE, "message too long") \
+ XX( 22, ENETDOWN, "network is down") \
+ XX( 23, ENETUNREACH, "network is unreachable") \
+ XX( 24, ENFILE, "file table overflow") \
+ XX( 25, ENOBUFS, "no buffer space available") \
+ XX( 26, ENOMEM, "not enough memory") \
+ XX( 27, ENOTDIR, "not a directory") \
+ XX( 28, EISDIR, "illegal operation on a directory") \
+ XX( 29, ENONET, "machine is not on the network") \
+ XX( 31, ENOTCONN, "socket is not connected") \
+ XX( 32, ENOTSOCK, "socket operation on non-socket") \
+ XX( 33, ENOTSUP, "operation not supported on socket") \
+ XX( 34, ENOENT, "no such file or directory") \
+ XX( 35, ENOSYS, "function not implemented") \
+ XX( 36, EPIPE, "broken pipe") \
+ XX( 37, EPROTO, "protocol error") \
+ XX( 38, EPROTONOSUPPORT, "protocol not supported") \
+ XX( 39, EPROTOTYPE, "protocol wrong type for socket") \
+ XX( 40, ETIMEDOUT, "connection timed out") \
+ XX( 41, ECHARSET, "") \
+ XX( 42, EAIFAMNOSUPPORT, "") \
+ XX( 44, EAISERVICE, "") \
+ XX( 45, EAISOCKTYPE, "") \
+ XX( 46, ESHUTDOWN, "") \
+ XX( 47, EEXIST, "file already exists") \
+ XX( 48, ESRCH, "no such process") \
+ XX( 49, ENAMETOOLONG, "name too long") \
+ XX( 50, EPERM, "operation not permitted") \
+ XX( 51, ELOOP, "too many symbolic links encountered") \
+ XX( 52, EXDEV, "cross-device link not permitted") \
+ XX( 53, ENOTEMPTY, "directory not empty") \
+ XX( 54, ENOSPC, "no space left on device") \
+ XX( 55, EIO, "i/o error") \
+ XX( 56, EROFS, "read-only file system" ) \
+ XX( 57, ENODEV, "no such device" ) \
XX( 58, ECANCELED, "operation canceled" )
} uv_err_code;
#undef UV_ERRNO_GEN
-#define UV_HANDLE_TYPE_MAP(XX) \
- XX(ASYNC, async) \
- XX(CHECK, check) \
- XX(FS_EVENT, fs_event) \
- XX(FS_POLL, fs_poll) \
- XX(IDLE, idle) \
- XX(NAMED_PIPE, pipe) \
- XX(POLL, poll) \
- XX(PREPARE, prepare) \
- XX(PROCESS, process) \
- XX(TCP, tcp) \
- XX(TIMER, timer) \
- XX(TTY, tty) \
- XX(UDP, udp) \
-
-#define UV_REQ_TYPE_MAP(XX) \
- XX(CONNECT, connect) \
- XX(WRITE, write) \
- XX(SHUTDOWN, shutdown) \
- XX(UDP_SEND, udp_send) \
- XX(FS, fs) \
- XX(WORK, work) \
- XX(GETADDRINFO, getaddrinfo) \
+#define UV_HANDLE_TYPE_MAP(XX) \
+ XX(ASYNC, async) \
+ XX(CHECK, check) \
+ XX(FS_EVENT, fs_event) \
+ XX(FS_POLL, fs_poll) \
+ XX(IDLE, idle) \
+ XX(NAMED_PIPE, pipe) \
+ XX(POLL, poll) \
+ XX(PREPARE, prepare) \
+ XX(PROCESS, process) \
+ XX(TCP, tcp) \
+ XX(TIMER, timer) \
+ XX(TTY, tty) \
+ XX(UDP, udp) \
+ XX(SIGNAL, signal) \
+
+#define UV_REQ_TYPE_MAP(XX) \
+ XX(CONNECT, connect) \
+ XX(WRITE, write) \
+ XX(SHUTDOWN, shutdown) \
+ XX(UDP_SEND, udp_send) \
+ XX(FS, fs) \
+ XX(WORK, work) \
+ XX(GETADDRINFO, getaddrinfo) \
typedef enum {
UV_UNKNOWN_HANDLE = 0,
typedef struct uv_process_s uv_process_t;
typedef struct uv_fs_event_s uv_fs_event_t;
typedef struct uv_fs_poll_s uv_fs_poll_t;
+typedef struct uv_signal_s uv_signal_t;
/* Request types. */
typedef struct uv_req_s uv_req_t;
const uv_statbuf_t* prev,
const uv_statbuf_t* curr);
+typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum);
+
+
typedef enum {
UV_LEAVE_GROUP = 0,
UV_JOIN_GROUP
UV_EXTERN const char* uv_err_name(uv_err_t err);
-#define UV_REQ_FIELDS \
- /* public */ \
- void* data; \
- /* private */ \
- ngx_queue_t active_queue; \
- UV_REQ_PRIVATE_FIELDS \
- /* read-only */ \
- uv_req_type type; \
+#define UV_REQ_FIELDS \
+ /* public */ \
+ void* data; \
+ /* private */ \
+ ngx_queue_t active_queue; \
+ UV_REQ_PRIVATE_FIELDS \
+ /* read-only */ \
+ uv_req_type type; \
/* Abstract base class of all requests. */
struct uv_req_s {
UV_EXTERN size_t uv_strlcat(char* dst, const char* src, size_t size);
-#define UV_STREAM_FIELDS \
- /* number of bytes queued for writing */ \
- size_t write_queue_size; \
- uv_alloc_cb alloc_cb; \
- uv_read_cb read_cb; \
- uv_read2_cb read2_cb; \
- /* private */ \
+#define UV_STREAM_FIELDS \
+ /* number of bytes queued for writing */ \
+ size_t write_queue_size; \
+ uv_alloc_cb alloc_cb; \
+ uv_read_cb read_cb; \
+ uv_read2_cb read2_cb; \
+ /* private */ \
UV_STREAM_PRIVATE_FIELDS
/*
/*
* uv_timer_t is a subclass of uv_handle_t.
*
- * Wraps libev's ev_timer watcher. Used to get woken up at a specified time
- * in the future.
+ * Used to get woken up at a specified time in the future.
*/
struct uv_timer_s {
UV_HANDLE_FIELDS
UV_EXTERN int uv_timer_init(uv_loop_t*, uv_timer_t* timer);
-UV_EXTERN int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb,
- int64_t timeout, int64_t repeat);
+/*
+ * Start the timer. `timeout` and `repeat` are in milliseconds.
+ *
+ * If timeout is zero, the callback fires on the next tick of the event loop.
+ *
+ * If repeat is non-zero, the callback fires first after timeout milliseconds
+ * and then repeatedly after repeat milliseconds.
+ *
+ * timeout and repeat are signed integers but that will change in a future
+ * version of libuv. Don't pass in negative values, you'll get a nasty surprise
+ * when that change becomes effective.
+ */
+UV_EXTERN int uv_timer_start(uv_timer_t* timer,
+ uv_timer_cb cb,
+ int64_t timeout,
+ int64_t repeat);
UV_EXTERN int uv_timer_stop(uv_timer_t* timer);
UV_EXTERN int uv_timer_again(uv_timer_t* timer);
/*
- * Set the repeat value. Note that if the repeat value is set from a timer
- * callback it does not immediately take effect. If the timer was non-repeating
- * before, it will have been stopped. If it was repeating, then the old repeat
- * value will have been used to schedule the next timeout.
+ * Set the repeat value in milliseconds. Note that if the repeat value is set
+ * from a timer callback it does not immediately take effect. If the timer was
+ * non-repeating before, it will have been stopped. If it was repeating, then
+ * the old repeat value will have been used to schedule the next timeout.
*/
UV_EXTERN void uv_timer_set_repeat(uv_timer_t* timer, int64_t repeat);
struct uv_fs_poll_s {
UV_HANDLE_FIELDS
/* Private, don't touch. */
- int busy_polling; /* TODO(bnoordhuis) Fold into flags field. */
- unsigned int interval;
- uint64_t start_time;
- char* path;
- uv_fs_poll_cb poll_cb;
- uv_timer_t timer_handle;
- uv_fs_t* fs_req;
- uv_statbuf_t statbuf;
+ void* poll_ctx;
+ /* v0.8 ABI compatibility */
+ char padding[sizeof(int)
+ + sizeof(unsigned int)
+ + sizeof(uint64_t)
+ + sizeof(char*)
+ + sizeof(uv_fs_poll_cb)
+ + sizeof(uv_timer_t)
+ + sizeof(uv_fs_t*)
+ + sizeof(uv_statbuf_t)
+ - sizeof(void*)];
};
UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle);
UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle);
+
+/*
+ * UNIX signal handling on a per-event loop basis. The implementation is not
+ * ultra efficient so don't go creating a million event loops with a million
+ * signal watchers.
+ *
+ * TODO(bnoordhuis) As of 2012-08-10 only the default event loop supports
+ * signals. That will be fixed.
+ *
+ * Some signal support is available on Windows:
+ *
+ * SIGINT is normally delivered when the user presses CTRL+C. However, like
+ * on Unix, it is not generated when terminal raw mode is enabled.
+ *
+ * SIGBREAK is delivered when the user pressed CTRL+BREAK.
+ *
+ * SIGHUP is generated when the user closes the console window. On SIGHUP the
+ * program is given approximately 10 seconds to perform cleanup. After that
+ * Windows will unconditionally terminate it.
+ *
+ * Watchers for other signals can be successfully created, but these signals
+ * are never generated. These signals are: SIGILL, SIGABRT, SIGFPE, SIGSEGV,
+ * SIGTERM and SIGKILL.
+ *
+ * Note that calls to raise() or abort() to programmatically raise a signal are
+ * not detected by libuv; these will not trigger a signal watcher.
+ */
+struct uv_signal_s {
+ UV_HANDLE_FIELDS
+ uv_signal_cb signal_cb;
+ UV_SIGNAL_PRIVATE_FIELDS
+ int signum;
+};
+
+/* These functions are no-ops on Windows. */
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle);
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum);
+int uv_signal_stop(uv_signal_t* handle);
+
+
/*
* Gets load avg
* See: http://en.wikipedia.org/wiki/Load_(computing)
struct uv_loop_s {
UV_LOOP_PRIVATE_FIELDS
- /* Diagnostic counters */
- uv_counters_t counters;
/* The last error */
uv_err_t last_err;
/* Loop reference counting */
#undef UV_FS_REQ_PRIVATE_FIELDS
#undef UV_WORK_PRIVATE_FIELDS
#undef UV_FS_EVENT_PRIVATE_FIELDS
+#undef UV_SIGNAL_PRIVATE_FIELDS
#undef UV_LOOP_PRIVATE_FIELDS
#undef UV_LOOP_PRIVATE_PLATFORM_FIELDS
#include <stdlib.h>
#include <string.h>
+struct poll_ctx {
+ uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */
+ int busy_polling;
+ unsigned int interval;
+ uint64_t start_time;
+ uv_loop_t* loop;
+ uv_fs_poll_cb poll_cb;
+ uv_timer_t timer_handle;
+ uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
+ uv_statbuf_t statbuf;
+ char path[1]; /* variable length */
+};
+
static int statbuf_eq(const uv_statbuf_t* a, const uv_statbuf_t* b);
-static void timer_cb(uv_timer_t* timer, int status);
static void poll_cb(uv_fs_t* req);
+static void timer_cb(uv_timer_t* timer, int status);
+static void timer_close_cb(uv_handle_t* handle);
static uv_statbuf_t zero_statbuf;
int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
- /* TODO(bnoordhuis) Mark fs_req internal. */
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
- loop->counters.fs_poll_init++;
-
- if (uv_timer_init(loop, &handle->timer_handle))
- return -1;
-
- handle->timer_handle.flags |= UV__HANDLE_INTERNAL;
- uv__handle_unref(&handle->timer_handle);
-
return 0;
}
uv_fs_poll_cb cb,
const char* path,
unsigned int interval) {
- uv_fs_t* req;
+ struct poll_ctx* ctx;
+ uv_loop_t* loop;
size_t len;
if (uv__is_active(handle))
return 0;
- len = strlen(path) + 1;
- req = malloc(sizeof(*req) + len);
+ loop = handle->loop;
+ len = strlen(path);
+ ctx = calloc(1, sizeof(*ctx) + len);
+
+ if (ctx == NULL)
+ return uv__set_artificial_error(loop, UV_ENOMEM);
- if (req == NULL)
- return uv__set_artificial_error(handle->loop, UV_ENOMEM);
+ ctx->loop = loop;
+ ctx->poll_cb = cb;
+ ctx->interval = interval ? interval : 1;
+ ctx->start_time = uv_now(loop);
+ ctx->parent_handle = handle;
+ memcpy(ctx->path, path, len + 1);
- req->data = handle;
- handle->path = memcpy(req + 1, path, len);
- handle->fs_req = req;
- handle->poll_cb = cb;
- handle->interval = interval ? interval : 1;
- handle->start_time = uv_now(handle->loop);
- handle->busy_polling = 0;
- memset(&handle->statbuf, 0, sizeof(handle->statbuf));
+ if (uv_timer_init(loop, &ctx->timer_handle))
+ abort();
+
+ ctx->timer_handle.flags |= UV__HANDLE_INTERNAL;
+ uv__handle_unref(&ctx->timer_handle);
- if (uv_fs_stat(handle->loop, handle->fs_req, handle->path, poll_cb))
+ if (uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb))
abort();
+ handle->poll_ctx = ctx;
uv__handle_start(handle);
return 0;
int uv_fs_poll_stop(uv_fs_poll_t* handle) {
+ struct poll_ctx* ctx;
+
if (!uv__is_active(handle))
return 0;
- /* Don't free the fs req if it's active. Signal poll_cb that it needs to free
- * the req by removing the handle backlink.
- *
- * TODO(bnoordhuis) Have uv-unix postpone the close callback until the req
- * finishes so we don't need this pointer / lifecycle hackery. The callback
- * always runs on the next tick now.
- */
- if (handle->fs_req->data)
- handle->fs_req->data = NULL;
- else
- free(handle->fs_req);
+ ctx = handle->poll_ctx;
+ assert(ctx != NULL);
+ assert(ctx->parent_handle != NULL);
- handle->fs_req = NULL;
- handle->path = NULL;
+ ctx->parent_handle = NULL;
+ uv_timer_stop(&ctx->timer_handle);
- uv_timer_stop(&handle->timer_handle);
+ handle->poll_ctx = NULL;
uv__handle_stop(handle);
return 0;
void uv__fs_poll_close(uv_fs_poll_t* handle) {
uv_fs_poll_stop(handle);
- uv_close((uv_handle_t*)&handle->timer_handle, NULL);
}
static void timer_cb(uv_timer_t* timer, int status) {
- uv_fs_poll_t* handle;
+ struct poll_ctx* ctx;
- handle = container_of(timer, uv_fs_poll_t, timer_handle);
- handle->start_time = uv_now(handle->loop);
- handle->fs_req->data = handle;
+ ctx = container_of(timer, struct poll_ctx, timer_handle);
- if (uv_fs_stat(handle->loop, handle->fs_req, handle->path, poll_cb))
- abort();
+ if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */
+ uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+ return;
+ }
- assert(uv__is_active(handle));
+ assert(ctx->parent_handle->poll_ctx == ctx);
+ ctx->start_time = uv_now(ctx->loop);
+
+ if (uv_fs_stat(ctx->loop, &ctx->fs_req, ctx->path, poll_cb))
+ abort();
}
static void poll_cb(uv_fs_t* req) {
uv_statbuf_t* statbuf;
- uv_fs_poll_t* handle;
+ struct poll_ctx* ctx;
uint64_t interval;
- handle = req->data;
-
- if (handle == NULL) /* Handle has been stopped or closed. */
- goto out;
+ ctx = container_of(req, struct poll_ctx, fs_req);
- assert(req == handle->fs_req);
+ if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */
+ uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+ uv_fs_req_cleanup(req);
+ return;
+ }
if (req->result != 0) {
- if (handle->busy_polling != -req->errorno) {
- uv__set_artificial_error(handle->loop, req->errorno);
- handle->poll_cb(handle, -1, &handle->statbuf, &zero_statbuf);
- handle->busy_polling = -req->errorno;
+ if (ctx->busy_polling != -req->errorno) {
+ uv__set_artificial_error(ctx->loop, req->errorno);
+ ctx->poll_cb(ctx->parent_handle, -1, &ctx->statbuf, &zero_statbuf);
+ ctx->busy_polling = -req->errorno;
}
goto out;
}
statbuf = req->ptr;
- if (handle->busy_polling != 0)
- if (handle->busy_polling < 0 || !statbuf_eq(&handle->statbuf, statbuf))
- handle->poll_cb(handle, 0, &handle->statbuf, statbuf);
+ if (ctx->busy_polling != 0)
+ if (ctx->busy_polling < 0 || !statbuf_eq(&ctx->statbuf, statbuf))
+ ctx->poll_cb(ctx->parent_handle, 0, &ctx->statbuf, statbuf);
- handle->statbuf = *statbuf;
- handle->busy_polling = 1;
+ ctx->statbuf = *statbuf;
+ ctx->busy_polling = 1;
out:
uv_fs_req_cleanup(req);
- if (req->data == NULL) { /* Handle has been stopped or closed. */
- free(req);
- return;
- }
-
- req->data = NULL; /* Tell uv_fs_poll_stop() it's safe to free the req. */
-
/* Reschedule timer, subtract the delay from doing the stat(). */
- interval = handle->interval;
- interval -= (uv_now(handle->loop) - handle->start_time) % interval;
+ interval = ctx->interval;
+ interval -= (uv_now(ctx->loop) - ctx->start_time) % interval;
- if (uv_timer_start(&handle->timer_handle, timer_cb, interval, 0))
+ if (uv_timer_start(&ctx->timer_handle, timer_cb, interval, 0))
abort();
}
+static void timer_close_cb(uv_handle_t* handle) {
+ free(container_of(handle, struct poll_ctx, timer_handle));
+}
+
+
static int statbuf_eq(const uv_statbuf_t* a, const uv_statbuf_t* b) {
#ifdef _WIN32
return a->st_mtime == b->st_mtime
return uv__set_sys_error(loop, errno);
uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
- loop->counters.async_init++;
-
handle->async_cb = async_cb;
handle->pending = 0;
uv__fs_poll_close((uv_fs_poll_t*)handle);
break;
+ case UV_SIGNAL:
+ uv__signal_close((uv_signal_t*)handle);
+ break;
+
default:
assert(0);
}
case UV_FS_EVENT:
case UV_FS_POLL:
case UV_POLL:
+ case UV_SIGNAL:
break;
case UV_NAMED_PIPE:
#define NANOSEC ((uint64_t) 1e9)
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
uint64_t uv_hrtime() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return (ts.tv_sec * NANOSEC + ts.tv_nsec);
+ return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
void uv_loadavg(double avg[3]) {
const char* filename,
uv_fs_event_cb cb,
int flags) {
- loop->counters.fs_event_init++;
uv__set_sys_error(loop, ENOSYS);
return -1;
}
static char *process_title;
+
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
#if TARGET_OS_IPHONE
/* see: http://developer.apple.com/library/mac/#qa/qa1398/_index.html */
uint64_t uv_hrtime() {
static char *process_title;
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
uint64_t uv_hrtime(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return (ts.tv_sec * NANOSEC + ts.tv_nsec);
+ return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
req->result = 0;
req->ptr = NULL;
req->path = path ? strdup(path) : NULL;
+ req->file = -1;
req->errorno = 0;
req->eio = NULL;
if (req->cb)
uv__req_unregister(req->loop, req);
- free(req->path);
+ free((void*)req->path);
req->path = NULL;
switch (req->fs_type) {
}
+#if defined(__APPLE__) && defined(F_FULLFSYNC)
+ssize_t uv__fs_fdatasync(uv_file file) {
+ return fcntl(file, F_FULLFSYNC);
+}
+
+
+void uv__fs_fdatasync_work(eio_req* eio) {
+ uv_fs_t* req = eio->data;
+
+ eio->result = uv__fs_fdatasync(req->file);
+}
+#endif
+
+
int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
char* path = NULL;
#if defined(__FreeBSD__) \
* do a full fsync instead.
*/
WRAP_EIO(UV_FS_FDATASYNC, eio_fdatasync, fsync, ARGS1(file))
+#elif defined(__APPLE__) && defined(F_FULLFSYNC)
+ /* OSX >= 10.6 does have fdatasync, but better use fcntl anyway */
+ uv_fs_req_init(loop, req, UV_FS_FDATASYNC, path, cb);
+ req->file = file;
+
+ if (cb) {
+ /* async */
+ req->eio = eio_custom(uv__fs_fdatasync_work,
+ EIO_PRI_DEFAULT,
+ uv__fs_after,
+ req,
+ &loop->uv_eio_channel);
+ if (req->eio == NULL) {
+ uv__set_sys_error(loop, ENOMEM);
+ return -1;
+ }
+ } else {
+ /* sync */
+ req->result = uv__fs_fdatasync(file);
+ if (req->result) {
+ uv__set_sys_error(loop, errno);
+ }
+ return req->result;
+ }
+ return 0;
#else
WRAP_EIO(UV_FS_FDATASYNC, eio_fdatasync, fdatasync, ARGS1(file))
#endif
#define UV__IO_WRITE EV_WRITE
#define UV__IO_ERROR EV_ERROR
-/* flags */
+/* handle flags */
enum {
UV_CLOSING = 0x01, /* uv_close() called but not finished. */
UV_CLOSED = 0x02, /* close(2) finished. */
UV_TCP_SINGLE_ACCEPT = 0x400 /* Only accept() when idle. */
};
+/* loop flags */
+enum {
+ UV_LOOP_EIO_INITIALIZED = 1
+};
+
inline static void uv__req_init(uv_loop_t* loop,
uv_req_t* req,
uv_req_type type) {
- loop->counters.req_init++;
req->type = type;
uv__req_register(loop, req);
}
void uv__run_timers(uv_loop_t* loop);
unsigned int uv__next_timeout(uv_loop_t* loop);
+/* signal */
+void uv__signal_close(uv_signal_t* handle);
+void uv__signal_unregister(uv_loop_t* loop);
+
+/* platform specific */
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop);
+void uv__platform_loop_delete(uv_loop_t* loop);
+
/* various */
void uv__async_close(uv_async_t* handle);
void uv__check_close(uv_check_t* handle);
int flags) {
int fd;
- loop->counters.fs_event_init++;
-
/* We don't support any flags yet. */
assert(!flags);
int events;
int wd;
- loop->counters.fs_event_init++;
-
/* We don't support any flags yet. */
assert(!flags);
size_t len;
} process_title;
+static void read_models(unsigned int numcpus, uv_cpu_info_t* ci);
+static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
+static void read_times(unsigned int numcpus, uv_cpu_info_t* ci);
+static unsigned long read_cpufreq(unsigned int cpunum);
+
+
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ loop->inotify_watchers = NULL;
+ loop->inotify_fd = -1;
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+ if (loop->inotify_fd == -1) return;
+ uv__io_stop(loop, &loop->inotify_read_watcher);
+ close(loop->inotify_fd);
+ loop->inotify_fd = -1;
+}
+
-/*
- * There's probably some way to get time from Linux than gettimeofday(). What
- * it is, I don't know.
- */
uint64_t uv_hrtime() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return (ts.tv_sec * NANOSEC + ts.tv_nsec);
+ return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
- unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
- multiplier = ((uint64_t)1000L / ticks), cpuspeed;
- int numcpus = 0, i = 0;
- unsigned long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
- char line[512], speedPath[256], model[512];
- FILE *fpStat = fopen("/proc/stat", "r");
- FILE *fpModel = fopen("/proc/cpuinfo", "r");
- FILE *fpSpeed;
- uv_cpu_info_t* cpu_info;
-
- if (fpModel) {
- while (fgets(line, 511, fpModel) != NULL) {
- if (strncmp(line, "model name", 10) == 0) {
- numcpus++;
- if (numcpus == 1) {
- char *p = strchr(line, ':') + 2;
- strcpy(model, p);
- model[strlen(model)-1] = 0;
- }
- } else if (strncmp(line, "cpu MHz", 7) == 0) {
- if (numcpus == 1) {
- sscanf(line, "%*s %*s : %u", &cpuspeed);
- }
- }
- }
- fclose(fpModel);
- }
+ unsigned int numcpus;
+ uv_cpu_info_t* ci;
- *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t));
- if (!(*cpu_infos)) {
- return uv__new_artificial_error(UV_ENOMEM);
- }
+ *cpu_infos = NULL;
+ *count = 0;
+ numcpus = sysconf(_SC_NPROCESSORS_ONLN);
+ assert(numcpus != (unsigned int) -1);
+ assert(numcpus != 0);
+
+ ci = calloc(numcpus, sizeof(*ci));
+ if (ci == NULL)
+ return uv__new_sys_error(ENOMEM);
+
+ read_speeds(numcpus, ci);
+ read_models(numcpus, ci);
+ read_times(numcpus, ci);
+
+ *cpu_infos = ci;
*count = numcpus;
- cpu_info = *cpu_infos;
-
- if (fpStat) {
- while (fgets(line, 511, fpStat) != NULL) {
- if (strncmp(line, "cpu ", 4) == 0) {
- continue;
- } else if (strncmp(line, "cpu", 3) != 0) {
- break;
- }
-
- sscanf(line, "%*s %lu %lu %lu %lu %*s %lu",
- &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr);
- snprintf(speedPath, sizeof(speedPath),
- "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
-
- fpSpeed = fopen(speedPath, "r");
-
- if (fpSpeed) {
- if (fgets(line, 511, fpSpeed) != NULL) {
- sscanf(line, "%u", &cpuspeed);
- cpuspeed /= 1000;
- }
- fclose(fpSpeed);
- }
-
- cpu_info->cpu_times.user = ticks_user * multiplier;
- cpu_info->cpu_times.nice = ticks_nice * multiplier;
- cpu_info->cpu_times.sys = ticks_sys * multiplier;
- cpu_info->cpu_times.idle = ticks_idle * multiplier;
- cpu_info->cpu_times.irq = ticks_intr * multiplier;
-
- cpu_info->model = strdup(model);
- cpu_info->speed = cpuspeed;
-
- cpu_info++;
+ return uv_ok_;
+}
+
+
+static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
+ unsigned int num;
+
+ for (num = 0; num < numcpus; num++)
+ ci[num].speed = read_cpufreq(num) / 1000;
+}
+
+
+static void read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
+#if defined(__i386__) || defined(__x86_64__)
+ static const char marker[] = "model name\t: ";
+#elif defined(__arm__)
+ static const char marker[] = "Processor\t: ";
+#elif defined(__mips__)
+ static const char marker[] = "cpu model\t\t: ";
+#else
+# warning uv_cpu_info() is not supported on this architecture.
+ static const char marker[] = "(dummy)";
+#endif
+ unsigned int num;
+ char buf[1024];
+ char* model;
+ FILE* fp;
+
+ fp = fopen("/proc/cpuinfo", "r");
+ if (fp == NULL)
+ return;
+
+ num = 0;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (num >= numcpus)
+ break;
+
+ if (strncmp(buf, marker, sizeof(marker) - 1))
+ continue;
+
+ model = buf + sizeof(marker) - 1;
+ model = strndup(model, strlen(model) - 1); /* strip newline */
+ ci[num++].model = model;
+ }
+ fclose(fp);
+}
+
+
+static void read_times(unsigned int numcpus, uv_cpu_info_t* ci) {
+ unsigned long clock_ticks;
+ struct uv_cpu_times_s ts;
+ unsigned long user;
+ unsigned long nice;
+ unsigned long sys;
+ unsigned long idle;
+ unsigned long dummy;
+ unsigned long irq;
+ unsigned int num;
+ unsigned int len;
+ char buf[1024];
+ FILE* fp;
+
+ clock_ticks = sysconf(_SC_CLK_TCK);
+ assert(clock_ticks != (unsigned long) -1);
+ assert(clock_ticks != 0);
+
+ fp = fopen("/proc/stat", "r");
+ if (fp == NULL)
+ return;
+
+ if (!fgets(buf, sizeof(buf), fp))
+ abort();
+
+ num = 0;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (num >= numcpus)
+ break;
+
+ if (strncmp(buf, "cpu", 3))
+ break;
+
+ /* skip "cpu<num> " marker */
+ {
+ unsigned int n = num;
+ for (len = sizeof("cpu0"); n /= 10; len++);
+ assert(sscanf(buf, "cpu%u ", &n) == 1 && n == num);
}
- fclose(fpStat);
+
+ /* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
+ * guest, guest_nice but we're only interested in the first four + irq.
+ *
+ * Don't use %*s to skip fields or %ll to read straight into the uint64_t
+ * fields, they're not allowed in C89 mode.
+ */
+ if (6 != sscanf(buf + len,
+ "%lu %lu %lu %lu %lu %lu",
+ &user,
+ &nice,
+ &sys,
+ &idle,
+ &dummy,
+ &irq))
+ abort();
+
+ ts.user = clock_ticks * user;
+ ts.nice = clock_ticks * nice;
+ ts.sys = clock_ticks * sys;
+ ts.idle = clock_ticks * idle;
+ ts.irq = clock_ticks * irq;
+ ci[num++].cpu_times = ts;
}
+ fclose(fp);
+}
- return uv_ok_;
+
+static unsigned long read_cpufreq(unsigned int cpunum) {
+ unsigned long val;
+ char buf[1024];
+ FILE* fp;
+
+ snprintf(buf,
+ sizeof(buf),
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
+ cpunum);
+
+ fp = fopen(buf, "r");
+ if (fp == NULL)
+ return 0;
+
+ val = 0;
+ fscanf(fp, "%lu", &val);
+ fclose(fp);
+
+ return val;
}
#define UV_LOOP_WATCHER_DEFINE(name, type) \
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \
- loop->counters.name##_init++; \
handle->name##_cb = NULL; \
return 0; \
} \
int uv__loop_init(uv_loop_t* loop, int default_loop) {
+ unsigned int i;
+ int flags;
+
#if HAVE_KQUEUE
- int flags = EVBACKEND_KQUEUE;
+ flags = EVBACKEND_KQUEUE;
#else
- int flags = EVFLAG_AUTO;
+ flags = EVFLAG_AUTO;
#endif
memset(loop, 0, sizeof(*loop));
ngx_queue_init(&loop->prepare_handles);
ngx_queue_init(&loop->handle_queue);
loop->closing_handles = NULL;
+ loop->signal_ctx = NULL;
loop->time = uv_hrtime() / 1000000;
loop->async_pipefd[0] = -1;
loop->async_pipefd[1] = -1;
ev_set_userdata(loop->ev, loop);
eio_channel_init(&loop->uv_eio_channel, loop);
-#if __linux__
- loop->inotify_watchers = NULL;
- loop->inotify_fd = -1;
-#endif
-#if HAVE_PORTS_FS
- loop->fs_fd = -1;
-#endif
+ uv_signal_init(loop, &loop->child_watcher);
+ uv__handle_unref(&loop->child_watcher);
+ loop->child_watcher.flags |= UV__HANDLE_INTERNAL;
+
+ for (i = 0; i < ARRAY_SIZE(loop->process_handles); i++)
+ ngx_queue_init(loop->process_handles + i);
+
+ if (uv__platform_loop_init(loop, default_loop))
+ return -1;
+
return 0;
}
void uv__loop_delete(uv_loop_t* loop) {
+ uv__platform_loop_delete(loop);
+ uv__signal_unregister(loop);
ev_loop_destroy(loop->ev);
-#if __linux__
- if (loop->inotify_fd != -1) {
- uv__io_stop(loop, &loop->inotify_read_watcher);
- close(loop->inotify_fd);
- loop->inotify_fd = -1;
- }
-#endif
-#if HAVE_PORTS_FS
- if (loop->fs_fd != -1)
- close(loop->fs_fd);
-#endif
}
#define NANOSEC ((uint64_t) 1e9)
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
uint64_t uv_hrtime(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return (ts.tv_sec * NANOSEC + ts.tv_nsec);
+ return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
void uv_loadavg(double avg[3]) {
static char *process_title;
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
uint64_t uv_hrtime(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return (ts.tv_sec * NANOSEC + ts.tv_nsec);
+ return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
- loop->counters.pipe_init++;
handle->shutdown_req = NULL;
handle->connect_req = NULL;
handle->pipe_fname = NULL;
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
- loop->counters.poll_init++;
-
handle->fd = fd;
handle->poll_cb = NULL;
uv__io_init(&handle->io_watcher, uv__poll_io, fd, 0);
#include "uv.h"
#include "internal.h"
+#include <stdio.h>
+#include <stdlib.h>
#include <assert.h>
#include <errno.h>
+
+#include <sys/types.h>
#include <sys/wait.h>
-#include <poll.h>
#include <unistd.h>
-#include <stdio.h>
#include <fcntl.h>
+#include <poll.h>
#ifdef __APPLE__
# include <TargetConditionals.h>
#endif
-static void uv__chld(EV_P_ ev_child* watcher, int revents) {
- int status = watcher->rstatus;
- int exit_status = 0;
- int term_signal = 0;
- uv_process_t *process = watcher->data;
+static ngx_queue_t* uv__process_queue(uv_loop_t* loop, int pid) {
+ assert(pid > 0);
+ return loop->process_handles + pid % ARRAY_SIZE(loop->process_handles);
+}
- assert(&process->child_watcher == watcher);
- assert(revents & EV_CHILD);
- ev_child_stop(EV_A_ &process->child_watcher);
+static uv_process_t* uv__process_find(uv_loop_t* loop, int pid) {
+ uv_process_t* handle;
+ ngx_queue_t* h;
+ ngx_queue_t* q;
- if (WIFEXITED(status)) {
- exit_status = WEXITSTATUS(status);
- }
+ h = uv__process_queue(loop, pid);
- if (WIFSIGNALED(status)) {
- term_signal = WTERMSIG(status);
+ ngx_queue_foreach(q, h) {
+ handle = ngx_queue_data(q, uv_process_t, queue);
+ if (handle->pid == pid) return handle;
}
- if (process->exit_cb) {
+ return NULL;
+}
+
+
+static void uv__chld(uv_signal_t* handle, int signum) {
+ uv_process_t* process;
+ int exit_status;
+ int term_signal;
+ int status;
+ pid_t pid;
+
+ assert(signum == SIGCHLD);
+
+ for (;;) {
+ pid = waitpid(-1, &status, WNOHANG);
+
+ if (pid == 0)
+ return;
+
+ if (pid == -1) {
+ if (errno == ECHILD)
+ return; /* XXX stop signal watcher? */
+ else
+ abort();
+ }
+
+ process = uv__process_find(handle->loop, pid);
+ if (process == NULL)
+ continue; /* XXX bug? abort? */
+
+ if (process->exit_cb == NULL)
+ continue;
+
+ exit_status = 0;
+ term_signal = 0;
+
+ if (WIFEXITED(status))
+ exit_status = WEXITSTATUS(status);
+
+ if (WIFSIGNALED(status))
+ term_signal = WTERMSIG(status);
+
+ if (process->errorno) {
+ uv__set_sys_error(process->loop, process->errorno);
+ exit_status = -1; /* execve() failed */
+ }
+
process->exit_cb(process, exit_status, term_signal);
}
}
* Used for initializing stdio streams like options.stdin_stream. Returns
* zero on success.
*/
-static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
- int writable) {
+static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
int fd = -1;
switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
UV_INHERIT_STREAM)) {
return -1;
}
- fds[writable ? 1 : 0] = fd;
+ fds[1] = fd;
return 0;
default:
}
-static int uv__process_open_stream(uv_stdio_container_t* container, int fds[2],
+static int uv__process_open_stream(uv_stdio_container_t* container,
+ int fds[2],
int writable) {
- int fd = fds[writable ? 1 : 0];
- int child_fd = fds[writable ? 0 : 1];
+ int child_fd;
int flags;
+ int fd;
+
+ fd = fds[0];
+ child_fd = fds[1];
/* No need to create stream */
- if (!(container->flags & UV_CREATE_PIPE) || fd < 0) {
+ if (!(container->flags & UV_CREATE_PIPE) || fd < 0)
return 0;
- }
assert(child_fd >= 0);
close(child_fd);
static void uv__process_close_stream(uv_stdio_container_t* container) {
if (!(container->flags & UV_CREATE_PIPE)) return;
-
uv__stream_close((uv_stream_t*)container->data.stream);
}
+static void uv__write_int(int fd, int val) {
+ ssize_t n;
+
+ do
+ n = write(fd, &val, sizeof(val));
+ while (n == -1 && errno == EINTR);
+
+ if (n == -1 && errno == EPIPE)
+ return; /* parent process has quit */
+
+ assert(n == sizeof(val));
+}
+
+
static void uv__process_child_init(uv_process_options_t options,
int stdio_count,
- int* pipes) {
+ int (*pipes)[2],
+ int error_fd) {
+ int close_fd;
+ int use_fd;
int i;
- if (options.flags & UV_PROCESS_DETACHED) {
+ if (options.flags & UV_PROCESS_DETACHED)
setsid();
- }
- /* Dup fds */
for (i = 0; i < stdio_count; i++) {
- /*
- * stdin has swapped ends of pipe
- * (it's the only one readable stream)
- */
- int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2];
- int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1];
-
- if (use_fd >= 0) {
+ close_fd = pipes[i][0];
+ use_fd = pipes[i][1];
+
+ if (use_fd >= 0)
close(close_fd);
- } else if (i < 3) {
- /* `/dev/null` stdin, stdout, stderr even if they've flag UV_IGNORE */
+ else if (i >= 3)
+ continue;
+ else {
+ /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
+ * set
+ */
use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);
- if (use_fd < 0) {
+ if (use_fd == -1) {
+ uv__write_int(error_fd, errno);
perror("failed to open stdio");
_exit(127);
}
- } else {
- continue;
}
- if (i != use_fd) {
+ if (i == use_fd)
+ uv__cloexec(use_fd, 0);
+ else {
dup2(use_fd, i);
close(use_fd);
- } else {
- uv__cloexec(use_fd, 0);
}
}
if (options.cwd && chdir(options.cwd)) {
+ uv__write_int(error_fd, errno);
perror("chdir()");
_exit(127);
}
if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
+ uv__write_int(error_fd, errno);
perror("setgid()");
_exit(127);
}
if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
+ uv__write_int(error_fd, errno);
perror("setuid()");
_exit(127);
}
environ = options.env;
execvp(options.file, options.args);
+ uv__write_int(error_fd, errno);
perror("execvp()");
_exit(127);
}
-#ifndef SPAWN_WAIT_EXEC
-# define SPAWN_WAIT_EXEC 1
-#endif
-
-int uv_spawn(uv_loop_t* loop, uv_process_t* process,
- uv_process_options_t options) {
- /*
- * Save environ in the case that we get it clobbered
- * by the child process.
- */
- char** save_our_env = environ;
-
- int stdio_count = options.stdio_count < 3 ? 3 : options.stdio_count;
- int* pipes = malloc(2 * stdio_count * sizeof(int));
-
-#if SPAWN_WAIT_EXEC
+int uv_spawn(uv_loop_t* loop,
+ uv_process_t* process,
+ const uv_process_options_t options) {
int signal_pipe[2] = { -1, -1 };
- struct pollfd pfd;
-#endif
- int status;
+ int (*pipes)[2];
+ int stdio_count;
+ ngx_queue_t* q;
+ ssize_t r;
pid_t pid;
int i;
- if (pipes == NULL) {
- errno = ENOMEM;
- goto error;
- }
-
assert(options.file != NULL);
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
UV_PROCESS_DETACHED |
UV_PROCESS_SETGID |
UV_PROCESS_SETUID)));
-
uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
- loop->counters.process_init++;
- uv__handle_start(process);
+ ngx_queue_init(&process->queue);
- process->exit_cb = options.exit_cb;
+ stdio_count = options.stdio_count;
+ if (stdio_count < 3)
+ stdio_count = 3;
+
+ pipes = malloc(stdio_count * sizeof(*pipes));
+ if (pipes == NULL) {
+ errno = ENOMEM;
+ goto error;
+ }
- /* Init pipe pairs */
for (i = 0; i < stdio_count; i++) {
- pipes[i * 2] = -1;
- pipes[i * 2 + 1] = -1;
+ pipes[i][0] = -1;
+ pipes[i][1] = -1;
}
- /* Create socketpairs/pipes, or use raw fd */
- for (i = 0; i < options.stdio_count; i++) {
- if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) {
+ for (i = 0; i < options.stdio_count; i++)
+ if (uv__process_init_stdio(options.stdio + i, pipes[i]))
goto error;
- }
+
+ /* swap stdin file descriptors, it's the only writable stream */
+ {
+ int* p = pipes[0];
+ int t = p[0];
+ p[0] = p[1];
+ p[1] = t;
}
/* This pipe is used by the parent to wait until
*
* To avoid ambiguity, we create a pipe with both ends
* marked close-on-exec. Then, after the call to `fork()`,
- * the parent polls the read end until it sees POLLHUP.
+ * the parent polls the read end until it EOFs or errors with EPIPE.
*/
-#if SPAWN_WAIT_EXEC
- if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
+ if (uv__make_pipe(signal_pipe, 0))
goto error;
-#endif
+
+ uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
pid = fork();
if (pid == -1) {
-#if SPAWN_WAIT_EXEC
close(signal_pipe[0]);
close(signal_pipe[1]);
-#endif
- environ = save_our_env;
goto error;
}
if (pid == 0) {
- /* Child */
- uv__process_child_init(options, stdio_count, pipes);
-
- /* Execution never reaches here. */
+ uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
+ abort();
}
- /* Parent. */
-
- /* Restore environment. */
- environ = save_our_env;
-
-#if SPAWN_WAIT_EXEC
- /* POLLHUP signals child has exited or execve()'d. */
close(signal_pipe[1]);
- do {
- pfd.fd = signal_pipe[0];
- pfd.events = POLLIN|POLLHUP;
- pfd.revents = 0;
- errno = 0, status = poll(&pfd, 1, -1);
- }
- while (status == -1 && (errno == EINTR || errno == ENOMEM));
- assert((status == 1) && "poll() on pipe read end failed");
- close(signal_pipe[0]);
-#endif
+ process->errorno = 0;
+ do
+ r = read(signal_pipe[0], &process->errorno, sizeof(process->errorno));
+ while (r == -1 && errno == EINTR);
- process->pid = pid;
+ if (r == 0)
+ ; /* okay, EOF */
+ else if (r == sizeof(process->errorno))
+ ; /* okay, read errorno */
+ else if (r == -1 && errno == EPIPE)
+ ; /* okay, got EPIPE */
+ else
+ abort();
- ev_child_init(&process->child_watcher, uv__chld, pid, 0);
- ev_child_start(process->loop->ev, &process->child_watcher);
- process->child_watcher.data = process;
+ close(signal_pipe[0]);
for (i = 0; i < options.stdio_count; i++) {
- if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
- int j;
- /* Close all opened streams */
- for (j = 0; j < i; j++) {
- uv__process_close_stream(&options.stdio[j]);
- }
-
+ if (uv__process_open_stream(options.stdio + i, pipes[i], i == 0)) {
+ while (i--) uv__process_close_stream(options.stdio + i);
goto error;
}
}
- free(pipes);
+ q = uv__process_queue(loop, pid);
+ ngx_queue_insert_tail(q, &process->queue);
+
+ process->pid = pid;
+ process->exit_cb = options.exit_cb;
+ uv__handle_start(process);
+ free(pipes);
return 0;
error:
uv__set_sys_error(process->loop, errno);
for (i = 0; i < stdio_count; i++) {
- close(pipes[i * 2]);
- close(pipes[i * 2 + 1]);
+ close(pipes[i][0]);
+ close(pipes[i][1]);
}
-
free(pipes);
return -1;
void uv__process_close(uv_process_t* handle) {
- ev_child_stop(handle->loop->ev, &handle->child_watcher);
+ /* TODO stop signal watcher when this is the last handle */
+ ngx_queue_remove(&handle->queue);
uv__handle_stop(handle);
}
--- /dev/null
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+struct signal_ctx {
+ int pipefd[2];
+ uv__io_t io_watcher;
+ unsigned int nqueues;
+ ngx_queue_t queues[1]; /* variable length */
+};
+
+static void uv__signal_handler(int signum);
+static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, int events);
+static struct signal_ctx* uv__signal_ctx_new(uv_loop_t* loop);
+static void uv__signal_ctx_delete(struct signal_ctx* ctx);
+static void uv__signal_write(int fd, unsigned int val);
+static unsigned int uv__signal_read(int fd);
+static unsigned int uv__signal_max(void);
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+ uv__handle_init(loop, (uv_handle_t*)handle, UV_SIGNAL);
+ handle->signum = 0;
+ return 0;
+}
+
+
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum_) {
+ struct signal_ctx* ctx;
+ struct sigaction sa;
+ unsigned int signum;
+ uv_loop_t* loop;
+ ngx_queue_t* q;
+
+ /* XXX doing this check in uv_signal_init() - the logical place for it -
+ * leads to an infinite loop when uv__loop_init() inits a signal watcher
+ */
+ /* FIXME */
+ assert(handle->loop == uv_default_loop() &&
+ "uv_signal_t is currently only supported by the default loop");
+
+ loop = handle->loop;
+ signum = signum_;
+
+ if (uv__is_active(handle))
+ return uv__set_artificial_error(loop, UV_EBUSY);
+
+ if (signal_cb == NULL)
+ return uv__set_artificial_error(loop, UV_EINVAL);
+
+ if (signum <= 0)
+ return uv__set_artificial_error(loop, UV_EINVAL);
+
+ ctx = loop->signal_ctx;
+
+ if (ctx == NULL) {
+ ctx = uv__signal_ctx_new(loop);
+
+ if (ctx == NULL)
+ return uv__set_artificial_error(loop, UV_ENOMEM);
+
+ loop->signal_ctx = ctx;
+ }
+
+ if (signum > ctx->nqueues)
+ return uv__set_artificial_error(loop, UV_EINVAL);
+
+ q = ctx->queues + signum;
+
+ if (!ngx_queue_empty(q))
+ goto skip;
+
+ /* XXX use a separate signal stack? */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = uv__signal_handler;
+
+ /* XXX save old action so we can restore it later on? */
+ if (sigaction(signum, &sa, NULL))
+ return uv__set_artificial_error(loop, UV_EINVAL);
+
+skip:
+ ngx_queue_insert_tail(q, &handle->queue);
+ uv__handle_start(handle);
+ handle->signum = signum;
+ handle->signal_cb = signal_cb;
+
+ return 0;
+}
+
+
+int uv_signal_stop(uv_signal_t* handle) {
+ struct signal_ctx* ctx;
+ struct sigaction sa;
+ unsigned int signum;
+ uv_loop_t* loop;
+
+ if (!uv__is_active(handle))
+ return 0;
+
+ signum = handle->signum;
+ loop = handle->loop;
+ ctx = loop->signal_ctx;
+ assert(signum > 0);
+ assert(signum <= ctx->nqueues);
+
+ ngx_queue_remove(&handle->queue);
+ uv__handle_stop(handle);
+ handle->signum = 0;
+
+ if (!ngx_queue_empty(ctx->queues + signum))
+ goto skip;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL; /* XXX restore previous action? */
+
+ if (sigaction(signum, &sa, NULL))
+ return uv__set_artificial_error(loop, UV_EINVAL);
+
+skip:
+ return 0;
+}
+
+
+void uv__signal_close(uv_signal_t* handle) {
+ uv_signal_stop(handle);
+}
+
+
+void uv__signal_unregister(uv_loop_t* loop) {
+ uv__signal_ctx_delete(loop->signal_ctx);
+ loop->signal_ctx = NULL;
+}
+
+
+static void uv__signal_handler(int signum) {
+ struct signal_ctx* ctx = uv_default_loop()->signal_ctx;
+ uv__signal_write(ctx->pipefd[1], (unsigned int) signum);
+}
+
+
+static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, int events) {
+ struct signal_ctx* ctx;
+ unsigned int signum;
+ uv_signal_t* h;
+ ngx_queue_t* q;
+
+ ctx = container_of(w, struct signal_ctx, io_watcher);
+ signum = uv__signal_read(ctx->pipefd[0]);
+ assert(signum > 0);
+ assert(signum <= ctx->nqueues);
+
+ ngx_queue_foreach(q, ctx->queues + signum) {
+ h = ngx_queue_data(q, uv_signal_t, queue);
+ h->signal_cb(h, signum);
+ }
+}
+
+
+static struct signal_ctx* uv__signal_ctx_new(uv_loop_t* loop) {
+ struct signal_ctx* ctx;
+ unsigned int nqueues;
+ unsigned int i;
+
+ nqueues = uv__signal_max();
+ assert(nqueues > 0);
+
+ /* The first ctx->queues entry is never used. It wastes a few bytes of memory
+ * but it saves us from having to substract 1 from the signum all the time -
+ * which inevitably someone will forget to do.
+ */
+ ctx = calloc(1, sizeof(*ctx) + sizeof(ctx->queues[0]) * (nqueues + 1));
+ if (ctx == NULL)
+ return NULL;
+
+ if (uv__make_pipe(ctx->pipefd, UV__F_NONBLOCK)) {
+ free(ctx);
+ return NULL;
+ }
+
+ uv__io_init(&ctx->io_watcher, uv__signal_event, ctx->pipefd[0], UV__IO_READ);
+ uv__io_start(loop, &ctx->io_watcher);
+ ctx->nqueues = nqueues;
+
+ for (i = 1; i <= nqueues; i++)
+ ngx_queue_init(ctx->queues + i);
+
+ return ctx;
+}
+
+
+static void uv__signal_ctx_delete(struct signal_ctx* ctx) {
+ if (ctx == NULL) return;
+ close(ctx->pipefd[0]);
+ close(ctx->pipefd[1]);
+ free(ctx);
+}
+
+
+static void uv__signal_write(int fd, unsigned int val) {
+ ssize_t n;
+
+ do
+ n = write(fd, &val, sizeof(val));
+ while (n == -1 && errno == EINTR);
+
+ if (n == sizeof(val))
+ return;
+
+ if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ return; /* pipe full - nothing we can do about that */
+
+ abort();
+}
+
+
+static unsigned int uv__signal_read(int fd) {
+ unsigned int val;
+ ssize_t n;
+
+ do
+ n = read(fd, &val, sizeof(val));
+ while (n == -1 && errno == EINTR);
+
+ if (n == sizeof(val))
+ return val;
+
+ abort();
+}
+
+
+static unsigned int uv__signal_max(void) {
+#if defined(_SC_RTSIG_MAX)
+ int max = sysconf(_SC_RTSIG_MAX);
+ if (max != -1) return max;
+#endif
+#if defined(SIGRTMAX)
+ return SIGRTMAX;
+#elif defined(NSIG)
+ return NSIG;
+#else
+ return 32;
+#endif
+}
uv_stream_t* stream,
uv_handle_type type) {
uv__handle_init(loop, (uv_handle_t*)stream, type);
- loop->counters.stream_init++;
-
stream->alloc_cb = NULL;
stream->close_cb = NULL;
stream->connection_cb = NULL;
} else {
/* Successful write */
- /* Update the counters. */
while (n >= 0) {
uv_buf_t* buf = &(req->bufs[req->write_index]);
size_t len = buf->len;
}
-int uv_write2(uv_write_t* req, uv_stream_t* stream, uv_buf_t bufs[], int bufcnt,
- uv_stream_t* send_handle, uv_write_cb cb) {
+int uv_write2(uv_write_t* req,
+ uv_stream_t* stream,
+ uv_buf_t bufs[],
+ int bufcnt,
+ uv_stream_t* send_handle,
+ uv_write_cb cb) {
int empty_queue;
+ assert(bufcnt > 0);
+
assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE ||
stream->type == UV_TTY) &&
"uv_write (unix) does not yet support other types of streams");
req->send_handle = send_handle;
ngx_queue_init(&req->queue);
- if (bufcnt <= UV_REQ_BUFSML_SIZE)
+ if (bufcnt <= (int) ARRAY_SIZE(req->bufsml))
req->bufs = req->bufsml;
else
req->bufs = malloc(sizeof(uv_buf_t) * bufcnt);
#endif
+int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+ loop->fs_fd = -1;
+ return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+ if (loop->fs_fd == -1) return;
+ close(loop->fs_fd);
+ loop->fs_fd = -1;
+}
+
+
uint64_t uv_hrtime() {
return (gethrtime());
}
int portfd;
int first_run = 0;
- loop->counters.fs_event_init++;
-
/* We don't support any flags yet. */
assert(!flags);
if (loop->fs_fd == -1) {
const char* filename,
uv_fs_event_cb cb,
int flags) {
- loop->counters.fs_event_init++;
uv__set_sys_error(loop, ENOSYS);
return -1;
}
cpu_info->model = NULL;
} else {
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clock_MHz");
- assert(knp->data_type == KSTAT_DATA_INT32);
- cpu_info->speed = knp->value.i32;
+ assert(knp->data_type == KSTAT_DATA_INT32 ||
+ knp->data_type == KSTAT_DATA_INT64);
+ cpu_info->speed = (knp->data_type == KSTAT_DATA_INT32) ? knp->value.i32
+ : knp->value.i64;
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"brand");
assert(knp->data_type == KSTAT_DATA_STRING);
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
- loop->counters.tcp_init++;
tcp->idle_handle = NULL;
return 0;
}
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
- loop->counters.timer_init++;
-
uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
handle->timer_cb = NULL;
tty->flags |= UV_STREAM_BLOCKING;
}
- loop->counters.tty_init++;
tty->mode = 0;
return 0;
}
struct sockaddr* addr,
socklen_t addrlen,
uv_udp_send_cb send_cb) {
+ assert(bufcnt > 0);
+
if (uv__udp_maybe_deferred_bind(handle, addr->sa_family))
return -1;
req->handle = handle;
req->bufcnt = bufcnt;
- if (bufcnt <= UV_REQ_BUFSML_SIZE) {
+ if (bufcnt <= (int) ARRAY_SIZE(req->bufsml)) {
req->bufs = req->bufsml;
}
else if ((req->bufs = malloc(bufcnt * sizeof(bufs[0]))) == NULL) {
memset(handle, 0, sizeof *handle);
uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP);
- loop->counters.udp_init++;
-
handle->fd = -1;
ngx_queue_init(&handle->write_queue);
ngx_queue_init(&handle->write_completed_queue);
void uv_eio_init(uv_loop_t* loop) {
- if (loop->counters.eio_init) return;
- loop->counters.eio_init = 1;
+ if (loop->flags & UV_LOOP_EIO_INITIALIZED) return;
+ loop->flags |= UV_LOOP_EIO_INITIALIZED;
uv_idle_init(loop, &loop->uv_eio_poller);
uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll);
UNUSED static void uv__handle_init(uv_loop_t* loop,
uv_handle_t* handle,
uv_handle_type type) {
- loop->counters.handle_init++;
handle->loop = loop;
handle->type = type;
handle->flags = UV__HANDLE_REF; /* ref the loop when active */
#include "uv.h"
#include "internal.h"
+#include "atomicops-inl.h"
#include "handle-inl.h"
#include "req-inl.h"
-/* Atomic set operation on char */
-#ifdef _MSC_VER /* MSVC */
-
-/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */
-/* efficient than InterlockedExchange, but InterlockedExchange8 does not */
-/* exist, and interlocked operations on larger targets might require the */
-/* target to be aligned. */
-#pragma intrinsic(_InterlockedOr8)
-
-static char __declspec(inline) uv_atomic_exchange_set(char volatile* target) {
- return _InterlockedOr8(target, 1);
-}
-
-#else /* GCC */
-
-/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */
-static inline char uv_atomic_exchange_set(char volatile* target) {
- const char one = 1;
- char old_value;
- __asm__ __volatile__ ("lock xchgb %0, %1\n\t"
- : "=r"(old_value), "=m"(*target)
- : "0"(one), "m"(*target)
- : "memory");
- return old_value;
-}
-
-#endif
-
-
void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING &&
!handle->async_sent) {
req->type = UV_WAKEUP;
req->data = handle;
- loop->counters.async_init++;
-
uv__handle_start(handle);
return 0;
/* or closed handle. */
assert(!(handle->flags & UV_HANDLE_CLOSING));
- if (!uv_atomic_exchange_set(&handle->async_sent)) {
+ if (!uv__atomic_exchange_set(&handle->async_sent)) {
POST_COMPLETION_FOR_REQ(loop, &handle->async_req);
}
--- /dev/null
+/* 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 UV_WIN_ATOMICOPS_INL_H_
+#define UV_WIN_ATOMICOPS_INL_H_
+
+#include "uv.h"
+
+
+/* Atomic set operation on char */
+#ifdef _MSC_VER /* MSVC */
+
+/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */
+/* efficient than InterlockedExchange, but InterlockedExchange8 does not */
+/* exist, and interlocked operations on larger targets might require the */
+/* target to be aligned. */
+#pragma intrinsic(_InterlockedOr8)
+
+static char __declspec(inline) uv__atomic_exchange_set(char volatile* target) {
+ return _InterlockedOr8(target, 1);
+}
+
+#else /* GCC */
+
+/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */
+static inline char uv__atomic_exchange_set(char volatile* target) {
+ const char one = 1;
+ char old_value;
+ __asm__ __volatile__ ("lock xchgb %0, %1\n\t"
+ : "=r"(old_value), "=m"(*target)
+ : "0"(one), "m"(*target)
+ : "memory");
+ return old_value;
+}
+
+#endif
+
+#endif /* UV_WIN_ATOMICOPS_INL_H_ */
/* Initialize FS */
uv_fs_init();
+ /* Initialize signal stuff */
+ uv_signals_init();
+
/* Initialize console */
uv_console_init();
loop->active_udp_streams = 0;
loop->last_err = uv_ok_;
-
- memset(&loop->counters, 0, sizeof loop->counters);
}
int uv_dlopen(const char* filename, uv_lib_t* lib) {
- wchar_t filename_w[32768];
+ WCHAR filename_w[32768];
lib->handle = NULL;
lib->errmsg = NULL;
case WSAEFAULT: return UV_EFAULT;
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
case WSAEHOSTUNREACH: return UV_EHOSTUNREACH;
- case ERROR_OPERATION_ABORTED: return UV_EINTR;
- case WSAEINTR: return UV_EINTR;
+ case ERROR_OPERATION_ABORTED: return UV_ECANCELED;
+ case WSAEINTR: return UV_ECANCELED;
case ERROR_INVALID_DATA: return UV_EINVAL;
case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL;
case WSAEINVAL: return UV_EINVAL;
}
uv__handle_start(handle);
-
- loop->counters.fs_event_init++;
}
}
-static int uv_split_path(const wchar_t* filename, wchar_t** dir,
- wchar_t** file) {
+static int uv_split_path(const WCHAR* filename, WCHAR** dir,
+ WCHAR** file) {
int len = wcslen(filename);
int i = len;
while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
if (i == 0) {
if (dir) {
- *dir = (wchar_t*)malloc((MAX_PATH + 1) * sizeof(wchar_t));
+ *dir = (WCHAR*)malloc((MAX_PATH + 1) * sizeof(WCHAR));
if (!*dir) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
*file = wcsdup(filename);
} else {
if (dir) {
- *dir = (wchar_t*)malloc((i + 1) * sizeof(wchar_t));
+ *dir = (WCHAR*)malloc((i + 1) * sizeof(WCHAR));
if (!*dir) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
(*dir)[i] = L'\0';
}
- *file = (wchar_t*)malloc((len - i) * sizeof(wchar_t));
+ *file = (WCHAR*)malloc((len - i) * sizeof(WCHAR));
if (!*file) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
const char* filename, uv_fs_event_cb cb, int flags) {
int name_size, is_path_dir;
DWORD attr, last_error;
- wchar_t* dir = NULL, *dir_to_watch, *filenamew = NULL;
- wchar_t short_path[MAX_PATH];
+ WCHAR* dir = NULL, *dir_to_watch, *filenamew = NULL;
+ WCHAR short_path[MAX_PATH];
/* We don't support any flags yet. */
assert(!flags);
uv_fs_event_init_handle(loop, handle, filename, cb);
/* Convert name to UTF16. */
- name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(wchar_t);
- filenamew = (wchar_t*)malloc(name_size);
+ name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(WCHAR);
+ filenamew = (WCHAR*)malloc(name_size);
if (!filenamew) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
if (!uv_utf8_to_utf16(filename, filenamew,
- name_size / sizeof(wchar_t))) {
+ name_size / sizeof(WCHAR))) {
uv__set_sys_error(loop, GetLastError());
return -1;
}
FILE_NOTIFY_INFORMATION* file_info;
int sizew, size, result;
char* filename = NULL;
- wchar_t* filenamew, *long_filenamew = NULL;
+ WCHAR* filenamew, *long_filenamew = NULL;
DWORD offset = 0;
assert(req->type == UV_FS_EVENT_REQ);
*/
if (handle->dirw ||
_wcsnicmp(handle->filew, file_info->FileName,
- file_info->FileNameLength / sizeof(wchar_t)) == 0 ||
+ file_info->FileNameLength / sizeof(WCHAR)) == 0 ||
_wcsnicmp(handle->short_filew, file_info->FileName,
- file_info->FileNameLength / sizeof(wchar_t)) == 0) {
+ file_info->FileNameLength / sizeof(WCHAR)) == 0) {
if (handle->dirw) {
/*
file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
/* Construct a full path to the file. */
size = wcslen(handle->dirw) +
- file_info->FileNameLength / sizeof(wchar_t) + 2;
+ file_info->FileNameLength / sizeof(WCHAR) + 2;
- filenamew = (wchar_t*)malloc(size * sizeof(wchar_t));
+ filenamew = (WCHAR*)malloc(size * sizeof(WCHAR));
if (!filenamew) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
size = GetLongPathNameW(filenamew, NULL, 0);
if (size) {
- long_filenamew = (wchar_t*)malloc(size * sizeof(wchar_t));
+ long_filenamew = (WCHAR*)malloc(size * sizeof(WCHAR));
if (!long_filenamew) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
*/
if (!long_filenamew) {
filenamew = file_info->FileName;
- sizew = file_info->FileNameLength / sizeof(wchar_t);
+ sizew = file_info->FileNameLength / sizeof(WCHAR);
}
} else {
/* Removed or renamed callbacks don't provide filename. */
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
- wchar_t* ai_canonname;
+ WCHAR* ai_canonname;
struct sockaddr* ai_addr;
struct addrinfoW* ai_next;
} ADDRINFOW, *PADDRINFOW;
- DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node,
- const wchar_t* service,
+ DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
+ const WCHAR* service,
const ADDRINFOW* hints,
PADDRINFOW* result);
/* calculate required memory size for all input values */
if (node != NULL) {
- nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t));
+ nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(WCHAR));
if (nodesize == 0) {
uv__set_sys_error(loop, GetLastError());
goto error;
if (service != NULL) {
servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) *
- sizeof(wchar_t));
+ sizeof(WCHAR));
if (servicesize == 0) {
uv__set_sys_error(loop, GetLastError());
goto error;
/* convert node string to UTF16 into allocated memory and save pointer in */
/* the reques. */
if (node != NULL) {
- req->node = (wchar_t*)alloc_ptr;
+ req->node = (WCHAR*)alloc_ptr;
if (uv_utf8_to_utf16(node,
- (wchar_t*) alloc_ptr,
- nodesize / sizeof(wchar_t)) == 0) {
+ (WCHAR*) alloc_ptr,
+ nodesize / sizeof(WCHAR)) == 0) {
uv__set_sys_error(loop, GetLastError());
goto error;
}
/* convert service string to UTF16 into allocated memory and save pointer */
/* in the req. */
if (service != NULL) {
- req->service = (wchar_t*)alloc_ptr;
+ req->service = (WCHAR*)alloc_ptr;
if (uv_utf8_to_utf16(service,
- (wchar_t*) alloc_ptr,
- servicesize / sizeof(wchar_t)) == 0) {
+ (WCHAR*) alloc_ptr,
+ servicesize / sizeof(WCHAR)) == 0) {
uv__set_sys_error(loop, GetLastError());
goto error;
}
uv_async_endgame(loop, (uv_async_t*) handle);
break;
+ case UV_SIGNAL:
+ uv_signal_endgame(loop, (uv_signal_t*) handle);
+ break;
+
case UV_PROCESS:
uv_process_endgame(loop, (uv_process_t*) handle);
break;
uv_async_close(loop, (uv_async_t*) handle);
return;
+ case UV_SIGNAL:
+ uv_signal_close(loop, (uv_signal_t*) handle);
+ return;
+
case UV_PROCESS:
uv_process_close(loop, (uv_process_t*) handle);
return;
#define UV_HANDLE_PIPESERVER 0x02000000
/* Only used by uv_tty_t handles. */
-#define UV_HANDLE_TTY_RAW 0x01000000
-#define UV_HANDLE_TTY_SAVED_POSITION 0x02000000
-#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x04000000
+#define UV_HANDLE_TTY_READABLE 0x01000000
+#define UV_HANDLE_TTY_RAW 0x02000000
+#define UV_HANDLE_TTY_SAVED_POSITION 0x04000000
+#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x08000000
/* Only used by uv_poll_t handles. */
#define UV_HANDLE_POLL_SLOW 0x02000000
/*
* Pipes
*/
-int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
+uv_err_t uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
char* name, size_t nameSize);
int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
/*
+ * Signal watcher
+ */
+void uv_signals_init();
+int uv__signal_dispatch(int signum);
+
+void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle);
+void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle);
+
+void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+ uv_req_t* req);
+
+
+/*
* Spawn
*/
void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle);
-void uv_process_proc_close(uv_loop_t* loop, uv_process_t* handle);
void uv_process_close(uv_loop_t* loop, uv_process_t* handle);
void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle);
/*
* Process stdio handles.
*/
-int uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options,
+uv_err_t uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options,
BYTE** buffer_ptr);
void uv__stdio_destroy(BYTE* buffer);
void uv__stdio_noinherit(BYTE* buffer);
#define UV_LOOP_WATCHER_DEFINE(name, NAME) \
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
uv__handle_init(loop, (uv_handle_t*) handle, UV_##NAME); \
- loop->counters.name##_init++; \
\
return 0; \
} \
uv_req_init(loop, (uv_req_t*) &handle->ipc_header_write_req);
- loop->counters.pipe_init++;
-
return 0;
}
}
-int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
+uv_err_t uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
char* name, size_t nameSize) {
HANDLE pipeHandle;
int errorno;
- int err;
+ uv_err_t err;
char* ptr = (char*)handle;
for (;;) {
errorno = GetLastError();
if (errorno != ERROR_PIPE_BUSY && errorno != ERROR_ACCESS_DENIED) {
- uv__set_sys_error(loop, errorno);
- err = -1;
- goto done;
+ err = uv__new_sys_error(errorno);
+ goto error;
}
/* Pipe name collision. Increment the pointer and try again. */
loop->iocp,
(ULONG_PTR)handle,
0) == NULL) {
- uv__set_sys_error(loop, GetLastError());
- err = -1;
- goto done;
+ err = uv__new_sys_error(GetLastError());
+ goto error;
}
uv_pipe_connection_init(handle);
handle->handle = pipeHandle;
- err = 0;
-done:
- if (err && pipeHandle != INVALID_HANDLE_VALUE) {
+ return uv_ok_;
+
+ error:
+ if (pipeHandle != INVALID_HANDLE_VALUE) {
CloseHandle(pipeHandle);
}
}
/* Convert name to UTF16. */
- nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(wchar_t);
- handle->name = (wchar_t*)malloc(nameSize);
+ nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(WCHAR);
+ handle->name = (WCHAR*)malloc(nameSize);
if (!handle->name) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
- if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(wchar_t))) {
+ if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(WCHAR))) {
uv__set_sys_error(loop, GetLastError());
return -1;
}
req->cb = cb;
/* Convert name to UTF16. */
- nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(wchar_t);
- handle->name = (wchar_t*)malloc(nameSize);
+ nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(WCHAR);
+ handle->name = (WCHAR*)malloc(nameSize);
if (!handle->name) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
- if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(wchar_t))) {
+ if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(WCHAR))) {
errorno = GetLastError();
goto error;
}
handle->poll_req_2.type = UV_POLL_REQ;
handle->poll_req_2.data = handle;
- loop->counters.poll_init++;
-
return 0;
}
}
-static int uv__create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe,
- HANDLE* child_pipe_ptr, unsigned int flags) {
+static uv_err_t uv__create_stdio_pipe_pair(uv_loop_t* loop,
+ uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) {
char pipe_name[64];
SECURITY_ATTRIBUTES sa;
DWORD server_access = 0;
DWORD client_access = 0;
HANDLE child_pipe = INVALID_HANDLE_VALUE;
+ uv_err_t err;
if (flags & UV_READABLE_PIPE) {
server_access |= PIPE_ACCESS_OUTBOUND;
}
/* Create server pipe handle. */
- if (uv_stdio_pipe_server(loop,
- server_pipe,
- server_access,
- pipe_name,
- sizeof(pipe_name)) < 0) {
+ err = uv_stdio_pipe_server(loop,
+ server_pipe,
+ server_access,
+ pipe_name,
+ sizeof(pipe_name));
+ if (err.code != UV_OK)
goto error;
- }
/* Create child pipe handle. */
sa.nLength = sizeof sa;
server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0,
NULL);
if (child_pipe == INVALID_HANDLE_VALUE) {
- uv__set_sys_error(loop, GetLastError());
+ err = uv__new_sys_error(GetLastError());
goto error;
}
/* both ends of the pipe created. */
if (!ConnectNamedPipe(server_pipe->handle, NULL)) {
if (GetLastError() != ERROR_PIPE_CONNECTED) {
- uv__set_sys_error(loop, GetLastError());
+ err = uv__new_sys_error(GetLastError());
goto error;
}
}
*child_pipe_ptr = child_pipe;
- return 0;
+ return uv_ok_;
error:
if (server_pipe->handle != INVALID_HANDLE_VALUE) {
CloseHandle(child_pipe);
}
- return -1;
+ return err;
}
}
-static int uv__create_nul_handle(uv_loop_t* loop, HANDLE* handle_ptr,
+uv_err_t uv__create_nul_handle(HANDLE* handle_ptr,
DWORD access) {
HANDLE handle;
SECURITY_ATTRIBUTES sa;
0,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
- uv__set_sys_error(loop, GetLastError());
- return -1;
+ return uv__new_sys_error(GetLastError());
}
*handle_ptr = handle;
- return 0;
+ return uv_ok_;
}
-int uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options,
+uv_err_t uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options,
BYTE** buffer_ptr) {
BYTE* buffer;
int count, i;
+ uv_err_t err;
count = options->stdio_count;
if (count < 0 || count > 255) {
/* Only support FDs 0-255 */
- uv__set_artificial_error(loop, UV_ENOTSUP);
- return -1;
+ return uv__new_artificial_error(UV_ENOTSUP);
} else if (count < 3) {
/* There should always be at least 3 stdio handles. */
count = 3;
}
/* Allocate the child stdio buffer */
- buffer = malloc(CHILD_STDIO_SIZE(count));
+ buffer = (BYTE*) malloc(CHILD_STDIO_SIZE(count));
if (buffer == NULL) {
- uv__set_artificial_error(loop, UV_ENOMEM);
- return -1;
+ return uv__new_artificial_error(UV_ENOMEM);
}
/* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */
if (i <= 2) {
DWORD access = (i == 0) ? FILE_GENERIC_READ :
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
- if (uv__create_nul_handle(loop,
- &CHILD_STDIO_HANDLE(buffer, i),
- access) < 0) {
+
+ err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
+ access);
+ if (err.code != UV_OK)
goto error;
- }
+
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
}
break;
assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
- if (uv__create_stdio_pipe_pair(loop,
- parent_pipe,
- &child_pipe,
- fdopt.flags) < 0) {
+ err = uv__create_stdio_pipe_pair(loop,
+ parent_pipe,
+ &child_pipe,
+ fdopt.flags);
+ if (err.code != UV_OK)
goto error;
- }
CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
}
*buffer_ptr = buffer;
- return 0;
+ return uv_ok_;
error:
uv__stdio_destroy(buffer);
- return -1;
+ return err;
}
typedef struct env_var {
const char* narrow;
- const wchar_t* wide;
+ const WCHAR* wide;
int len; /* including null or '=' */
int supplied;
int value_len;
#define E_V(str) { str "=", L##str, sizeof(str), 0, 0 }
-#define UTF8_TO_UTF16(s, t) \
- size = uv_utf8_to_utf16(s, NULL, 0) * sizeof(wchar_t); \
- t = (wchar_t*)malloc(size); \
- if (!t) { \
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \
- } \
- if (!uv_utf8_to_utf16(s, t, size / sizeof(wchar_t))) { \
- uv__set_sys_error(loop, GetLastError()); \
- err = -1; \
- goto done; \
+static uv_err_t uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
+ int ws_len, r;
+ WCHAR* ws;
+
+ ws_len = MultiByteToWideChar(CP_UTF8,
+ 0,
+ s,
+ -1,
+ NULL,
+ 0);
+ if (ws_len <= 0) {
+ return uv__new_sys_error(GetLastError());
+ }
+
+ ws = (WCHAR*) malloc(ws_len * sizeof(WCHAR));
+ if (ws == NULL) {
+ return uv__new_artificial_error(UV_ENOMEM);
}
+ r = MultiByteToWideChar(CP_UTF8,
+ 0,
+ s,
+ -1,
+ ws,
+ ws_len);
+ assert(r == ws_len);
+
+ *ws_ptr = ws;
+ return uv_ok_;
+}
+
static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
handle->exit_cb = NULL;
handle->pid = 0;
+ handle->spawn_error = uv_ok_;
handle->exit_signal = 0;
handle->wait_handle = INVALID_HANDLE_VALUE;
handle->process_handle = INVALID_HANDLE_VALUE;
- handle->close_handle = INVALID_HANDLE_VALUE;
handle->child_stdio_buffer = NULL;
+ handle->exit_cb_pending = 0;
uv_req_init(loop, (uv_req_t*)&handle->exit_req);
handle->exit_req.type = UV_PROCESS_EXIT;
handle->exit_req.data = handle;
- uv_req_init(loop, (uv_req_t*)&handle->close_req);
- handle->close_req.type = UV_PROCESS_CLOSE;
- handle->close_req.data = handle;
-
- loop->counters.handle_init++;
- loop->counters.process_init++;
}
/*
* Helper function for search_path
*/
-static wchar_t* search_path_join_test(const wchar_t* dir,
+static WCHAR* search_path_join_test(const WCHAR* dir,
int dir_len,
- const wchar_t* name,
+ const WCHAR* name,
int name_len,
- const wchar_t* ext,
+ const WCHAR* ext,
int ext_len,
- const wchar_t* cwd,
+ const WCHAR* cwd,
int cwd_len) {
- wchar_t *result, *result_pos;
+ WCHAR *result, *result_pos;
DWORD attrs;
if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
}
/* Allocate buffer for output */
- result = result_pos = (wchar_t*)malloc(sizeof(wchar_t) *
+ result = result_pos = (WCHAR*)malloc(sizeof(WCHAR) *
(cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
/* Copy cwd */
/*
* Helper function for search_path
*/
-static wchar_t* path_search_walk_ext(const wchar_t *dir,
+static WCHAR* path_search_walk_ext(const WCHAR *dir,
int dir_len,
- const wchar_t *name,
+ const WCHAR *name,
int name_len,
- wchar_t *cwd,
+ WCHAR *cwd,
int cwd_len,
int name_has_ext) {
- wchar_t* result;
+ WCHAR* result;
/* If the name itself has a nonempty extension, try this extension first */
if (name_has_ext) {
*
* TODO: correctly interpret UNC paths
*/
-static wchar_t* search_path(const wchar_t *file,
- wchar_t *cwd,
- const wchar_t *path) {
+static WCHAR* search_path(const WCHAR *file,
+ WCHAR *cwd,
+ const WCHAR *path) {
int file_has_dir;
- wchar_t* result = NULL;
- wchar_t *file_name_start;
- wchar_t *dot;
- const wchar_t *dir_start, *dir_end, *dir_path;
+ WCHAR* result = NULL;
+ WCHAR *file_name_start;
+ WCHAR *dot;
+ const WCHAR *dir_start, *dir_end, *dir_path;
int dir_len;
int name_has_ext;
/* Find the start of the filename so we can split the directory from the */
/* name. */
- for (file_name_start = (wchar_t*)file + file_len;
+ for (file_name_start = (WCHAR*)file + file_len;
file_name_start > file
&& file_name_start[-1] != L'\\'
&& file_name_start[-1] != L'/'
* Quotes command line arguments
* Returns a pointer to the end (next char to be written) of the buffer
*/
-wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target) {
+WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
int len = wcslen(source),
i, quote_hit;
- wchar_t* start;
+ WCHAR* start;
/*
* Check if the string must be quoted;
}
-wchar_t* make_program_args(char** args, int verbatim_arguments) {
- wchar_t* dst;
- wchar_t* ptr;
+uv_err_t make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) {
char** arg;
- size_t size = 0;
- size_t len;
+ WCHAR* dst = NULL;
+ WCHAR* temp_buffer = NULL;
+ size_t dst_len = 0;
+ size_t temp_buffer_len = 0;
+ WCHAR* pos;
int arg_count = 0;
- wchar_t* buffer;
- int arg_size;
- int buffer_size = 0;
+ uv_err_t err = uv_ok_;
/* Count the required size. */
for (arg = args; *arg; arg++) {
- arg_size = uv_utf8_to_utf16(*arg, NULL, 0) * sizeof(wchar_t);
- size += arg_size;
- buffer_size = arg_size > buffer_size ? arg_size : buffer_size;
+ DWORD arg_len;
+
+ arg_len = MultiByteToWideChar(CP_UTF8,
+ 0,
+ *arg,
+ -1,
+ NULL,
+ 0);
+ if (arg_len == 0) {
+ return uv__new_sys_error(GetLastError());
+ }
+
+ dst_len += arg_len;
+
+ if (arg_len > temp_buffer_len)
+ temp_buffer_len = arg_len;
+
arg_count++;
}
- /* Adjust for potential quotes. Also assume the worst-case scenario
+ /* Adjust for potential quotes. Also assume the worst-case scenario */
/* that every character needs escaping, so we need twice as much space. */
- size = size * 2 + arg_count * 2;
+ dst_len = dst_len * 2 + arg_count * 2;
- dst = (wchar_t*)malloc(size);
- if (!dst) {
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+ /* Allocate buffer for the final command line. */
+ dst = (WCHAR*) malloc(dst_len * sizeof(WCHAR));
+ if (dst == NULL) {
+ err = uv__new_artificial_error(UV_ENOMEM);
+ goto error;
}
- buffer = (wchar_t*)malloc(buffer_size);
- if (!buffer) {
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+ /* Allocate temporary working buffer. */
+ temp_buffer = (WCHAR*) malloc(temp_buffer_len * sizeof(WCHAR));
+ if (temp_buffer == NULL) {
+ err = uv__new_artificial_error(UV_ENOMEM);
+ goto error;
}
- ptr = dst;
+ pos = dst;
for (arg = args; *arg; arg++) {
- len = uv_utf8_to_utf16(*arg, buffer, (size_t)(size - (ptr - dst)));
- if (!len) {
+ DWORD arg_len;
+
+ /* Convert argument to wide char. */
+ arg_len = MultiByteToWideChar(CP_UTF8,
+ 0,
+ *arg,
+ -1,
+ temp_buffer,
+ dst + dst_len - pos);
+ if (arg_len == 0) {
goto error;
}
+
if (verbatim_arguments) {
- wcscpy(ptr, buffer);
- ptr += len - 1;
+ /* Copy verbatim. */
+ wcscpy(pos, temp_buffer);
+ pos += arg_len - 1;
} else {
- ptr = quote_cmd_arg(buffer, ptr);
+ /* Quote/escape, if needed. */
+ pos = quote_cmd_arg(temp_buffer, pos);
}
- *ptr++ = *(arg + 1) ? L' ' : L'\0';
+
+ *pos++ = *(arg + 1) ? L' ' : L'\0';
}
- free(buffer);
- return dst;
+ free(temp_buffer);
+
+ *dst_ptr = dst;
+ return uv_ok_;
error:
free(dst);
- free(buffer);
- return NULL;
+ free(temp_buffer);
+ return err;
}
* these get defined if the input environment block does not contain any
* values for them.
*/
-wchar_t* make_program_env(char** env_block) {
- wchar_t* dst;
- wchar_t* ptr;
+WCHAR* make_program_env(char** env_block) {
+ WCHAR* dst;
+ WCHAR* ptr;
char** env;
- int env_len = 1 * sizeof(wchar_t); /* room for closing null */
+ int env_len = 1 * sizeof(WCHAR); /* room for closing null */
int len;
int i;
DWORD var_size;
check_required_vars_contains_var(required_vars,
ARRAY_SIZE(required_vars),
*env);
- env_len += (uv_utf8_to_utf16(*env, NULL, 0) * sizeof(wchar_t));
+ env_len += (uv_utf8_to_utf16(*env, NULL, 0) * sizeof(WCHAR));
}
for (i = 0; i < ARRAY_SIZE(required_vars); ++i) {
if (!required_vars[i].supplied) {
- env_len += required_vars[i].len * sizeof(wchar_t);
+ env_len += required_vars[i].len * sizeof(WCHAR);
var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
if (var_size == 0) {
uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
}
required_vars[i].value_len = (int)var_size;
- env_len += (int)var_size * sizeof(wchar_t);
+ env_len += (int)var_size * sizeof(WCHAR);
}
}
* a child process has exited.
*/
static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
- uv_process_t* process = (uv_process_t*)data;
- uv_loop_t* loop = process->loop;
-
- assert(didTimeout == FALSE);
- assert(process);
-
- /* Post completed */
- POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
-}
-
-
-/*
- * Called on Windows thread-pool thread to indicate that
- * UnregisterWaitEx has completed.
- */
-static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) {
- uv_process_t* process = (uv_process_t*)data;
+ uv_process_t* process = (uv_process_t*) data;
uv_loop_t* loop = process->loop;
assert(didTimeout == FALSE);
assert(process);
+ assert(!process->exit_cb_pending);
- /* Post completed */
- POST_COMPLETION_FOR_REQ(loop, &process->close_req);
-}
-
-
-/*
- * Called on windows thread pool when CreateProcess failed. It writes an error
- * message to the process' intended stderr and then posts a PROCESS_EXIT
- * packet to the completion port.
- */
-static DWORD WINAPI spawn_failure(void* data) {
- char syscall[] = "CreateProcessW: ";
- char unknown[] = "unknown error\n";
- uv_process_t* process = (uv_process_t*) data;
- uv_loop_t* loop = process->loop;
- HANDLE child_stderr = uv__stdio_handle(process->child_stdio_buffer, 2);
- char* buf = NULL;
- DWORD count, written;
-
- if (child_stderr != INVALID_HANDLE_VALUE) {
- WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL);
-
- count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- process->spawn_errno,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPSTR) &buf,
- 0,
- NULL);
-
- if (buf != NULL && count > 0) {
- WriteFile(child_stderr, buf, count, &written, NULL);
- LocalFree(buf);
- } else {
- WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL);
- }
-
- FlushFileBuffers(child_stderr);
- }
+ process->exit_cb_pending = 1;
/* Post completed */
POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
-
- return 0;
}
void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
DWORD exit_code;
- /* FIXME: race condition. */
+ assert(handle->exit_cb_pending);
+ handle->exit_cb_pending = 0;
+
+ /* If we're closing, don't call the exit callback. Just schedule a close */
+ /* callback now. */
if (handle->flags & UV_HANDLE_CLOSING) {
+ uv_want_endgame(loop, (uv_handle_t*) handle);
return;
}
handle->wait_handle = INVALID_HANDLE_VALUE;
}
- if (handle->process_handle == INVALID_HANDLE_VALUE ||
- !GetExitCodeProcess(handle->process_handle, &exit_code)) {
- /* The process never even started in the first place, or we were unable */
- /* to obtain the exit code. */
- exit_code = 127;
- }
-
/* Set the handle to inactive: no callbacks will be made after the exit */
/* callback.*/
uv__handle_stop(handle);
+ if (handle->spawn_error.code != UV_OK) {
+ /* Spawning failed. */
+ exit_code = (DWORD) -1;
+ } else if (!GetExitCodeProcess(handle->process_handle, &exit_code)) {
+ /* Unable to to obtain the exit code. This should never happen. */
+ exit_code = (DWORD) -1;
+ }
+
/* Fire the exit callback. */
if (handle->exit_cb) {
+ loop->last_err = handle->spawn_error;
handle->exit_cb(handle, exit_code, handle->exit_signal);
}
}
-/* Called on main thread after UnregisterWaitEx finishes. */
-void uv_process_proc_close(uv_loop_t* loop, uv_process_t* handle) {
- uv_want_endgame(loop, (uv_handle_t*)handle);
-}
-
-
void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
uv__handle_start(handle);
if (handle->wait_handle != INVALID_HANDLE_VALUE) {
- handle->close_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
- UnregisterWaitEx(handle->wait_handle, handle->close_handle);
- handle->wait_handle = NULL;
+ /* This blocks until either the wait was cancelled, or the callback has */
+ /* completed. */
+ BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE);
+ if (!r) {
+ /* This should never happen, and if it happens, we can't recover... */
+ uv_fatal_error(GetLastError(), "UnregisterWaitEx");
+ }
- RegisterWaitForSingleObject(&handle->wait_handle, handle->close_handle,
- close_wait_callback, (void*)handle, INFINITE,
- WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
- } else {
+ handle->wait_handle = INVALID_HANDLE_VALUE;
+ }
+
+ if (!handle->exit_cb_pending) {
uv_want_endgame(loop, (uv_handle_t*)handle);
}
}
void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
- if (handle->flags & UV_HANDLE_CLOSING) {
- assert(!(handle->flags & UV_HANDLE_CLOSED));
- uv__handle_stop(handle);
+ assert(!handle->exit_cb_pending);
+ assert(handle->flags & UV_HANDLE_CLOSING);
+ assert(!(handle->flags & UV_HANDLE_CLOSED));
- /* Clean-up the process handle. */
- CloseHandle(handle->process_handle);
+ uv__handle_stop(handle);
- /* Clean up the child stdio ends that may have been left open. */
- if (handle->child_stdio_buffer != NULL) {
- uv__stdio_destroy(handle->child_stdio_buffer);
- }
+ /* Clean-up the process handle. */
+ CloseHandle(handle->process_handle);
- uv__handle_close(handle);
- }
+ uv__handle_close(handle);
}
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
uv_process_options_t options) {
- int i, size, err = 0, keep_child_stdio_open = 0;
- wchar_t* path = NULL;
+ int i;
+ uv_err_t err = uv_ok_;
+ WCHAR* path = NULL;
BOOL result;
- wchar_t* application_path = NULL, *application = NULL, *arguments = NULL,
+ WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
*env = NULL, *cwd = NULL;
STARTUPINFOW startup;
PROCESS_INFORMATION info;
return -1;
}
+ if (options.file == NULL ||
+ options.args == NULL) {
+ uv__set_artificial_error(loop, UV_EINVAL);
+ return -1;
+ }
+
assert(options.file != NULL);
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
UV_PROCESS_DETACHED |
UV_PROCESS_SETUID)));
uv_process_init(loop, process);
-
process->exit_cb = options.exit_cb;
- UTF8_TO_UTF16(options.file, application);
- arguments = options.args ? make_program_args(options.args,
- options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS) : NULL;
- env = options.env ? make_program_env(options.env) : NULL;
+
+ err = uv_utf8_to_utf16_alloc(options.file, &application);
+ if (err.code != UV_OK)
+ goto done;
+
+ err = make_program_args(options.args,
+ options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
+ &arguments);
+ if (err.code != UV_OK)
+ goto done;
if (options.cwd) {
- UTF8_TO_UTF16(options.cwd, cwd);
+ /* Explicit cwd */
+ err = uv_utf8_to_utf16_alloc(options.cwd, &cwd);
+ if (err.code != UV_OK)
+ goto done;
+
} else {
- size = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t);
- if (size) {
- cwd = (wchar_t*)malloc(size);
- if (!cwd) {
- uv__set_artificial_error(loop, UV_ENOMEM);
- err = -1;
- goto done;
- }
+ /* Inherit cwd */
+ DWORD cwd_len, r;
- GetCurrentDirectoryW(size, cwd);
- } else {
- uv__set_sys_error(loop, GetLastError());
- err = -1;
+ cwd_len = GetCurrentDirectoryW(0, NULL);
+ if (!cwd_len) {
+ err = uv__new_sys_error(GetLastError());
+ goto done;
+ }
+
+ cwd = (WCHAR*) malloc(cwd_len * sizeof(WCHAR));
+ if (cwd == NULL) {
+ err = uv__new_artificial_error(UV_ENOMEM);
+ goto done;
+ }
+
+ r = GetCurrentDirectoryW(cwd_len, cwd);
+ if (r == 0 || r >= cwd_len) {
+ err = uv__new_sys_error(GetLastError());
goto done;
}
}
- /* Get PATH env. variable. */
- size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1;
- path = (wchar_t*)malloc(size * sizeof(wchar_t));
- if (!path) {
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+ /* Get PATH environment variable. */
+ {
+ DWORD path_len, r;
+
+ path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+ if (path_len == 0) {
+ err = uv__new_sys_error(GetLastError());
+ goto done;
+ }
+
+
+ path = (WCHAR*) malloc(path_len * sizeof(WCHAR));
+ if (path == NULL) {
+ err = uv__new_artificial_error(UV_ENOMEM);
+ goto done;
+ }
+
+ r = GetEnvironmentVariableW(L"PATH", path, path_len);
+ if (r == 0 || r >= path_len) {
+ err = uv__new_sys_error(GetLastError());
+ goto done;
+ }
}
- GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t));
- path[size - 1] = L'\0';
application_path = search_path(application,
cwd,
path);
-
- if (!application_path) {
- /* CreateProcess will fail, but this allows us to pass this error to */
- /* the user asynchronously. */
- application_path = application;
+ if (application_path == NULL) {
+ /* Not found. */
+ err = uv__new_artificial_error(UV_ENOENT);
+ goto done;
}
- if (uv__stdio_create(loop, &options, &process->child_stdio_buffer) < 0) {
- err = -1;
- goto done;
- }
+ err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
+ if (err.code != UV_OK)
+ goto done;
startup.cb = sizeof(startup);
startup.lpReserved = NULL;
CloseHandle(info.hThread);
} else {
- /* CreateProcessW failed, but this failure should be delivered */
- /* asynchronously to retain unix compatibility. So pretend spawn */
- /* succeeded, and start a thread instead that prints an error */
- /* to the child's intended stderr. */
- process->spawn_errno = GetLastError();
- keep_child_stdio_open = 1;
- if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) {
- uv_fatal_error(GetLastError(), "QueueUserWorkItem");
- }
+ /* CreateProcessW failed. */
+ err = uv__new_sys_error(GetLastError());
}
done:
free(application);
- if (application_path != application) {
- free(application_path);
- }
+ free(application_path);
free(arguments);
free(cwd);
free(env);
free(path);
- /* Under normal circumstances we should close the stdio handles now - the */
- /* the child now has its own duplicates, or something went horribly wrong */
- /* The only exception is when CreateProcess has failed, then we actually */
- /* need to keep the stdio handles to report the error asynchronously. */
- if (process->child_stdio_buffer == NULL) {
- /* Something went wrong before child stdio was initialized. */
- } else if (!keep_child_stdio_open) {
+ process->spawn_error = err;
+
+ if (process->child_stdio_buffer != NULL) {
+ /* Clean up child stdio handles. */
uv__stdio_destroy(process->child_stdio_buffer);
process->child_stdio_buffer = NULL;
- } else {
- /* We're keeping the handles open, the thread pool is going to have */
- /* it's way with them. But at least make them non-inheritable. */
- uv__stdio_noinherit(process->child_stdio_buffer);
}
- if (err == 0) {
- /* Spawn was succesful. The handle will be active until the exit */
- /* is made or the handle is closed, whichever happens first. */
- uv__handle_start(process);
- } else {
- /* Spawn was not successful. Clean up. */
- if (process->wait_handle != INVALID_HANDLE_VALUE) {
- UnregisterWait(process->wait_handle);
- process->wait_handle = INVALID_HANDLE_VALUE;
- }
+ /* Make the handle active. It will remain active until the exit callback */
+ /* is made or the handle is closed, whichever happens first. */
+ uv__handle_start(process);
- if (process->process_handle != INVALID_HANDLE_VALUE) {
- CloseHandle(process->process_handle);
- process->process_handle = INVALID_HANDLE_VALUE;
- }
+ /* If an error happened, queue the exit req. */
+ if (err.code != UV_OK) {
+ process->exit_cb_pending = 1;
+ uv_insert_pending_req(loop, (uv_req_t*) &process->exit_req);
}
- return err;
+ return 0;
}
INLINE static void uv_req_init(uv_loop_t* loop, uv_req_t* req) {
- loop->counters.req_init++;
req->type = UV_UNKNOWN_REQ;
SET_REQ_SUCCESS(req);
}
uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
break;
+ case UV_SIGNAL_REQ:
+ uv_process_signal_req(loop, (uv_signal_t*) req->data, req);
+ break;
+
case UV_POLL_REQ:
uv_process_poll_req(loop, (uv_poll_t*) req->data, req);
break;
uv_process_proc_exit(loop, (uv_process_t*) req->data);
break;
- case UV_PROCESS_CLOSE:
- uv_process_proc_close(loop, (uv_process_t*) req->data);
- break;
-
case UV_FS:
uv_process_fs_req(loop, (uv_fs_t*) req);
break;
--- /dev/null
+/* 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 <assert.h>
+#include <signal.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+RB_HEAD(uv_signal_tree_s, uv_signal_s);
+
+static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree);
+static ssize_t volatile uv__signal_control_handler_refs = 0;
+static CRITICAL_SECTION uv__signal_lock;
+
+
+void uv_signals_init() {
+ InitializeCriticalSection(&uv__signal_lock);
+}
+
+
+static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
+ /* Compare signums first so all watchers with the same signnum end up */
+ /* adjacent. */
+ if (w1->signum < w2->signum) return -1;
+ if (w1->signum > w2->signum) return 1;
+
+ /* Sort by loop pointer, so we can easily look up the first item after */
+ /* { .signum = x, .loop = NULL } */
+ if ((uintptr_t) w1->loop < (uintptr_t) w2->loop) return -1;
+ if ((uintptr_t) w1->loop > (uintptr_t) w2->loop) return 1;
+
+ if ((uintptr_t) w1 < (uintptr_t) w2) return -1;
+ if ((uintptr_t) w1 > (uintptr_t) w2) return 1;
+
+ return 0;
+}
+
+
+RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare);
+
+
+/*
+ * Dispatches signal {signum} to all active uv_signal_t watchers in all loops.
+ * Returns 1 if the signal was dispatched to any watcher, or 0 if there were
+ * no active signal watchers observing this signal.
+ */
+int uv__signal_dispatch(int signum) {
+ uv_signal_t lookup;
+ uv_signal_t* handle;
+ int dispatched = 0;
+
+ EnterCriticalSection(&uv__signal_lock);
+
+ lookup.signum = signum;
+ lookup.loop = NULL;
+
+ for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
+ handle != NULL && handle->signum == signum;
+ handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
+ unsigned long previous = InterlockedExchange(&handle->pending_signum, signum);
+
+ if (!previous) {
+ POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req);
+ }
+
+ dispatched = 1;
+ }
+
+ LeaveCriticalSection(&uv__signal_lock);
+
+ return dispatched;
+}
+
+
+static BOOL WINAPI uv__signal_control_handler(DWORD type) {
+ switch (type) {
+ case CTRL_C_EVENT:
+ return uv__signal_dispatch(SIGINT);
+
+ case CTRL_BREAK_EVENT:
+ return uv__signal_dispatch(SIGBREAK);
+
+ case CTRL_CLOSE_EVENT:
+ if (uv__signal_dispatch(SIGHUP)) {
+ /* Windows will terminate the process after the control handler */
+ /* returns. After that it will just terminate our process. Therefore */
+ /* block the signal handler so the main loop has some time to pick */
+ /* up the signal and do something for a few seconds. */
+ Sleep(INFINITE);
+ return TRUE;
+ }
+ return FALSE;
+
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ /* These signals are only sent to services. Services have their own */
+ /* notification mechanism, so there's no point in handling these. */
+
+ default:
+ /* We don't handle these. */
+ return FALSE;
+ }
+}
+
+
+static uv_err_t uv__signal_register_control_handler() {
+ /* When this function is called, the uv__signal_lock must be held. */
+
+ /* If the console control handler has already been hooked, just add a */
+ /* reference. */
+ if (uv__signal_control_handler_refs > 0)
+ return uv_ok_;
+
+ if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE))
+ return uv__new_sys_error(GetLastError());
+
+ uv__signal_control_handler_refs++;
+
+ return uv_ok_;
+}
+
+
+static void uv__signal_unregister_control_handler() {
+ /* When this function is called, the uv__signal_lock must be held. */
+ BOOL r;
+
+ /* Don't unregister if the number of console control handlers exceeds one. */
+ /* Just remove a reference in that case. */
+ if (uv__signal_control_handler_refs > 1) {
+ uv__signal_control_handler_refs--;
+ return;
+ }
+
+ assert(uv__signal_control_handler_refs == 1);
+
+ r = SetConsoleCtrlHandler(uv__signal_control_handler, FALSE);
+ /* This should never fail; if it does it is probably a bug in libuv. */
+ assert(r);
+
+ uv__signal_control_handler_refs--;
+}
+
+
+static uv_err_t uv__signal_register(int signum) {
+ switch (signum) {
+ case SIGINT:
+ case SIGBREAK:
+ case SIGHUP:
+ return uv__signal_register_control_handler();
+
+ case SIGILL:
+ case SIGABRT_COMPAT:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGTERM:
+ case SIGABRT:
+ /* Signal is never raised. */
+ return uv_ok_;
+
+ default:
+ /* Invalid signal. */
+ return uv__new_artificial_error(UV_EINVAL);
+ }
+}
+
+
+static void uv__signal_unregister(int signum) {
+ switch (signum) {
+ case SIGINT:
+ case SIGBREAK:
+ case SIGHUP:
+ uv__signal_unregister_control_handler();
+ return;
+
+ case SIGILL:
+ case SIGABRT_COMPAT:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGTERM:
+ case SIGABRT:
+ /* Nothing is registered for this signal. */
+ return;
+
+ default:
+ /* Libuv bug. */
+ assert(0 && "Invalid signum");
+ return;
+ }
+}
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+ uv_req_t* req;
+
+ uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
+ handle->pending_signum = 0;
+ handle->signum = 0;
+ handle->signal_cb = NULL;
+
+ req = &handle->signal_req;
+ uv_req_init(loop, req);
+ req->type = UV_SIGNAL_REQ;
+ req->data = handle;
+
+ uv__handle_start(handle);
+
+ return 0;
+}
+
+
+int uv_signal_stop(uv_signal_t* handle) {
+ uv_signal_t* removed_handle;
+
+ /* If the watcher wasn't started, this is a no-op. */
+ if (handle->signum == 0)
+ return 0;
+
+ EnterCriticalSection(&uv__signal_lock);
+
+ uv__signal_unregister(handle->signum);
+
+ removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle);
+ assert(removed_handle == handle);
+
+ LeaveCriticalSection(&uv__signal_lock);
+
+ handle->signum = 0;
+ uv__handle_stop(handle);
+
+ return 0;
+}
+
+
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
+ uv_err_t err;
+
+ /* If the user supplies signum == 0, then return an error already. If the */
+ /* signum is otherwise invalid then uv__signal_register will find out */
+ /* eventually. */
+ if (signum == 0) {
+ uv__set_artificial_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
+
+ /* Short circuit: if the signal watcher is already watching {signum} don't */
+ /* go through the process of deregistering and registering the handler. */
+ /* Additionally, this avoids pending signals getting lost in the (small) */
+ /* time frame that handle->signum == 0. */
+ if (signum == handle->signum) {
+ handle->signal_cb = signal_cb;
+ return 0;
+ }
+
+ /* If the signal handler was already active, stop it first. */
+ if (handle->signum != 0) {
+ int r = uv_signal_stop(handle);
+ /* uv_signal_stop is infallible. */
+ assert(r == 0);
+ }
+
+ EnterCriticalSection(&uv__signal_lock);
+
+ err = uv__signal_register(signum);
+ if (err.code != UV_OK) {
+ /* Uh-oh, didn't work. */
+ handle->loop->last_err = err;
+ LeaveCriticalSection(&uv__signal_lock);
+ return -1;
+ }
+
+ handle->signum = signum;
+ RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle);
+
+ LeaveCriticalSection(&uv__signal_lock);
+
+ handle->signal_cb = signal_cb;
+ uv__handle_start(handle);
+
+ return 0;
+}
+
+
+void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+ uv_req_t* req) {
+ unsigned long dispatched_signum;
+
+ assert(handle->type == UV_SIGNAL);
+ assert(req->type == UV_SIGNAL_REQ);
+
+ dispatched_signum = InterlockedExchange(&handle->pending_signum, 0);
+ assert(dispatched_signum != 0);
+
+ /* Check if the pending signal equals the signum that we are watching for. */
+ /* These can get out of sync when the handler is stopped and restarted */
+ /* while the signal_req is pending. */
+ if (dispatched_signum == handle->signum)
+ handle->signal_cb(handle, dispatched_signum);
+
+ if (handle->flags & UV_HANDLE_CLOSING) {
+ /* When it is closing, it must be stopped at this point. */
+ assert(handle->signum == 0);
+ uv_want_endgame(loop, (uv_handle_t*) handle);
+ }
+}
+
+
+void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) {
+ uv_signal_stop(handle);
+
+ if (handle->pending_signum == 0) {
+ uv__handle_start(handle);
+ uv_want_endgame(loop, (uv_handle_t*) handle);
+ }
+}
+
+
+void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) {
+ assert(handle->flags & UV_HANDLE_CLOSING);
+ assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+ assert(handle->signum == 0);
+ assert(handle->pending_signum == 0);
+
+ handle->flags |= UV_HANDLE_CLOSED;
+
+ uv__handle_stop(handle);
+ uv__handle_close(handle);
+}
uv__handle_init(loop, (uv_handle_t*) handle, type);
handle->write_queue_size = 0;
handle->activecnt = 0;
-
- loop->counters.stream_init++;
}
handle->func_connectex = NULL;
handle->processed_accepts = 0;
- loop->counters.tcp_init++;
-
return 0;
}
handle->timer_cb = NULL;
handle->repeat = 0;
- loop->counters.timer_init++;
-
return 0;
}
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) {
- HANDLE win_handle;
- CONSOLE_SCREEN_BUFFER_INFO info;
-
- loop->counters.tty_init++;
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ DWORD original_console_mode = 0;
+ CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
- win_handle = (HANDLE) _get_osfhandle(fd);
- if (win_handle == INVALID_HANDLE_VALUE) {
- uv__set_sys_error(loop, ERROR_INVALID_HANDLE);
+ handle = (HANDLE) _get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ uv__set_artificial_error(loop, UV_EBADF);
return -1;
}
- if (!GetConsoleMode(win_handle, &tty->original_console_mode)) {
- uv__set_sys_error(loop, GetLastError());
- return -1;
- }
+ if (readable) {
+ /* Try to obtain the original console mode fromt he input handle. */
+ if (!GetConsoleMode(handle, &original_console_mode)) {
+ uv__set_sys_error(loop, GetLastError());
+ return -1;
+ }
- /* Initialize virtual window size; if it fails, assume that this is stdin. */
- if (GetConsoleScreenBufferInfo(win_handle, &info)) {
+ } else {
+ /* Obtain the screen buffer info with the output handle. */
+ if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) {
+ uv__set_sys_error(loop, GetLastError());
+ return -1;
+ }
+
+ /* Update the virtual window. We must hold the tty_output_lock because the */
+ /* virtual window state is shared between all uv_tty handles. */
EnterCriticalSection(&uv_tty_output_lock);
- uv_tty_update_virtual_window(&info);
+ uv_tty_update_virtual_window(&screen_buffer_info);
LeaveCriticalSection(&uv_tty_output_lock);
}
+
uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY);
uv_connection_init((uv_stream_t*) tty);
- tty->handle = win_handle;
- tty->read_line_handle = NULL;
- tty->read_line_buffer = uv_null_buf_;
- tty->read_raw_wait = NULL;
+ tty->handle = handle;
tty->reqs_pending = 0;
tty->flags |= UV_HANDLE_BOUND;
- /* Init keycode-to-vt100 mapper state. */
- tty->last_key_len = 0;
- tty->last_key_offset = 0;
- tty->last_utf16_high_surrogate = 0;
- memset(&tty->last_input_record, 0, sizeof tty->last_input_record);
-
- /* Init utf8-to-utf16 conversion state. */
- tty->utf8_bytes_left = 0;
- tty->utf8_codepoint = 0;
+ if (readable) {
+ /* Initialize TTY input specific fields. */
+ tty->original_console_mode = original_console_mode;
+ tty->flags |= UV_HANDLE_TTY_READABLE;
+ tty->read_line_handle = NULL;
+ tty->read_line_buffer = uv_null_buf_;
+ tty->read_raw_wait = NULL;
+
+ /* Init keycode-to-vt100 mapper state. */
+ tty->last_key_len = 0;
+ tty->last_key_offset = 0;
+ tty->last_utf16_high_surrogate = 0;
+ memset(&tty->last_input_record, 0, sizeof tty->last_input_record);
+ } else {
+ /* TTY output specific fields. */
+ /* Init utf8-to-utf16 conversion state. */
+ tty->utf8_bytes_left = 0;
+ tty->utf8_codepoint = 0;
- /* Initialize eol conversion state */
- tty->previous_eol = 0;
+ /* Initialize eol conversion state */
+ tty->previous_eol = 0;
- /* Init ANSI parser state. */
- tty->ansi_parser_state = ANSI_NORMAL;
+ /* Init ANSI parser state. */
+ tty->ansi_parser_state = ANSI_NORMAL;
+ }
return 0;
}
uv_alloc_cb alloc_cb;
uv_read_cb read_cb;
+ if (!(tty->flags & UV_HANDLE_TTY_READABLE)) {
+ uv__set_artificial_error(tty->loop, UV_EINVAL);
+ return -1;
+ }
+
if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
return 0;
}
off_t buf_used;
assert(handle->type == UV_TTY);
+ assert(handle->flags & UV_HANDLE_TTY_READABLE);
handle->flags &= ~UV_HANDLE_READ_PENDING;
if (!(handle->flags & UV_HANDLE_READING) ||
uv_buf_t buf;
assert(handle->type == UV_TTY);
+ assert(handle->flags & UV_HANDLE_TTY_READABLE);
buf = handle->read_line_buffer;
void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
uv_req_t* req) {
+ assert(handle->type == UV_TTY);
+ assert(handle->flags & UV_HANDLE_TTY_READABLE);
/* If the read_line_buffer member is zero, it must have been an raw read. */
/* Otherwise it was a line-buffered read. */
uv_read_cb read_cb) {
uv_loop_t* loop = handle->loop;
+ if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
+ uv__set_artificial_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
+
handle->flags |= UV_HANDLE_READING;
INCREASE_ACTIVE_COUNT(loop, handle);
handle->read_cb = read_cb;
int uv_tty_read_stop(uv_tty_t* handle) {
uv_loop_t* loop = handle->loop;
+ if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
+ uv__set_artificial_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
if (handle->flags & UV_HANDLE_READING) {
handle->flags &= ~UV_HANDLE_READING;
uv_buf_t bufs[], int bufcnt, uv_write_cb cb) {
DWORD error;
+ if (handle->flags & UV_HANDLE_TTY_READABLE) {
+ uv__set_artificial_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
+
if ((handle->flags & UV_HANDLE_SHUTTING) ||
(handle->flags & UV_HANDLE_CLOSING)) {
uv__set_sys_error(loop, WSAESHUTDOWN);
void uv_tty_close(uv_tty_t* handle) {
- handle->flags |= UV_HANDLE_SHUTTING;
-
- uv_tty_read_stop(handle);
CloseHandle(handle->handle);
+ if (handle->flags & UV_HANDLE_TTY_READABLE) {
+ /* Readable TTY handle */
+ uv_tty_read_stop(handle);
+ } else {
+ /* Writable TTY handle */
+ handle->flags |= UV_HANDLE_SHUTTING;
+ }
+
uv__handle_start(handle);
if (handle->reqs_pending == 0) {
void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
- if ((handle->flags && UV_HANDLE_CONNECTION) &&
+ if (!(handle->flags && UV_HANDLE_TTY_READABLE) &&
handle->shutdown_req != NULL &&
handle->write_reqs_pending == 0) {
UNREGISTER_HANDLE_REQ(loop, handle, handle->shutdown_req);
handle->reqs_pending == 0) {
/* The console handle duplicate used for line reading should be destroyed */
/* by uv_tty_read_stop. */
- assert(handle->read_line_handle == NULL);
+ assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
+ handle->read_line_handle == NULL);
/* The wait handle used for raw reading should be unregistered when the */
/* wait callback runs. */
- assert(handle->read_raw_wait == NULL);
+ assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
+ handle->read_raw_wait == NULL);
assert(!(handle->flags & UV_HANDLE_CLOSED));
uv__handle_stop(handle);
handle->recv_req.type = UV_UDP_RECV;
handle->recv_req.data = handle;
- loop->counters.udp_init++;
-
return 0;
}
}
-int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size,
+int uv_utf16_to_utf8(const WCHAR* utf16Buffer, size_t utf16Size,
char* utf8Buffer, size_t utf8Size) {
return WideCharToMultiByte(CP_UTF8,
0,
}
-int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer,
+int uv_utf8_to_utf16(const char* utf8Buffer, WCHAR* utf16Buffer,
size_t utf16Size) {
return MultiByteToWideChar(CP_UTF8,
0,
utf16_buffer_len = (int) *size_ptr;
}
- utf16_buffer = (wchar_t*) malloc(sizeof(WCHAR) * utf16_buffer_len);
+ utf16_buffer = (WCHAR*) malloc(sizeof(WCHAR) * utf16_buffer_len);
if (!utf16_buffer) {
return -1;
}
uv_err_t uv_set_process_title(const char* title) {
uv_err_t err;
int length;
- wchar_t* title_w = NULL;
+ WCHAR* title_w = NULL;
uv__once_init();
}
/* Convert to wide-char string */
- title_w = (wchar_t*)malloc(sizeof(wchar_t) * length);
+ title_w = (WCHAR*)malloc(sizeof(WCHAR) * length);
if (!title_w) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
static int uv__get_process_title() {
- wchar_t title_w[MAX_TITLE_LENGTH];
+ WCHAR title_w[MAX_TITLE_LENGTH];
int length;
if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) {
#include <stdlib.h>
#define NUM_SYNC_REQS (10 * 1e5)
-#define NUM_ASYNC_REQS (1 * 1e5)
+#define NUM_ASYNC_REQS (1 * (int) 1e5)
#define MAX_CONCURRENT_REQS 32
#define sync_stat(req, path) \
LOGF("uv_idle_t: %u bytes\n", (unsigned int) sizeof(uv_idle_t));
LOGF("uv_async_t: %u bytes\n", (unsigned int) sizeof(uv_async_t));
LOGF("uv_timer_t: %u bytes\n", (unsigned int) sizeof(uv_timer_t));
+ LOGF("uv_fs_poll_t: %u bytes\n", (unsigned int) sizeof(uv_fs_poll_t));
LOGF("uv_fs_event_t: %u bytes\n", (unsigned int) sizeof(uv_fs_event_t));
LOGF("uv_process_t: %u bytes\n", (unsigned int) sizeof(uv_process_t));
LOGF("uv_poll_t: %u bytes\n", (unsigned int) sizeof(uv_poll_t));
goto error;
if (part) {
- if (_snwprintf((wchar_t*)args,
- sizeof(args) / sizeof(wchar_t),
+ if (_snwprintf((WCHAR*)args,
+ sizeof(args) / sizeof(WCHAR),
L"\"%s\" %S %S",
image,
name,
goto error;
}
} else {
- if (_snwprintf((wchar_t*)args,
- sizeof(args) / sizeof(wchar_t),
+ if (_snwprintf((WCHAR*)args,
+ sizeof(args) / sizeof(WCHAR),
L"\"%s\" %S",
image,
name) < 0) {
char* p;
p = (char *) calloc(1, 32) + 31; /* leaks memory */
- v = d;
+ v = (uint64_t) d;
#if 0 /* works but we don't care about fractional precision */
if (d - v >= 0.01) {
+++ /dev/null
-/* 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.
- */
-#define UNIX (defined(__unix__) || defined(__POSIX__) || defined(__APPLE__))
-#include "task.h"
-#include "uv.h"
-#include <fcntl.h>
-
-#if UNIX
-#include <unistd.h> /* unlink, rmdir, etc. */
-#else
-# include <direct.h>
-# include <io.h>
-# define unlink _unlink
-# define rmdir _rmdir
-# define stat _stati64
-# define open _open
-# define write _write
-# define lseek _lseek
-# define close _close
-#endif
-
-static char exepath[1024];
-static size_t exepath_size = 1024;
-static char* args[3];
-static uv_fs_t open_req;
-static uv_tcp_t tcp;
-static uv_udp_t udp;
-static uv_pipe_t uvpipe;
-static uv_tty_t tty;
-static uv_prepare_t prepare;
-static uv_check_t check;
-static uv_idle_t idle;
-static uv_async_t async;
-static uv_timer_t timer;
-static uv_fs_event_t fs_event;
-static uv_process_t process;
-static uv_process_options_t options;
-static uv_fs_t fs_req;
-
-static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
- ASSERT(exit_status == 1);
- ASSERT(term_signal == 0);
- uv_close((uv_handle_t*)process, NULL);
-}
-
-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 create_dir(uv_loop_t* loop, const char* name) {
- int r;
- uv_fs_t req;
- r = uv_fs_rmdir(loop, &req, name, NULL);
- r = uv_fs_mkdir(loop, &req, name, 0755, NULL);
- ASSERT(r == 0 || uv_last_error(loop).code == UV_EEXIST);
- uv_fs_req_cleanup(&req);
-}
-
-static void create_cb(uv_fs_t* req) {
- ASSERT(req == &open_req);
- ASSERT(req->fs_type == UV_FS_OPEN);
- ASSERT(req->result != -1);
- uv_fs_req_cleanup(req);
- unlink("test_file");
-}
-
-TEST_IMPL(counters_init) {
- int r;
- uint64_t eio_init_prev;
- uint64_t req_init_prev;
- uint64_t handle_init_prev;
- uint64_t stream_init_prev;
- uint64_t tcp_init_prev;
- uint64_t udp_init_prev;
- uint64_t pipe_init_prev;
- uint64_t tty_init_prev;
- uint64_t prepare_init_prev;
- uint64_t check_init_prev;
- uint64_t idle_init_prev;
- uint64_t async_init_prev;
- uint64_t timer_init_prev;
- uint64_t process_init_prev;
- uint64_t fs_event_init_prev;
-
- /* req_init and eio_init test by uv_fs_open() */
- unlink("test_file");
- req_init_prev = uv_default_loop()->counters.req_init;
- eio_init_prev = uv_default_loop()->counters.eio_init;
- r = uv_fs_open(uv_default_loop(), &open_req, "test_file", O_WRONLY | O_CREAT,
- S_IREAD | S_IWRITE, create_cb);
- ASSERT(r == 0);
- ASSERT(open_req.result == 0);
- ASSERT(uv_default_loop()->counters.req_init == ++req_init_prev);
-#ifndef _WIN32
- ASSERT(uv_default_loop()->counters.eio_init == ++eio_init_prev);
-#endif
-
- /* tcp_init, stream_init and handle_init test by uv_tcp_init() */
- tcp_init_prev = uv_default_loop()->counters.tcp_init;
- stream_init_prev = uv_default_loop()->counters.stream_init;
- handle_init_prev = uv_default_loop()->counters.handle_init;
- r = uv_tcp_init(uv_default_loop(), &tcp);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.tcp_init == ++tcp_init_prev);
- ASSERT(uv_default_loop()->counters.stream_init == ++stream_init_prev);
- ASSERT(uv_default_loop()->counters.handle_init == ++handle_init_prev);
- uv_close((uv_handle_t*)&tcp, NULL);
-
- /* udp_init test by uv_udp_init() */
- udp_init_prev = uv_default_loop()->counters.udp_init;
- r = uv_udp_init(uv_default_loop(), &udp);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.udp_init == ++udp_init_prev);
- uv_close((uv_handle_t*)&udp, NULL);
-
- /* pipe_init uv_pipe_init() */
- pipe_init_prev = uv_default_loop()->counters.pipe_init;
- uv_pipe_init(uv_default_loop(), &uvpipe, 0);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.pipe_init == ++pipe_init_prev);
- uv_close((uv_handle_t*)&uvpipe, NULL);
-
- /* tty_init test by uv_tty_init()*/
- tty_init_prev = uv_default_loop()->counters.tty_init;
- r = uv_tty_init(uv_default_loop(), &tty, 1, 0);
- /* uv_tty_init() always returns -1 in run_test in Windows
- so that we avoid to check return value.
- */
-#ifndef _WIN32
- ASSERT(r == 0);
- uv_close((uv_handle_t*)&tty, NULL);
-#endif
- ASSERT(uv_default_loop()->counters.tty_init == ++tty_init_prev);
-
- /* prepare_init test by uv_prepare_init() */
- prepare_init_prev = uv_default_loop()->counters.prepare_init;
- r = uv_prepare_init(uv_default_loop(), &prepare);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.prepare_init == ++prepare_init_prev);
- uv_close((uv_handle_t*)&prepare, NULL);
-
- /* check_init test by uv_check_init() */
- check_init_prev = uv_default_loop()->counters.check_init;
- r = uv_check_init(uv_default_loop(), &check);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.check_init == ++check_init_prev);
- uv_close((uv_handle_t*)&check, NULL);
-
- /* idle_init test by uv_idle_init() */
- idle_init_prev = uv_default_loop()->counters.idle_init;
- r = uv_idle_init(uv_default_loop(), &idle);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.idle_init == ++idle_init_prev);
- uv_close((uv_handle_t*)&idle, NULL);
-
- /* async_init test by uv_async_init() */
- async_init_prev = uv_default_loop()->counters.async_init;
- r = uv_async_init(uv_default_loop(), &async, NULL);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.async_init == ++async_init_prev);
- uv_close((uv_handle_t*)&async, NULL);
-
- /* timer_init test by uv_timer_init() */
- timer_init_prev = uv_default_loop()->counters.timer_init;
- r = uv_timer_init(uv_default_loop(), &timer);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.timer_init == ++timer_init_prev);
- uv_close((uv_handle_t*)&timer, NULL);
-
- /* process_init test by uv_spawn() */
- process_init_prev = uv_default_loop()->counters.process_init;
- init_process_options("spawn_helper1", exit_cb);
- r = uv_spawn(uv_default_loop(), &process, options);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.process_init == ++process_init_prev);
- r = uv_run(uv_default_loop());
- ASSERT(r == 0);
-
- /* fs_event_init test by uv_fs_event_init() */
- create_dir(uv_default_loop(), "watch_dir");
- fs_event_init_prev = uv_default_loop()->counters.fs_event_init;
- r = uv_fs_event_init(uv_default_loop(), &fs_event, "watch_dir", NULL, 0);
- ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.fs_event_init == ++fs_event_init_prev);
- uv_fs_rmdir(uv_default_loop(), &fs_req, "watch_dir", NULL);
- uv_fs_req_cleanup(&fs_req);
-
- return 0;
-}
static void do_accept(uv_timer_t* timer_handle, int status) {
uv_tcp_t* server;
uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle);
- uint64_t tcpcnt;
int r;
ASSERT(timer_handle != NULL);
r = uv_tcp_init(uv_default_loop(), accepted_handle);
ASSERT(r == 0);
- /* Test to that uv_default_loop()->counters.tcp_init does not increase across the uv_accept. */
- tcpcnt = uv_default_loop()->counters.tcp_init;
-
server = (uv_tcp_t*)timer_handle->data;
r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle);
ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.tcp_init == tcpcnt);
-
do_accept_called++;
/* Immediately close the accepted handle. */
r = uv_tcp_init(uv_default_loop(), server);
ASSERT(r == 0);
- ASSERT(uv_default_loop()->counters.tcp_init == 1);
- ASSERT(uv_default_loop()->counters.handle_init == 1);
-
r = uv_tcp_bind(server, addr);
ASSERT(r == 0);
TEST_DECLARE (getsockname_udp)
TEST_DECLARE (fail_always)
TEST_DECLARE (pass_always)
+TEST_DECLARE (spawn_fails)
TEST_DECLARE (spawn_exit_code)
TEST_DECLARE (spawn_stdout)
TEST_DECLARE (spawn_stdin)
TEST_DECLARE (thread_create)
TEST_DECLARE (strlcpy)
TEST_DECLARE (strlcat)
-TEST_DECLARE (counters_init)
TEST_DECLARE (dlerror)
TEST_DECLARE (poll_duplex)
TEST_DECLARE (poll_unidirectional)
TEST_DECLARE (poll_close)
+TEST_DECLARE (we_get_signal)
+TEST_DECLARE (we_get_signals)
#ifdef _WIN32
TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows)
TEST_DECLARE (argument_escaping)
TEST_ENTRY (poll_unidirectional)
TEST_ENTRY (poll_close)
+ TEST_ENTRY (spawn_fails)
TEST_ENTRY (spawn_exit_code)
TEST_ENTRY (spawn_stdout)
TEST_ENTRY (spawn_stdin)
TEST_ENTRY (spawn_stdout_to_file)
TEST_ENTRY (fs_poll)
TEST_ENTRY (kill)
+
+ TEST_ENTRY (we_get_signal)
+ TEST_ENTRY (we_get_signals)
+
#ifdef _WIN32
TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows)
TEST_ENTRY (argument_escaping)
TEST_ENTRY (thread_create)
TEST_ENTRY (strlcpy)
TEST_ENTRY (strlcat)
- TEST_ENTRY (counters_init)
TEST_ENTRY (dlerror)
#if 0
/* These are for testing the test runner. */
--- /dev/null
+/* 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
+
+TEST_IMPL(we_get_signal) {
+ return 0;
+}
+
+
+TEST_IMPL(we_get_signals) {
+ return 0;
+}
+
+#else /* !_WIN32 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+/* This test does not pretend to be cross-platform. */
+#include <pthread.h>
+#include <signal.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, int status) {
+ 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));
+ ASSERT(tc.ncalls == NSIGNALS);
+ ASSERT(sc.ncalls == NSIGNALS);
+
+ start_timer(loop, SIGCHLD, &tc);
+ ASSERT(0 == uv_run(loop));
+ 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));
+ ASSERT(tc.ncalls == NSIGNALS);
+ ASSERT(sc.ncalls == NSIGNALS);
+
+ 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));
+
+ 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);
+
+ return 0;
+}
+
+#endif /* _WIN32 */
int term_signal) {
printf("exit_cb\n");
exit_cb_called++;
- ASSERT(exit_status == 127);
+ ASSERT(exit_status == -1);
ASSERT(term_signal == 0);
uv_close((uv_handle_t*)process, close_cb);
}
}
+TEST_IMPL(spawn_fails) {
+ init_process_options("", exit_cb_failure_expected);
+ options.file = options.args[0] = "program-that-had-better-not-exist";
+ ASSERT(0 == uv_spawn(uv_default_loop(), &process, options));
+ ASSERT(0 != uv_is_active((uv_handle_t*)&process));
+ ASSERT(0 == uv_run(uv_default_loop()));
+ ASSERT(uv_last_error(uv_default_loop()).code == UV_ENOENT);
+ return 0;
+}
+
+
TEST_IMPL(spawn_exit_code) {
int r;
}
-wchar_t* make_program_args(char** args, int verbatim_arguments);
-wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target);
+uv_err_t 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_t* test_str[] = {
+ const WCHAR* test_str[] = {
L"HelloWorld",
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_t** test_output;
- wchar_t* command_line;
- wchar_t** cracked;
+ WCHAR** test_output;
+ WCHAR* command_line;
+ WCHAR** cracked;
size_t total_size = 0;
int i;
int num_args;
+ uv_err_t result;
char* verbatim[] = {
"cmd.exe",
"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"",
NULL
};
- wchar_t* verbatim_output;
- wchar_t* non_verbatim_output;
+ WCHAR* verbatim_output;
+ WCHAR* non_verbatim_output;
- test_output = calloc(count, sizeof(wchar_t*));
+ test_output = calloc(count, sizeof(WCHAR*));
for (i = 0; i < count; ++i) {
- test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(wchar_t));
+ 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_t));
+ command_line = calloc(total_size + 1, sizeof(WCHAR));
for (i = 0; i < count; ++i) {
wcscat(command_line, test_output[i]);
wcscat(command_line, L" ");
free(test_output[i]);
}
- verbatim_output = make_program_args(verbatim, 1);
- non_verbatim_output = make_program_args(verbatim, 0);
+ result = make_program_args(verbatim, 1, &verbatim_output);
+ ASSERT(result.code == UV_OK);
+ result = make_program_args(verbatim, 0, &non_verbatim_output);
+ ASSERT(result.code == UV_OK);
wprintf(L" verbatim_output: %s\n", verbatim_output);
wprintf(L"non_verbatim_output: %s\n", non_verbatim_output);
return 0;
}
-wchar_t* make_program_env(char** env_block);
+WCHAR* make_program_env(char** env_block);
TEST_IMPL(environment_creation) {
int i;
NULL
};
- wchar_t expected[512];
- wchar_t* ptr = expected;
- wchar_t* result;
- wchar_t* str;
+ WCHAR expected[512];
+ WCHAR* ptr = expected;
+ WCHAR* result;
+ WCHAR* str;
for (i = 0; i < sizeof(environment) / sizeof(environment[0]) - 1; i++) {
ptr += uv_utf8_to_utf16(environment[i], ptr, expected + sizeof(expected) - ptr);
}
memcpy(ptr, L"SYSTEMROOT=", sizeof(L"SYSTEMROOT="));
- ptr += sizeof(L"SYSTEMROOT=")/sizeof(wchar_t) - 1;
+ ptr += sizeof(L"SYSTEMROOT=")/sizeof(WCHAR) - 1;
ptr += GetEnvironmentVariableW(L"SYSTEMROOT", ptr, expected + sizeof(expected) - ptr);
++ptr;
memcpy(ptr, L"SYSTEMDRIVE=", sizeof(L"SYSTEMDRIVE="));
- ptr += sizeof(L"SYSTEMDRIVE=")/sizeof(wchar_t) - 1;
+ ptr += sizeof(L"SYSTEMDRIVE=")/sizeof(WCHAR) - 1;
ptr += GetEnvironmentVariableW(L"SYSTEMDRIVE", ptr, expected + sizeof(expected) - ptr);
++ptr;
*ptr = '\0';
* start busy looping when the server sends a message and the client isn't
* reading.
*/
- ASSERT(ticks <= 10);
+ ASSERT(ticks <= 20);
return 0;
}
'sources': [
'include/uv-private/uv-win.h',
'src/win/async.c',
+ 'src/win/atomicops-inl.h',
'src/win/core.c',
'src/win/dl.c',
'src/win/error.c',
'src/win/process-stdio.c',
'src/win/req.c',
'src/win/req-inl.h',
+ 'src/win/signal.c',
'src/win/stream.c',
'src/win/stream-inl.h',
'src/win/tcp.c',
'include/uv-private/eio.h',
'include/uv-private/ev.h',
'include/uv-private/uv-unix.h',
+ 'include/uv-private/uv-linux.h',
+ 'include/uv-private/uv-sunos.h',
+ 'include/uv-private/uv-darwin.h',
+ 'include/uv-private/uv-bsd.h',
'src/unix/async.c',
'src/unix/core.c',
'src/unix/dl.c',
'src/unix/pipe.c',
'src/unix/poll.c',
'src/unix/process.c',
+ 'src/unix/signal.c',
'src/unix/stream.c',
'src/unix/tcp.c',
'src/unix/thread.c',
'test/test-semaphore.c',
'test/test-shutdown-close.c',
'test/test-shutdown-eof.c',
+ 'test/test-signal.c',
'test/test-spawn.c',
'test/test-fs-poll.c',
'test/test-stdio-over-pipes.c',
'test/test-tcp-unexpected-read.c',
'test/test-threadpool.c',
'test/test-mutexes.c',
+ 'test/test-signal.c',
'test/test-thread.c',
'test/test-timer-again.c',
'test/test-timer.c',
'test/test-udp-options.c',
'test/test-udp-send-and-recv.c',
'test/test-udp-multicast-join.c',
- 'test/test-counters-init.c',
'test/test-dlerror.c',
'test/test-udp-multicast-ttl.c',
],