diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index 91c0d20c..b330830b 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -40,10 +40,17 @@ #include #include -#include #include #include +#if defined(__APPLE__) && defined(__MACH__) +# include +# include +# include +#else +# include +#endif + #if __sun # include # include @@ -67,7 +74,11 @@ typedef pthread_once_t uv_once_t; 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 /* Platform-specific definitions for uv_spawn support. */ typedef gid_t uv_gid_t; @@ -94,11 +105,8 @@ struct uv__io_s { #if __linux__ # define UV_LOOP_PRIVATE_PLATFORM_FIELDS \ - /* RB_HEAD(uv__inotify_watchers, uv_fs_event_s) */ \ - struct uv__inotify_watchers { \ - struct uv_fs_event_s* rbh_root; \ - } inotify_watchers; \ uv__io_t inotify_read_watcher; \ + void* inotify_watchers; \ int inotify_fd; #elif defined(PORT_SOURCE_FILE) # define UV_LOOP_PRIVATE_PLATFORM_FIELDS \ @@ -266,15 +274,11 @@ struct uv__io_s { #if defined(__linux__) #define UV_FS_EVENT_PRIVATE_FIELDS \ - /* RB_ENTRY(fs_event_s) node; */ \ - struct { \ - struct uv_fs_event_s* rbe_left; \ - struct uv_fs_event_s* rbe_right; \ - struct uv_fs_event_s* rbe_parent; \ - int rbe_color; \ - } node; \ + ngx_queue_t watchers; \ uv_fs_event_cb cb; \ - int fd; \ + int wd; \ + void* pad0; \ + void* pad1; \ #elif defined(__APPLE__) \ || defined(__FreeBSD__) \ diff --git a/src/unix/core.c b/src/unix/core.c index 2f881596..0e4919c6 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -228,7 +228,7 @@ void uv_loop_delete(uv_loop_t* loop) { static unsigned int uv__poll_timeout(uv_loop_t* loop) { - if (!uv__has_active_handles(loop)) + if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) return 0; if (!ngx_queue_empty(&loop->idle_handles)) diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index d916b0b1..be8006c5 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -139,9 +139,23 @@ char** uv_setup_args(int argc, char** argv) { uv_err_t uv_set_process_title(const char* title) { + int oid[4]; + if (process_title) free(process_title); process_title = strdup(title); - setproctitle(title); + + oid[0] = CTL_KERN; + oid[1] = KERN_PROC; + oid[2] = KERN_PROC_ARGS; + oid[3] = getpid(); + + sysctl(oid, + ARRAY_SIZE(oid), + NULL, + NULL, + process_title, + strlen(process_title) + 1); + return uv_ok_; } @@ -261,7 +275,7 @@ uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return uv__new_sys_error(ENOMEM); } - if (sysctlbyname("kern.cp_times", &cp_times, &size, NULL, 0) < 0) { + if (sysctlbyname("kern.cp_times", cp_times, &size, NULL, 0) < 0) { free(cp_times); free(*cpu_infos); return uv__new_sys_error(errno); diff --git a/src/unix/internal.h b/src/unix/internal.h index 23f16f7b..dd46c95e 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -139,8 +139,6 @@ int uv__stream_open(uv_stream_t*, int fd, int flags); void uv__stream_destroy(uv_stream_t* stream); void uv__server_io(uv_loop_t* loop, uv__io_t* watcher, int events); int uv__accept(int sockfd); -int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, - socklen_t addrlen, uv_connect_cb cb); /* tcp */ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); diff --git a/src/unix/linux/inotify.c b/src/unix/linux/inotify.c index 54525bc7..51ea996a 100644 --- a/src/unix/linux/inotify.c +++ b/src/unix/linux/inotify.c @@ -33,6 +33,18 @@ #include #include +struct watcher_list { + RB_ENTRY(watcher_list) entry; + ngx_queue_t watchers; + char* path; + int wd; +}; + +struct watcher_root { + struct watcher_list* rbh_root; +}; +#define CAST(p) ((struct watcher_root*)(p)) + /* Don't look aghast, this is exactly how glibc's basename() works. */ static char* basename_r(const char* path) { @@ -41,14 +53,15 @@ static char* basename_r(const char* path) { } -static int compare_watchers(const uv_fs_event_t* a, const uv_fs_event_t* b) { - if (a->fd < b->fd) return -1; - if (a->fd > b->fd) return 1; +static int compare_watchers(const struct watcher_list* a, + const struct watcher_list* b) { + if (a->wd < b->wd) return -1; + if (a->wd > b->wd) return 1; return 0; } -RB_GENERATE_STATIC(uv__inotify_watchers, uv_fs_event_s, node, compare_watchers) +RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) static void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, int revents); @@ -95,36 +108,27 @@ static int init_inotify(uv_loop_t* loop) { } -static void add_watcher(uv_fs_event_t* handle) { - RB_INSERT(uv__inotify_watchers, &handle->loop->inotify_watchers, handle); +static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { + struct watcher_list w; + w.wd = wd; + return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w); } -static uv_fs_event_t* find_watcher(uv_loop_t* loop, int wd) { - uv_fs_event_t handle; - handle.fd = wd; - return RB_FIND(uv__inotify_watchers, &loop->inotify_watchers, &handle); -} - - -static void remove_watcher(uv_fs_event_t* handle) { - RB_REMOVE(uv__inotify_watchers, &handle->loop->inotify_watchers, handle); -} - - -static void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, int events) { +static void uv__inotify_read(uv_loop_t* loop, uv__io_t* dummy, int events) { const struct uv__inotify_event* e; - uv_fs_event_t* handle; - const char* filename; + struct watcher_list* w; + uv_fs_event_t* h; + ngx_queue_t* q; + const char* path; ssize_t size; const char *p; /* needs to be large enough for sizeof(inotify_event) + strlen(filename) */ char buf[4096]; while (1) { - do { - size = read(loop->inotify_fd, buf, sizeof buf); - } + do + size = read(loop->inotify_fd, buf, sizeof(buf)); while (size == -1 && errno == EINTR); if (size == -1) { @@ -144,17 +148,20 @@ static void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, int events) { if (e->mask & ~(UV__IN_ATTRIB|UV__IN_MODIFY)) events |= UV_RENAME; - handle = find_watcher(loop, e->wd); - if (handle == NULL) - continue; /* Handle has already been closed. */ + w = find_watcher(loop, e->wd); + if (w == NULL) + continue; /* Stale event, no watchers left. */ /* inotify does not return the filename when monitoring a single file * for modifications. Repurpose the filename for API compatibility. * I'm not convinced this is a good thing, maybe it should go. */ - filename = e->len ? (const char*) (e + 1) : basename_r(handle->filename); + path = e->len ? (const char*) (e + 1) : basename_r(w->path); - handle->cb(handle, filename, events, 0); + ngx_queue_foreach(q, &w->watchers) { + h = ngx_queue_data(q, uv_fs_event_t, watchers); + h->cb(h, path, events, 0); + } } } } @@ -162,9 +169,10 @@ static void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, int events) { int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, - const char* filename, + const char* path, uv_fs_event_cb cb, int flags) { + struct watcher_list* w; int events; int wd; @@ -184,26 +192,50 @@ int uv_fs_event_init(uv_loop_t* loop, | UV__IN_MOVED_FROM | UV__IN_MOVED_TO; - wd = uv__inotify_add_watch(loop->inotify_fd, filename, events); - if (wd == -1) return uv__set_sys_error(loop, errno); + wd = uv__inotify_add_watch(loop->inotify_fd, path, events); + if (wd == -1) + return uv__set_sys_error(loop, errno); + w = find_watcher(loop, wd); + if (w) + goto no_insert; + + w = malloc(sizeof(*w) + strlen(path) + 1); + if (w == NULL) + return uv__set_sys_error(loop, ENOMEM); + + w->wd = wd; + w->path = strcpy((char*)(w + 1), path); + ngx_queue_init(&w->watchers); + RB_INSERT(watcher_root, CAST(&loop->inotify_watchers), w); + +no_insert: uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); uv__handle_start(handle); /* FIXME shouldn't start automatically */ - handle->filename = strdup(filename); + ngx_queue_insert_tail(&w->watchers, &handle->watchers); + handle->filename = w->path; handle->cb = cb; - handle->fd = wd; - add_watcher(handle); + handle->wd = wd; return 0; } void uv__fs_event_close(uv_fs_event_t* handle) { - uv__inotify_rm_watch(handle->loop->inotify_fd, handle->fd); - remove_watcher(handle); - handle->fd = -1; + struct watcher_list* w; - free(handle->filename); + w = find_watcher(handle->loop, handle->wd); + assert(w != NULL); + + handle->wd = -1; handle->filename = NULL; uv__handle_stop(handle); + ngx_queue_remove(&handle->watchers); + + if (ngx_queue_empty(&w->watchers)) { + /* No watchers left for this path. Clean up. */ + RB_REMOVE(watcher_root, CAST(&handle->loop->inotify_watchers), w); + uv__inotify_rm_watch(handle->loop->inotify_fd, w->wd); + free(w); + } } diff --git a/src/unix/loop.c b/src/unix/loop.c index 75fad436..fcf9faf3 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -54,7 +54,7 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) { eio_channel_init(&loop->uv_eio_channel, loop); #if __linux__ - RB_INIT(&loop->inotify_watchers); + loop->inotify_watchers = NULL; loop->inotify_fd = -1; #endif #if HAVE_PORTS_FS diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 317ac67c..957e96f8 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -170,18 +170,17 @@ void uv_pipe_connect(uv_connect_t* req, uv_connect_cb cb) { struct sockaddr_un saddr; int saved_errno; - int sockfd; - int status; + int new_sock; + int err; int r; saved_errno = errno; - sockfd = -1; - status = -1; + new_sock = (handle->fd == -1); + err = -1; - if ((sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - uv__set_sys_error(handle->loop, errno); - goto out; - } + if (new_sock) + if ((handle->fd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + goto out; memset(&saddr, 0, sizeof saddr); uv_strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); @@ -191,25 +190,25 @@ void uv_pipe_connect(uv_connect_t* req, * is either there or not. */ do { - r = connect(sockfd, (struct sockaddr*)&saddr, sizeof saddr); + r = connect(handle->fd, (struct sockaddr*)&saddr, sizeof saddr); } while (r == -1 && errno == EINTR); - if (r == -1) { - status = errno; - close(sockfd); + if (r == -1) goto out; - } - uv__stream_open((uv_stream_t*)handle, - sockfd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); + if (new_sock) + if (uv__stream_open((uv_stream_t*)handle, + handle->fd, + UV_STREAM_READABLE | UV_STREAM_WRITABLE)) + goto out; + uv__io_start(handle->loop, &handle->read_watcher); uv__io_start(handle->loop, &handle->write_watcher); - status = 0; + err = 0; out: - handle->delayed_error = status; /* Passed to callback. */ + handle->delayed_error = err ? errno : 0; /* Passed to callback. */ handle->connect_req = req; uv__req_init(handle->loop, req, UV_CONNECT); diff --git a/src/unix/stream.c b/src/unix/stream.c index fd024ed3..47e32b24 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -784,9 +784,6 @@ static void uv__stream_connect(uv_stream_t* stream) { if (error == EINPROGRESS) return; - if (error == 0) - uv__io_start(stream->loop, &stream->read_watcher); - stream->connect_req = NULL; uv__req_unregister(stream->loop, req); @@ -797,65 +794,6 @@ static void uv__stream_connect(uv_stream_t* stream) { } -int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, - socklen_t addrlen, uv_connect_cb cb) { - int sockfd; - int r; - - if (stream->type != UV_TCP) - return uv__set_sys_error(stream->loop, ENOTSOCK); - - if (stream->connect_req) - return uv__set_sys_error(stream->loop, EALREADY); - - if (stream->fd <= 0) { - sockfd = uv__socket(addr->sa_family, SOCK_STREAM, 0); - - if (sockfd == -1) - return uv__set_sys_error(stream->loop, errno); - - if (uv__stream_open(stream, - sockfd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE)) { - close(sockfd); - return -1; - } - } - - stream->delayed_error = 0; - - do - r = connect(stream->fd, addr, addrlen); - while (r == -1 && errno == EINTR); - - if (r == -1) { - if (errno == EINPROGRESS) - ; /* not an error */ - else if (errno == ECONNREFUSED) - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. - */ - stream->delayed_error = errno; - else - return uv__set_sys_error(stream->loop, errno); - } - - uv__req_init(stream->loop, req, UV_CONNECT); - req->cb = cb; - req->handle = stream; - ngx_queue_init(&req->queue); - stream->connect_req = req; - - uv__io_start(stream->loop, &stream->write_watcher); - - if (stream->delayed_error) - uv__io_feed(stream->loop, &stream->write_watcher, UV__IO_WRITE); - - return 0; -} - - 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; diff --git a/src/unix/tcp.c b/src/unix/tcp.c index 07ad2d9e..233be825 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -34,6 +34,26 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { } +static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { + int sockfd; + + if (handle->fd != -1) + return 0; + + sockfd = uv__socket(domain, SOCK_STREAM, 0); + + if (sockfd == -1) + return uv__set_sys_error(handle->loop, errno); + + if (uv__stream_open((uv_stream_t*)handle, sockfd, flags)) { + close(sockfd); + return -1; + } + + return 0; +} + + static int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, @@ -44,23 +64,8 @@ static int uv__bind(uv_tcp_t* tcp, saved_errno = errno; status = -1; - if (tcp->fd < 0) { - if ((tcp->fd = uv__socket(domain, SOCK_STREAM, 0)) == -1) { - uv__set_sys_error(tcp->loop, errno); - goto out; - } - - if (uv__stream_open((uv_stream_t*)tcp, - tcp->fd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE)) { - close(tcp->fd); - tcp->fd = -1; - status = -2; - goto out; - } - } - - assert(tcp->fd >= 0); + if (maybe_new_socket(tcp, domain, UV_STREAM_READABLE|UV_STREAM_WRITABLE)) + return -1; tcp->delayed_error = 0; if (bind(tcp->fd, addr, addrsize) == -1) { @@ -79,6 +84,58 @@ out: } +static int uv__connect(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr* addr, + socklen_t addrlen, + uv_connect_cb cb) { + int r; + + assert(handle->type == UV_TCP); + + if (handle->connect_req) + return uv__set_sys_error(handle->loop, EALREADY); + + if (maybe_new_socket(handle, + addr->sa_family, + UV_STREAM_READABLE|UV_STREAM_WRITABLE)) { + return -1; + } + + handle->delayed_error = 0; + + do + r = connect(handle->fd, addr, addrlen); + while (r == -1 && errno == EINTR); + + if (r == -1) { + if (errno == EINPROGRESS) + ; /* not an error */ + else if (errno == ECONNREFUSED) + /* If we get a ECONNREFUSED wait until the next tick to report the + * error. Solaris wants to report immediately--other unixes want to + * wait. + */ + handle->delayed_error = errno; + else + return uv__set_sys_error(handle->loop, errno); + } + + uv__req_init(handle->loop, req, UV_CONNECT); + req->cb = cb; + req->handle = (uv_stream_t*) handle; + ngx_queue_init(&req->queue); + handle->connect_req = req; + + uv__io_start(handle->loop, &handle->write_watcher); + + if (handle->delayed_error) + uv__io_feed(handle->loop, &handle->write_watcher, UV__IO_WRITE); + + return 0; +} + + int uv__tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { return uv__bind(handle, AF_INET, @@ -170,33 +227,14 @@ out: int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - int r; + if (tcp->delayed_error) + return uv__set_sys_error(tcp->loop, tcp->delayed_error); - if (tcp->delayed_error) { - uv__set_sys_error(tcp->loop, tcp->delayed_error); + if (maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE)) return -1; - } - if (tcp->fd < 0) { - if ((tcp->fd = uv__socket(AF_INET, SOCK_STREAM, 0)) == -1) { - uv__set_sys_error(tcp->loop, errno); - return -1; - } - - if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_STREAM_READABLE)) { - close(tcp->fd); - tcp->fd = -1; - return -1; - } - } - - assert(tcp->fd >= 0); - - r = listen(tcp->fd, backlog); - if (r < 0) { - uv__set_sys_error(tcp->loop, errno); - return -1; - } + if (listen(tcp->fd, backlog)) + return uv__set_sys_error(tcp->loop, errno); tcp->connection_cb = cb; @@ -209,37 +247,31 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { int uv__tcp_connect(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr_in address, - uv_connect_cb cb) { - int saved_errno = errno; + uv_tcp_t* handle, + struct sockaddr_in addr, + uv_connect_cb cb) { + int saved_errno; int status; - status = uv__connect(req, - (uv_stream_t*)handle, - (struct sockaddr*)&address, - sizeof address, - cb); - + saved_errno = errno; + status = uv__connect(req, handle, (struct sockaddr*)&addr, sizeof addr, cb); errno = saved_errno; + return status; } int uv__tcp_connect6(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr_in6 address, - uv_connect_cb cb) { - int saved_errno = errno; + uv_tcp_t* handle, + struct sockaddr_in6 addr, + uv_connect_cb cb) { + int saved_errno; int status; - status = uv__connect(req, - (uv_stream_t*)handle, - (struct sockaddr*)&address, - sizeof address, - cb); - + saved_errno = errno; + status = uv__connect(req, handle, (struct sockaddr*)&addr, sizeof addr, cb); errno = saved_errno; + return status; } diff --git a/src/unix/thread.c b/src/unix/thread.c index cd4e3333..67c78769 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -78,7 +78,7 @@ int uv_mutex_trylock(uv_mutex_t* mutex) { r = pthread_mutex_trylock(mutex); - if (r && r != EAGAIN) + if (r && r != EBUSY && r != EAGAIN) abort(); if (r) @@ -119,7 +119,7 @@ int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { r = pthread_rwlock_tryrdlock(rwlock); - if (r && r != EAGAIN) + if (r && r != EBUSY && r != EAGAIN) abort(); if (r) @@ -146,7 +146,7 @@ int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { r = pthread_rwlock_trywrlock(rwlock); - if (r && r != EAGAIN) + if (r && r != EBUSY && r != EAGAIN) abort(); if (r) @@ -167,6 +167,44 @@ void uv_once(uv_once_t* guard, void (*callback)(void)) { abort(); } +#if defined(__APPLE__) && defined(__MACH__) + +int uv_sem_init(uv_sem_t* sem, unsigned int value) { + return semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value); +} + + +void uv_sem_destroy(uv_sem_t* sem) { + if (semaphore_destroy(mach_task_self(), *sem)) + abort(); +} + + +void uv_sem_post(uv_sem_t* sem) { + if (semaphore_signal(*sem)) + abort(); +} + + +void uv_sem_wait(uv_sem_t* sem) { + if (semaphore_wait(*sem)) + abort(); +} + + +int uv_sem_trywait(uv_sem_t* sem) { + mach_timespec_t interval; + + interval.tv_sec = 0; + interval.tv_nsec = 0; + + if (semaphore_timedwait(*sem, interval) == KERN_SUCCESS) + return 0; + else + return -1; +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ int uv_sem_init(uv_sem_t* sem, unsigned int value) { return sem_init(sem, 0, value); @@ -209,3 +247,5 @@ int uv_sem_trywait(uv_sem_t* sem) { return r; } + +#endif /* defined(__APPLE__) && defined(__MACH__) */ diff --git a/test/benchmark-sizes.c b/test/benchmark-sizes.c index 6d16e3ca..b9cf74f2 100644 --- a/test/benchmark-sizes.c +++ b/test/benchmark-sizes.c @@ -36,7 +36,9 @@ BENCHMARK_IMPL(sizes) { 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_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)); + LOGF("uv_loop_t: %u bytes\n", (unsigned int) sizeof(uv_loop_t)); return 0; } diff --git a/test/test-fs-event.c b/test/test-fs-event.c index fe7bce2a..4d4cfbc3 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -85,6 +85,13 @@ static void close_cb(uv_handle_t* handle) { close_cb_called++; } +static void fail_cb(uv_fs_event_t* handle, + const char* path, + int events, + int status) { + ASSERT(0 && "fail_cb called"); +} + static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; @@ -159,6 +166,13 @@ static void timer_cb_touch(uv_timer_t* timer, int status) { timer_cb_touch_called++; } +static void timer_cb_watch_twice(uv_timer_t* handle, int status) { + uv_fs_event_t* handles = handle->data; + uv_close((uv_handle_t*) (handles + 0), NULL); + uv_close((uv_handle_t*) (handles + 1), NULL); + uv_close((uv_handle_t*) handle, NULL); +} + TEST_IMPL(fs_event_watch_dir) { uv_fs_t fs_req; uv_loop_t* loop = uv_default_loop(); @@ -225,6 +239,24 @@ TEST_IMPL(fs_event_watch_file) { return 0; } +TEST_IMPL(fs_event_watch_file_twice) { + const char path[] = "test/fixtures/empty_file"; + uv_fs_event_t watchers[2]; + uv_timer_t timer; + uv_loop_t* loop; + + loop = uv_default_loop(); + timer.data = watchers; + + ASSERT(0 == uv_fs_event_init(loop, watchers + 0, path, fail_cb, 0)); + ASSERT(0 == uv_fs_event_init(loop, watchers + 1, path, fail_cb, 0)); + ASSERT(0 == uv_timer_init(loop, &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0)); + ASSERT(0 == uv_run(loop)); + + return 0; +} + TEST_IMPL(fs_event_watch_file_current_dir) { uv_timer_t timer; uv_loop_t* loop; diff --git a/test/test-hrtime.c b/test/test-hrtime.c index 2a9156ec..72a4d4b1 100644 --- a/test/test-hrtime.c +++ b/test/test-hrtime.c @@ -47,7 +47,7 @@ TEST_IMPL(hrtime) { /* Check that the difference between the two hrtime values is somewhat in */ /* the range we expect it to be. */ ASSERT(diff > (uint64_t) 25 * NANOSEC / MILLISEC); - ASSERT(diff < (uint64_t) 60 * NANOSEC / MILLISEC); + ASSERT(diff < (uint64_t) 80 * NANOSEC / MILLISEC); --i; } return 0; diff --git a/test/test-list.h b/test/test-list.h index c1836fbd..19670e56 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -56,6 +56,7 @@ TEST_DECLARE (tcp_close) TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_error) TEST_DECLARE (tcp_write_to_half_open_connection) +TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_fault) @@ -157,6 +158,7 @@ TEST_DECLARE (fs_stat_missing_path) TEST_DECLARE (fs_read_file_eof) TEST_DECLARE (fs_event_watch_dir) TEST_DECLARE (fs_event_watch_file) +TEST_DECLARE (fs_event_watch_file_twice) TEST_DECLARE (fs_event_watch_file_current_dir) TEST_DECLARE (fs_event_no_callback_on_close) TEST_DECLARE (fs_event_immediate_close) @@ -251,6 +253,7 @@ TASK_LIST_START TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_error) TEST_ENTRY (tcp_write_to_half_open_connection) + TEST_ENTRY (tcp_unexpected_read) TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrnotavail) @@ -394,6 +397,7 @@ TASK_LIST_START TEST_ENTRY (fs_file_open_append) TEST_ENTRY (fs_event_watch_dir) TEST_ENTRY (fs_event_watch_file) + TEST_ENTRY (fs_event_watch_file_twice) TEST_ENTRY (fs_event_watch_file_current_dir) TEST_ENTRY (fs_event_no_callback_on_close) TEST_ENTRY (fs_event_immediate_close) diff --git a/test/test-process-title.c b/test/test-process-title.c index 59fceda3..13d9dddf 100644 --- a/test/test-process-title.c +++ b/test/test-process-title.c @@ -23,20 +23,27 @@ #include "task.h" #include -TEST_IMPL(process_title) { + +static void set_title(const char* title) { char buffer[512]; uv_err_t err; err = uv_get_process_title(buffer, sizeof(buffer)); ASSERT(UV_OK == err.code); - err = uv_set_process_title("new title"); + err = uv_set_process_title(title); ASSERT(UV_OK == err.code); err = uv_get_process_title(buffer, sizeof(buffer)); ASSERT(UV_OK == err.code); - ASSERT(strcmp(buffer, "new title") == 0); + ASSERT(strcmp(buffer, title) == 0); +} + +TEST_IMPL(process_title) { + /* Check for format string vulnerabilities. */ + set_title("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"); + set_title("new title"); return 0; } diff --git a/test/test-tcp-unexpected-read.c b/test/test-tcp-unexpected-read.c new file mode 100644 index 00000000..7adb4dae --- /dev/null +++ b/test/test-tcp-unexpected-read.c @@ -0,0 +1,111 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static uv_check_t check_handle; +static uv_timer_t timer_handle; +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; +static uv_tcp_t peer_handle; +static uv_write_t write_req; +static uv_connect_t connect_req; + +static unsigned long ticks; /* event loop ticks */ + + +static void check_cb(uv_check_t* handle, int status) { + ticks++; +} + + +static void timer_cb(uv_timer_t* handle, int status) { + uv_close((uv_handle_t*) &check_handle, NULL); + uv_close((uv_handle_t*) &timer_handle, NULL); + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); + uv_close((uv_handle_t*) &peer_handle, NULL); +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + ASSERT(0 && "alloc_cb should not have been called"); +} + + +static void read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { + ASSERT(0 && "read_cb should not have been called"); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &client_handle); + ASSERT(0 == status); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &peer_handle); + ASSERT(0 == status); +} + + +static void connection_cb(uv_stream_t* handle, int status) { + uv_buf_t buf; + + buf = uv_buf_init("PING", 4); + + ASSERT(0 == status); + ASSERT(0 == uv_tcp_init(uv_default_loop(), &peer_handle)); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &peer_handle, + &buf, 1, write_cb)); +} + + +TEST_IMPL(tcp_unexpected_read) { + struct sockaddr_in addr; + uv_loop_t* loop; + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + loop = uv_default_loop(); + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1000, 0)); + ASSERT(0 == uv_check_init(loop, &check_handle)); + ASSERT(0 == uv_check_start(&check_handle, check_cb)); + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_bind(&server_handle, addr)); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + ASSERT(0 == uv_tcp_connect(&connect_req, &client_handle, addr, connect_cb)); + ASSERT(0 == uv_run(loop)); + + /* This is somewhat inexact but the idea is that the event loop should not + * start busy looping when the server sends a message and the client isn't + * reading. + */ + ASSERT(ticks <= 10); + + return 0; +} diff --git a/uv.gyp b/uv.gyp index c14df2cc..3e57f12c 100644 --- a/uv.gyp +++ b/uv.gyp @@ -358,6 +358,7 @@ 'test/test-tcp-write-error.c', 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-writealot.c', + 'test/test-tcp-unexpected-read.c', 'test/test-threadpool.c', 'test/test-mutexes.c', 'test/test-thread.c',