- libeio, located at eio/ is copyright Marc Alexander Lehmann, and
dual-licensed under the MIT license and GPL2.
+
+ - inet_pton and inet_ntop implementations, contained in src/inet.c, are
+ copyright the Internet Systems Consortium, Inc., and licensed under the ISC
+ license.
## Documentation
-See `include/uv.h`.
-
+ * [include/uv.h](https://github.com/joyent/libuv/blob/master/include/uv.h)
+ — API documentation in the form of detailed header comments.
+ * [An Introduction to libuv](http://nikhilm.github.com/uvbook/) — An
+ overview of libuv with tutorials.
## Build Instructions
RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi
RUNNER_SRC=test/runner-win.c
-uv.a: $(WIN_OBJS) src/cares.o src/fs-poll.o src/uv-common.o $(CARES_OBJS)
+uv.a: $(WIN_OBJS) src/cares.o src/fs-poll.o src/inet.o src/uv-common.o $(CARES_OBJS)
$(AR) rcs uv.a $^
src/%.o: src/%.c include/uv.h include/uv-private/uv-win.h
src/win/%.o: src/win/%.c include/uv.h include/uv-private/uv-win.h src/win/internal.h
$(CC) $(CFLAGS) -o $@ -c $<
-EIO_CPPFLAGS += $(CPPFLAGS)
-EIO_CPPFLAGS += -DEIO_STACKSIZE=65536
-EIO_CPPFLAGS += -D_GNU_SOURCE
-
clean-platform:
-rm -f src/ares/*.o
- -rm -f src/eio/*.o
-rm -f src/win/*.o
distclean-platform:
-rm -f src/ares/*.o
- -rm -f src/eio/*.o
-rm -f src/win/*.o
RUNNER_LIBS=
RUNNER_SRC=test/runner-unix.c
-uv.a: $(OBJS) src/cares.o src/fs-poll.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS)
+uv.a: $(OBJS) src/cares.o src/fs-poll.o src/inet.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS)
$(AR) rcs uv.a $^
src/%.o: src/%.c include/uv.h include/uv-private/uv-unix.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>
+#endif
+
#if __sun
# include <sys/port.h>
# include <port.h>
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;
int fd; \
-/* UV_TCP */
-#define UV_TCP_PRIVATE_FIELDS
+/* UV_TCP, idle_handle is for UV_TCP_SINGLE_ACCEPT handles */
+#define UV_TCP_PRIVATE_FIELDS \
+ uv_idle_t* idle_handle; \
/* UV_UDP */
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( 57, ENODEV, "no such device" ) \
+ XX( 58, ECANCELED, "operation canceled" )
#define UV_ERRNO_GEN(val, name, s) UV_##name = val,
* Note that handles that wrap file descriptors are closed immediately but
* close_cb will still be deferred to the next iteration of the event loop.
* It gives you a chance to free up any resources associated with the handle.
+ *
+ * In-progress requests, like uv_connect_t or uv_write_t, are cancelled and
+ * have their callbacks called asynchronously with status=-1 and the error code
+ * set to UV_ECANCELED.
*/
UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
* { .base = "4", .len = 1 }
* };
*
+ * uv_write_t req1;
+ * uv_write_t req2;
+ *
* // writes "1234"
- * uv_write(req, stream, a, 2);
- * uv_write(req, stream, b, 2);
+ * uv_write(&req1, stream, a, 2);
+ * uv_write(&req2, stream, b, 2);
*
*/
UV_EXTERN int uv_write(uv_write_t* req, uv_stream_t* handle,
UV_EXTERN int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size);
UV_EXTERN int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size);
+/* Cross-platform IPv6-capable implementation of the 'standard' inet_ntop */
+/* and inet_pton functions. On success they return UV_OK. If an error */
+/* the target of the `dst` pointer is unmodified. */
+uv_err_t uv_inet_ntop(int af, const void* src, char* dst, size_t size);
+uv_err_t uv_inet_pton(int af, const char* src, void* dst);
+
/* Gets the executable path */
UV_EXTERN int uv_exepath(char* buffer, size_t* size);
--- /dev/null
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "uv.h"
+#include "uv-common.h"
+
+
+static const uv_err_t uv_eafnosupport_ = { UV_EAFNOSUPPORT, 0 };
+static const uv_err_t uv_enospc_ = { UV_ENOSPC, 0 };
+static const uv_err_t uv_einval_ = { UV_EINVAL, 0 };
+
+static uv_err_t inet_ntop4(const unsigned char *src, char *dst, size_t size);
+static uv_err_t inet_ntop6(const unsigned char *src, char *dst, size_t size);
+static uv_err_t inet_pton4(const char *src, unsigned char *dst);
+static uv_err_t inet_pton6(const char *src, unsigned char *dst);
+
+
+uv_err_t uv_inet_ntop(int af, const void* src, char* dst, size_t size) {
+ switch (af) {
+ case AF_INET:
+ return (inet_ntop4(src, dst, size));
+ case AF_INET6:
+ return (inet_ntop6(src, dst, size));
+ default:
+ return uv_eafnosupport_;
+ }
+ /* NOTREACHED */
+}
+
+
+static uv_err_t inet_ntop4(const unsigned char *src, char *dst, size_t size) {
+ static const char fmt[] = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ size_t l;
+
+#ifndef _WIN32
+ l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
+#else
+ l = _snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
+#endif
+ if (l <= 0 || l >= size) {
+ return uv_enospc_;
+ }
+ strncpy(dst, tmp, size);
+ dst[size - 1] = '\0';
+ return uv_ok_;
+}
+
+
+static uv_err_t inet_ntop6(const unsigned char *src, char *dst, size_t size) {
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct { int base, len; } best, cur;
+ unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)];
+ int i;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < (int) sizeof(struct in6_addr); i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ best.len = 0;
+ cur.base = -1;
+ cur.len = 0;
+ for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 && (best.len == 6 ||
+ (best.len == 7 && words[7] != 0x0001) ||
+ (best.len == 5 && words[5] == 0xffff))) {
+ uv_err_t err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp));
+ if (err.code != UV_OK)
+ return err;
+ tp += strlen(tp);
+ break;
+ }
+ tp += sprintf(tp, "%x", words[i]);
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ return uv_enospc_;
+ }
+ strcpy(dst, tmp);
+ return uv_ok_;
+}
+
+
+uv_err_t uv_inet_pton(int af, const char* src, void* dst) {
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+ default:
+ return uv_eafnosupport_;
+ }
+ /* NOTREACHED */
+}
+
+
+static uv_err_t inet_pton4(const char *src, unsigned char *dst) {
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[sizeof(struct in_addr)], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int nw = *tp * 10 + (pch - digits);
+
+ if (saw_digit && *tp == 0)
+ return uv_einval_;
+ if (nw > 255)
+ return uv_einval_;
+ *tp = nw;
+ if (!saw_digit) {
+ if (++octets > 4)
+ return uv_einval_;
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return uv_einval_;
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return uv_einval_;
+ }
+ if (octets < 4)
+ return uv_einval_;
+ memcpy(dst, tmp, sizeof(struct in_addr));
+ return uv_ok_;
+}
+
+
+static uv_err_t inet_pton6(const char *src, unsigned char *dst) {
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, seen_xdigits;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', sizeof tmp);
+ endp = tp + sizeof tmp;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return uv_einval_;
+ curtok = src;
+ seen_xdigits = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (++seen_xdigits > 4)
+ return uv_einval_;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!seen_xdigits) {
+ if (colonp)
+ return uv_einval_;
+ colonp = tp;
+ continue;
+ } else if (*src == '\0') {
+ return uv_einval_;
+ }
+ if (tp + sizeof(uint16_t) > endp)
+ return uv_einval_;
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ seen_xdigits = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) {
+ uv_err_t err = inet_pton4(curtok, tp);
+ if (err.code == 0) {
+ tp += sizeof(struct in_addr);
+ seen_xdigits = 0;
+ break; /*%< '\\0' was seen by inet_pton4(). */
+ }
+ }
+ return uv_einval_;
+ }
+ if (seen_xdigits) {
+ if (tp + sizeof(uint16_t) > endp)
+ return uv_einval_;
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ if (tp == endp)
+ return uv_einval_;
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return uv_einval_;
+ memcpy(dst, tmp, sizeof tmp);
+ return uv_ok_;
+}
break;
case UV_TTY:
- case UV_TCP:
uv__stream_close((uv_stream_t*)handle);
break;
+ case UV_TCP:
+ uv__tcp_close((uv_tcp_t*)handle);
+ break;
+
case UV_UDP:
uv__udp_close((uv_udp_t*)handle);
break;
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))
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_;
}
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);
UV_STREAM_WRITABLE = 0x40, /* The stream is writable */
UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */
UV_TCP_NODELAY = 0x100, /* Disable Nagle. */
- UV_TCP_KEEPALIVE = 0x200 /* Turn on keep-alive. */
+ UV_TCP_KEEPALIVE = 0x200, /* Turn on keep-alive. */
+ UV_TCP_SINGLE_ACCEPT = 0x400 /* Only accept() when idle. */
};
inline static void uv__req_init(uv_loop_t* loop,
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);
void uv__prepare_close(uv_prepare_t* handle);
void uv__process_close(uv_process_t* handle);
void uv__stream_close(uv_stream_t* handle);
+void uv__tcp_close(uv_tcp_t* handle);
void uv__timer_close(uv_timer_t* handle);
void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle);
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));
* 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);
if (stream->connect_req) {
uv__req_unregister(stream->loop, stream->connect_req);
- uv__set_artificial_error(stream->loop, UV_EINTR);
+ uv__set_artificial_error(stream->loop, UV_ECANCELED);
stream->connect_req->cb(stream->connect_req, -1);
stream->connect_req = NULL;
}
free(req->bufs);
if (req->cb) {
- uv__set_artificial_error(req->handle->loop, UV_EINTR);
+ uv__set_artificial_error(req->handle->loop, UV_ECANCELED);
req->cb(req, -1);
}
}
if (stream->shutdown_req) {
uv__req_unregister(stream->loop, stream->shutdown_req);
- uv__set_artificial_error(stream->loop, UV_EINTR);
+ uv__set_artificial_error(stream->loop, UV_ECANCELED);
stream->shutdown_req->cb(stream->shutdown_req, -1);
stream->shutdown_req = NULL;
}
}
+static void uv__next_accept(uv_idle_t* idle, int status) {
+ uv_stream_t* stream = idle->data;
+
+ uv_idle_stop(idle);
+
+ if (stream->accepted_fd == -1)
+ uv__io_start(stream->loop, &stream->read_watcher);
+}
+
+
void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) {
int fd;
uv_stream_t* stream = container_of(w, uv_stream_t, read_watcher);
}
} else {
stream->accepted_fd = fd;
- stream->connection_cb((uv_stream_t*)stream, 0);
- if (stream->accepted_fd >= 0) {
+ stream->connection_cb(stream, 0);
+
+ if (stream->accepted_fd != -1 ||
+ (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) {
/* The user hasn't yet accepted called uv_accept() */
uv__io_stop(stream->loop, &stream->read_watcher);
- return;
+ break;
}
}
}
+
+ if (stream->fd != -1 &&
+ stream->accepted_fd == -1 &&
+ (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT))
+ {
+ /* Defer the next accept() syscall to the next event loop tick.
+ * This lets us guarantee fair load balancing in in multi-process setups.
+ * The problem is as follows:
+ *
+ * 1. Multiple processes listen on the same socket.
+ * 2. The OS scheduler commonly gives preference to one process to
+ * avoid task switches.
+ * 3. That process therefore accepts most of the new connections,
+ * leading to a (sometimes very) unevenly distributed load.
+ *
+ * Here is how we mitigate this issue:
+ *
+ * 1. Accept a connection.
+ * 2. Start an idle watcher.
+ * 3. Don't accept new connections until the idle callback fires.
+ *
+ * This works because the callback only fires when there have been
+ * no recent events, i.e. none of the watched file descriptors have
+ * recently been readable or writable.
+ */
+ uv_tcp_t* tcp = (uv_tcp_t*) stream;
+ uv_idle_start(tcp->idle_handle, uv__next_accept);
+ }
}
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);
}
-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;
static void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, int revents) {
- uv_fs_event_t *handle;
+ uv_fs_event_t *handle = NULL;
timespec_t timeout;
port_event_t pe;
int events;
(void) revents;
do {
- /* TODO use port_getn() */
+ uint_t n = 1;
+
+ /*
+ * Note that our use of port_getn() here (and not port_get()) is deliberate:
+ * there is a bug in event ports (Sun bug 6456558) whereby a zeroed timeout
+ * causes port_get() to return success instead of ETIME when there aren't
+ * actually any events (!); by using port_getn() in lieu of port_get(),
+ * we can at least workaround the bug by checking for zero returned events
+ * and treating it as we would ETIME.
+ */
do {
memset(&timeout, 0, sizeof timeout);
- r = port_get(loop->fs_fd, &pe, &timeout);
+ r = port_getn(loop->fs_fd, &pe, 1, &n, &timeout);
}
while (r == -1 && errno == EINTR);
- if (r == -1 && errno == ETIME)
+ if ((r == -1 && errno == ETIME) || n == 0)
break;
handle = (uv_fs_event_t *)pe.portev_user;
}
while (handle->fd != PORT_DELETED);
- if (handle->fd != PORT_DELETED)
+ if (handle != NULL && handle->fd != PORT_DELETED)
uv__fs_event_rearm(handle);
}
#include "uv.h"
#include "internal.h"
+#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
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;
+}
+
+
+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;
}
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) {
}
+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,
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
- int r;
+ static int single_accept = -1;
- if (tcp->delayed_error) {
- uv__set_sys_error(tcp->loop, tcp->delayed_error);
- return -1;
+ if (tcp->delayed_error)
+ return uv__set_sys_error(tcp->loop, tcp->delayed_error);
+
+ if (single_accept == -1) {
+ const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
+ single_accept = (val == NULL) || (atoi(val) != 0); /* on by default */
}
- 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 (!single_accept)
+ goto no_single_accept;
- if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_STREAM_READABLE)) {
- close(tcp->fd);
- tcp->fd = -1;
- return -1;
- }
- }
+ tcp->idle_handle = malloc(sizeof(*tcp->idle_handle));
+ if (tcp->idle_handle == NULL)
+ return uv__set_sys_error(tcp->loop, ENOMEM);
+
+ if (uv_idle_init(tcp->loop, tcp->idle_handle))
+ abort();
- assert(tcp->fd >= 0);
+ tcp->flags |= UV_TCP_SINGLE_ACCEPT;
- r = listen(tcp->fd, backlog);
- if (r < 0) {
- uv__set_sys_error(tcp->loop, errno);
+no_single_accept:
+ if (maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE))
return -1;
- }
+
+ if (listen(tcp->fd, backlog))
+ return uv__set_sys_error(tcp->loop, errno);
tcp->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;
}
int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
+ if (enable)
+ handle->flags |= UV_TCP_SINGLE_ACCEPT;
+ else
+ handle->flags &= ~UV_TCP_SINGLE_ACCEPT;
return 0;
}
+
+
+void uv__tcp_close(uv_tcp_t* handle) {
+ if (handle->idle_handle)
+ uv_close((uv_handle_t*)handle->idle_handle, (uv_close_cb)free);
+
+ uv__stream_close((uv_stream_t*)handle);
+}
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);
return r;
}
+
+#endif /* defined(__APPLE__) && defined(__MACH__) */
#include <stdlib.h> /* malloc */
#include <string.h> /* memset */
-/* use inet_pton from c-ares if necessary */
-#include "ares_config.h"
-#include "ares/inet_net_pton.h"
-#include "ares/inet_ntop.h"
#define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
- ares_inet_pton(AF_INET6, ip, &addr.sin6_addr);
+ uv_inet_pton(AF_INET6, ip, &addr.sin6_addr);
return addr;
}
int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size) {
- const char* d = ares_inet_ntop(AF_INET, &src->sin_addr, dst, size);
- return d != dst;
+ uv_err_t err = uv_inet_ntop(AF_INET, &src->sin_addr, dst, size);
+ return err.code != UV_OK;
}
int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size) {
- const char* d = ares_inet_ntop(AF_INET6, &src->sin6_addr, dst, size);
- return d != dst;
+ uv_err_t err = uv_inet_ntop(AF_INET6, &src->sin6_addr, dst, size);
+ return err.code != UV_OK;
}
case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT;
case ERROR_MOD_NOT_FOUND: return UV_ENOENT;
case ERROR_PATH_NOT_FOUND: return UV_ENOENT;
+ case WSANO_DATA: return UV_ENOENT;
case ERROR_ACCESS_DENIED: return UV_EPERM;
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
case ERROR_NOACCESS: return UV_EACCES;
/* Already closing. Cancel the shutdown. */
if (req->cb) {
- uv__set_sys_error(loop, WSAEINTR);
+ uv__set_artificial_error(loop, UV_ECANCELED);
req->cb(req, -1);
}
void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
int status;
- int sys_error;
unsigned int i;
uv_tcp_accept_t* req;
if (handle->flags & UV_HANDLE_CLOSING) {
status = -1;
- sys_error = WSAEINTR;
+ uv__set_artificial_error(loop, UV_ECANCELED);
} else if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) {
status = 0;
handle->flags |= UV_HANDLE_SHUT;
} else {
status = -1;
- sys_error = WSAGetLastError();
+ uv__set_sys_error(loop, WSAGetLastError());
}
if (handle->shutdown_req->cb) {
- if (status == -1) {
- uv__set_sys_error(loop, sys_error);
- }
handle->shutdown_req->cb(handle->shutdown_req, status);
}
/* TTY shutdown is really just a no-op */
if (handle->shutdown_req->cb) {
if (handle->flags & UV_HANDLE_CLOSING) {
- uv__set_sys_error(loop, WSAEINTR);
+ uv__set_artificial_error(loop, UV_ECANCELED);
handle->shutdown_req->cb(handle->shutdown_req, -1);
} else {
handle->shutdown_req->cb(handle->shutdown_req, 0);
return;
if (nread == -1) {
- ASSERT(uv_last_error(loop).code == UV_EINTR); /* FIXME change error code */
+ ASSERT(uv_last_error(loop).code == UV_ECANCELED);
return;
}
/* 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;
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)
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)
#include "task.h"
#include <string.h>
-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;
}
static void shutdown_cb(uv_shutdown_t* req, int status) {
+ int err = uv_last_error(uv_default_loop()).code;
ASSERT(req == &shutdown_req);
- ASSERT(status == 0 ||
- (status == -1 && uv_last_error(uv_default_loop()).code == UV_EINTR));
+ ASSERT(status == 0 || (status == -1 && err == UV_ECANCELED));
shutdown_cb_called++;
}
static void connect_cb(uv_connect_t* req, int status) {
ASSERT(status == -1);
- ASSERT(uv_last_error(req->handle->loop).code == UV_EINTR);
+ ASSERT(uv_last_error(req->handle->loop).code == UV_ECANCELED);
uv_timer_stop(&timer2_handle);
connect_cb_called++;
}
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) {
ASSERT(0 && "alloc_cb should not have been called");
+ /* Satisfy the compiler. */
+ return uv_buf_init(NULL, 0);
}
'include/uv-private/tree.h',
'src/cares.c',
'src/fs-poll.c',
+ 'src/inet.c',
'src/uv-common.c',
'src/uv-common.h',
'src/ares/ares_cancel.c',
'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',