Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74afcb33e2 | ||
|
|
b38158dc47 | ||
|
|
7efe7d1dc3 | ||
|
|
c447d9058c | ||
|
|
d4ff8fd5c1 | ||
|
|
65223248da | ||
|
|
82f025e036 | ||
|
|
cbc83c6d0c | ||
|
|
fa0fa2fac4 | ||
|
|
8af26a7b84 | ||
|
|
8f931a229a | ||
|
|
b52d74e21b | ||
|
|
84fe1e744a | ||
|
|
cc4d42a89a | ||
|
|
e28a5d55ba | ||
|
|
2797aa329e | ||
|
|
4dc978825d | ||
|
|
5c274ae47b | ||
|
|
80b6f6912f | ||
|
|
37aa4aa9b9 | ||
|
|
2773e1181d | ||
|
|
22725f2433 | ||
|
|
91d3598475 |
2
AUTHORS
2
AUTHORS
@ -134,3 +134,5 @@ Michael Hudson-Doyle <michael.hudson@linaro.org>
|
||||
Helge Deller <deller@gmx.de>
|
||||
Logan Rosen <loganrosen@gmail.com>
|
||||
Kenneth Perry <thothonegan@gmail.com>
|
||||
Michael Penick <michael.penick@datastax.com>
|
||||
Stephen von Takach <steve@advancedcontrol.com.au>
|
||||
|
||||
50
ChangeLog
50
ChangeLog
@ -1,3 +1,53 @@
|
||||
2016.06.14, Version 0.10.37 (Stable), b38158dc473f1534c5a819e6a8afa76d0657ffa5
|
||||
|
||||
Changes since version 0.10.36:
|
||||
|
||||
* build: update the location of gyp (Stephen von Takach)
|
||||
|
||||
* linux: fix epoll_pwait() fallback on arm64 (Ben Noordhuis)
|
||||
|
||||
* test: fix fs_chown when running as root (Ben Noordhuis)
|
||||
|
||||
* tests: skip some tests when network is unreachable (Luca Bruno)
|
||||
|
||||
* unix: do not discard environmental LDFLAGS (Luca Bruno)
|
||||
|
||||
* src: replace ngx_queue_split with ngx_queue_move (Ben Noordhuis)
|
||||
|
||||
* unix: use ngx_queue_move when iterating over lists (Ben Noordhuis)
|
||||
|
||||
* win: fix unsavory rwlock fallback implementation (Bert Belder)
|
||||
|
||||
* unix: map ENFILE errno (Saúl Ibarra Corretgé)
|
||||
|
||||
* doc: add note indicating branch status (Saúl Ibarra Corretgé)
|
||||
|
||||
|
||||
2015.02.27, Version 0.10.36 (Stable), cc4d42a89a2a0ae0ff8e14321de086eba3c3b4ca
|
||||
|
||||
Changes since version 0.10.35:
|
||||
|
||||
* stream: ignore EINVAL for SO_OOBINLINE on OS X (Fedor Indutny)
|
||||
|
||||
|
||||
2015.02.25, Version 0.10.35 (Stable), 4dc978825d870643bbaa4660f71d22975efba29e
|
||||
|
||||
Changes since version 0.10.34:
|
||||
|
||||
* stream: use SO_OOBINLINE on OS X (Fedor Indutny)
|
||||
|
||||
|
||||
2015.02.21, Version 0.10.34 (Stable), 37aa4aa9b9712c778d7b249563e868cabfdb8332
|
||||
|
||||
Changes since version 0.10.33:
|
||||
|
||||
* unix: add atomic-ops.h (Ben Noordhuis)
|
||||
|
||||
* unix: fix for uv_async data race (Michael Penick)
|
||||
|
||||
* unix: call setgoups before calling setuid/setgid (Saúl Ibarra Corretgé)
|
||||
|
||||
|
||||
2015.01.29, Version 0.10.33 (Stable), 7a2253d33ad8215a26c1b34f1952aee7242dd687
|
||||
|
||||
Changes since version 0.10.32:
|
||||
|
||||
11
README.md
11
README.md
@ -6,6 +6,9 @@ eventually contain all platform differences in this library.
|
||||
|
||||
http://nodejs.org/
|
||||
|
||||
**This branch only receives security fixes and will be EOL'd by the end of 2016,
|
||||
please switch to version v1.x**
|
||||
|
||||
## Features
|
||||
|
||||
* Non-blocking TCP sockets
|
||||
@ -81,13 +84,7 @@ To have GYP generate build script for another system, make sure that
|
||||
you have Python 2.6 or 2.7 installed, then checkout GYP into the
|
||||
project tree manually:
|
||||
|
||||
mkdir -p build
|
||||
svn co http://gyp.googlecode.com/svn/trunk build/gyp
|
||||
|
||||
Or:
|
||||
|
||||
mkdir -p build
|
||||
git clone https://git.chromium.org/external/gyp.git build/gyp
|
||||
git clone https://chromium.googlesource.com/external/gyp.git build/gyp
|
||||
|
||||
Unix users run
|
||||
|
||||
|
||||
1
build.mk
1
build.mk
@ -127,6 +127,7 @@ TESTS= \
|
||||
test/test-tcp-connect-timeout.o \
|
||||
test/test-tcp-flags.o \
|
||||
test/test-tcp-open.o \
|
||||
test/test-tcp-oob.o \
|
||||
test/test-tcp-read-stop.o \
|
||||
test/test-tcp-shutdown-after-write.o \
|
||||
test/test-tcp-unexpected-read.o \
|
||||
|
||||
@ -145,6 +145,7 @@ test/test-tcp-open.c
|
||||
test/test-tcp-read-stop.c
|
||||
test/test-tcp-shutdown-after-write.c
|
||||
test/test-tcp-unexpected-read.c
|
||||
test/test-tcp-oob.c
|
||||
test/test-tcp-write-error.c
|
||||
test/test-tcp-write-to-half-open-connection.c
|
||||
test/test-tcp-writealot.c
|
||||
|
||||
@ -22,7 +22,7 @@ E=
|
||||
CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter
|
||||
CFLAGS += -g
|
||||
CPPFLAGS += -I$(SRCDIR)/src
|
||||
LDFLAGS=-lm -pthread
|
||||
LDFLAGS += -lm -pthread
|
||||
|
||||
CPPFLAGS += -D_LARGEFILE_SOURCE
|
||||
CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
@ -186,7 +186,7 @@ src/.buildstamp src/unix/.buildstamp test/.buildstamp:
|
||||
mkdir -p $(@D)
|
||||
touch $@
|
||||
|
||||
src/unix/%.o src/unix/%.pic.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h src/unix/.buildstamp $(DTRACE_HEADER)
|
||||
src/unix/%.o src/unix/%.pic.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/atomic-ops.h src/unix/internal.h src/unix/.buildstamp $(DTRACE_HEADER)
|
||||
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
src/%.o src/%.pic.o: src/%.c include/uv.h include/uv-private/uv-unix.h src/.buildstamp
|
||||
|
||||
@ -106,6 +106,17 @@ struct ngx_queue_s {
|
||||
while (0)
|
||||
|
||||
|
||||
#define ngx_queue_move(h, n) \
|
||||
do { \
|
||||
if (ngx_queue_empty(h)) \
|
||||
ngx_queue_init(n); \
|
||||
else { \
|
||||
ngx_queue_t* q = ngx_queue_head(h); \
|
||||
ngx_queue_split(h, q, n); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define ngx_queue_add(h, n) \
|
||||
do { \
|
||||
(h)->prev->next = (n)->next; \
|
||||
@ -120,6 +131,9 @@ struct ngx_queue_s {
|
||||
((type *) ((unsigned char *) q - offsetof(type, link)))
|
||||
|
||||
|
||||
/* Important note: mutating the list while ngx_queue_foreach is
|
||||
* iterating over its elements results in undefined behavior.
|
||||
*/
|
||||
#define ngx_queue_foreach(q, h) \
|
||||
for ((q) = ngx_queue_head(h); \
|
||||
(q) != ngx_queue_sentinel(h) && !ngx_queue_empty(h); \
|
||||
|
||||
@ -235,14 +235,20 @@ typedef union {
|
||||
} uv_cond_t;
|
||||
|
||||
typedef union {
|
||||
/* srwlock_ has type SRWLOCK, but not all toolchains define this type in */
|
||||
/* windows.h. */
|
||||
SRWLOCK srwlock_;
|
||||
struct {
|
||||
uv_mutex_t read_mutex_;
|
||||
uv_mutex_t write_mutex_;
|
||||
unsigned int num_readers_;
|
||||
} fallback_;
|
||||
CRITICAL_SECTION num_readers_lock_;
|
||||
HANDLE write_semaphore_;
|
||||
} state_;
|
||||
/* TODO: remove me in v2.x. */
|
||||
struct {
|
||||
SRWLOCK unused_;
|
||||
} unused1_;
|
||||
/* TODO: remove me in v2.x. */
|
||||
struct {
|
||||
uv_mutex_t unused1_;
|
||||
uv_mutex_t unused2_;
|
||||
} unused2_;
|
||||
} uv_rwlock_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "atomic-ops.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
@ -34,7 +35,6 @@
|
||||
static void uv__async_event(uv_loop_t* loop,
|
||||
struct uv__async* w,
|
||||
unsigned int nevents);
|
||||
static int uv__async_make_pending(int* pending);
|
||||
static int uv__async_eventfd(void);
|
||||
|
||||
|
||||
@ -54,7 +54,11 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
|
||||
|
||||
|
||||
int uv_async_send(uv_async_t* handle) {
|
||||
if (uv__async_make_pending(&handle->pending) == 0)
|
||||
/* Do a cheap read first. */
|
||||
if (ACCESS_ONCE(int, handle->pending) != 0)
|
||||
return 0;
|
||||
|
||||
if (cmpxchgi(&handle->pending, 0, 1) == 0)
|
||||
uv__async_send(&handle->loop->async_watcher);
|
||||
|
||||
return 0;
|
||||
@ -70,49 +74,26 @@ void uv__async_close(uv_async_t* handle) {
|
||||
static void uv__async_event(uv_loop_t* loop,
|
||||
struct uv__async* w,
|
||||
unsigned int nevents) {
|
||||
ngx_queue_t queue;
|
||||
ngx_queue_t* q;
|
||||
uv_async_t* h;
|
||||
|
||||
ngx_queue_foreach(q, &loop->async_handles) {
|
||||
ngx_queue_move(&loop->async_handles, &queue);
|
||||
while (!ngx_queue_empty(&queue)) {
|
||||
q = ngx_queue_head(&queue);
|
||||
h = ngx_queue_data(q, uv_async_t, queue);
|
||||
if (!h->pending) continue;
|
||||
h->pending = 0;
|
||||
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_tail(&loop->async_handles, q);
|
||||
|
||||
if (cmpxchgi(&h->pending, 1, 0) == 0)
|
||||
continue;
|
||||
|
||||
h->async_cb(h, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__async_make_pending(int* pending) {
|
||||
/* Do a cheap read first. */
|
||||
if (ACCESS_ONCE(int, *pending) != 0)
|
||||
return 1;
|
||||
|
||||
/* Micro-optimization: use atomic memory operations to detect if we've been
|
||||
* preempted by another thread and don't have to make an expensive syscall.
|
||||
* This speeds up the heavily contended case by about 1-2% and has little
|
||||
* if any impact on the non-contended case.
|
||||
*
|
||||
* Use XCHG instead of the CMPXCHG that __sync_val_compare_and_swap() emits
|
||||
* on x86, it's about 4x faster. It probably makes zero difference in the
|
||||
* grand scheme of things but I'm OCD enough not to let this one pass.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
{
|
||||
unsigned int val = 1;
|
||||
__asm__ __volatile__ ("xchgl %0, %1"
|
||||
: "+r" (val)
|
||||
: "m" (*pending));
|
||||
return val != 0;
|
||||
}
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 0)
|
||||
return __sync_val_compare_and_swap(pending, 0, 1) != 0;
|
||||
#else
|
||||
ACCESS_ONCE(int, *pending) = 1;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
struct uv__async* wa;
|
||||
char buf[1024];
|
||||
|
||||
60
src/unix/atomic-ops.h
Normal file
60
src/unix/atomic-ops.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
|
||||
*/
|
||||
|
||||
#ifndef UV_ATOMIC_OPS_H_
|
||||
#define UV_ATOMIC_OPS_H_
|
||||
|
||||
#include "internal.h" /* UV_UNUSED */
|
||||
|
||||
UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
|
||||
UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval));
|
||||
UV_UNUSED(static void cpu_relax(void));
|
||||
|
||||
/* Prefer hand-rolled assembly over the gcc builtins because the latter also
|
||||
* issue full memory barriers.
|
||||
*/
|
||||
UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
int out;
|
||||
__asm__ __volatile__ ("lock; cmpxchg %2, %1;"
|
||||
: "=a" (out), "+m" (*(volatile int*) ptr)
|
||||
: "r" (newval), "0" (oldval)
|
||||
: "memory");
|
||||
return out;
|
||||
#else
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
#endif
|
||||
}
|
||||
|
||||
UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
long out;
|
||||
__asm__ __volatile__ ("lock; cmpxchg %2, %1;"
|
||||
: "=a" (out), "+m" (*(volatile long*) ptr)
|
||||
: "r" (newval), "0" (oldval)
|
||||
: "memory");
|
||||
return out;
|
||||
#else
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
#endif
|
||||
}
|
||||
|
||||
UV_UNUSED(static void cpu_relax(void)) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
__asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* UV_ATOMIC_OPS_H_ */
|
||||
@ -133,15 +133,12 @@ static void uv__cf_loop_cb(void* arg) {
|
||||
loop = arg;
|
||||
|
||||
uv_mutex_lock(&loop->cf_mutex);
|
||||
ngx_queue_init(&split_head);
|
||||
if (!ngx_queue_empty(&loop->cf_signals)) {
|
||||
ngx_queue_t* split_pos = ngx_queue_next(&loop->cf_signals);
|
||||
ngx_queue_split(&loop->cf_signals, split_pos, &split_head);
|
||||
}
|
||||
ngx_queue_move(&loop->cf_signals, &split_head);
|
||||
uv_mutex_unlock(&loop->cf_mutex);
|
||||
|
||||
while (!ngx_queue_empty(&split_head)) {
|
||||
item = ngx_queue_head(&split_head);
|
||||
ngx_queue_remove(item);
|
||||
|
||||
s = ngx_queue_data(item, uv__cf_loop_signal_t, member);
|
||||
|
||||
@ -151,7 +148,6 @@ static void uv__cf_loop_cb(void* arg) {
|
||||
else
|
||||
s->cb(s->arg);
|
||||
|
||||
ngx_queue_remove(item);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,6 +110,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) {
|
||||
case ERANGE: return UV_ERANGE;
|
||||
case ENXIO: return UV_ENXIO;
|
||||
case EMLINK: return UV_EMLINK;
|
||||
case ENFILE: return UV_ENFILE;
|
||||
default: return UV_UNKNOWN;
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
||||
@ -55,11 +55,7 @@ struct uv__fsevents_event_s {
|
||||
ngx_queue_t split_head; \
|
||||
uv__fsevents_event_t* event; \
|
||||
uv_mutex_lock(&(handle)->cf_mutex); \
|
||||
ngx_queue_init(&split_head); \
|
||||
if (!ngx_queue_empty(&(handle)->cf_events)) { \
|
||||
ngx_queue_t* split_pos = ngx_queue_next(&(handle)->cf_events); \
|
||||
ngx_queue_split(&(handle)->cf_events, split_pos, &split_head); \
|
||||
} \
|
||||
ngx_queue_move(&(handle)->cf_events, &split_head); \
|
||||
uv_mutex_unlock(&(handle)->cf_mutex); \
|
||||
while (!ngx_queue_empty(&split_head)) { \
|
||||
curr = ngx_queue_head(&split_head); \
|
||||
|
||||
@ -66,6 +66,21 @@
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* The __clang__ and __INTEL_COMPILER checks are superfluous because they
|
||||
* define __GNUC__. They are here to convey to you, dear reader, that these
|
||||
* macros are enabled when compiling with clang or icc.
|
||||
*/
|
||||
#if defined(__clang__) || \
|
||||
defined(__GNUC__) || \
|
||||
defined(__INTEL_COMPILER) || \
|
||||
defined(__SUNPRO_C)
|
||||
# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration
|
||||
# define UV_UNUSED(declaration) __attribute__((unused)) declaration
|
||||
#else
|
||||
# define UV_DESTRUCTOR(declaration) declaration
|
||||
# define UV_UNUSED(declaration) declaration
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
# define UV__POLLIN UV__EPOLLIN
|
||||
# define UV__POLLOUT UV__EPOLLOUT
|
||||
|
||||
@ -199,7 +199,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
|
||||
abort();
|
||||
|
||||
if (sigmask != 0 && no_epoll_pwait == 0) {
|
||||
if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) {
|
||||
nfds = uv__epoll_pwait(loop->backend_fd,
|
||||
events,
|
||||
ARRAY_SIZE(events),
|
||||
|
||||
@ -119,6 +119,7 @@ static void uv__inotify_read(uv_loop_t* loop,
|
||||
const struct uv__inotify_event* e;
|
||||
struct watcher_list* w;
|
||||
uv_fs_event_t* h;
|
||||
ngx_queue_t queue;
|
||||
ngx_queue_t* q;
|
||||
const char* path;
|
||||
ssize_t size;
|
||||
@ -158,8 +159,14 @@ static void uv__inotify_read(uv_loop_t* loop,
|
||||
*/
|
||||
path = e->len ? (const char*) (e + 1) : basename_r(w->path);
|
||||
|
||||
ngx_queue_foreach(q, &w->watchers) {
|
||||
ngx_queue_move(&w->watchers, &queue);
|
||||
while (!ngx_queue_empty(&queue)) {
|
||||
q = ngx_queue_head(&queue);
|
||||
h = ngx_queue_data(q, uv_fs_event_t, watchers);
|
||||
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_tail(&w->watchers, q);
|
||||
|
||||
h->cb(h, path, events, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,9 +48,14 @@
|
||||
\
|
||||
void uv__run_##name(uv_loop_t* loop) { \
|
||||
uv_##name##_t* h; \
|
||||
ngx_queue_t queue; \
|
||||
ngx_queue_t* q; \
|
||||
ngx_queue_foreach(q, &loop->name##_handles) { \
|
||||
ngx_queue_move(&loop->name##_handles, &queue); \
|
||||
while (!ngx_queue_empty(&queue)) { \
|
||||
q = ngx_queue_head(&queue); \
|
||||
h = ngx_queue_data(q, uv_##name##_t, queue); \
|
||||
ngx_queue_remove(q); \
|
||||
ngx_queue_insert_tail(&loop->name##_handles, q); \
|
||||
h->name##_cb(h, 0); \
|
||||
} \
|
||||
} \
|
||||
|
||||
@ -40,6 +40,10 @@
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <grp.h>
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_queue_t* uv__process_queue(uv_loop_t* loop, int pid) {
|
||||
assert(pid > 0);
|
||||
@ -331,6 +335,17 @@ static void uv__process_child_init(uv_process_options_t options,
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (options.flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
|
||||
/* When dropping privileges from root, the `setgroups` call will
|
||||
* remove any extraneous groups. If we don't call this, then
|
||||
* even though our uid has dropped, we may still have groups
|
||||
* that enable us to do super-user things. This will fail if we
|
||||
* aren't root, so don't bother checking the return value, this
|
||||
* is just done as an optimistic privilege dropping function.
|
||||
*/
|
||||
SAVE_ERRNO(setgroups(0, NULL));
|
||||
}
|
||||
|
||||
if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
|
||||
uv__write_int(error_fd, errno);
|
||||
_exit(127);
|
||||
|
||||
@ -231,6 +231,8 @@ void uv__signal_loop_cleanup(uv_loop_t* loop) {
|
||||
/* Stop all the signal watchers that are still attached to this loop. This
|
||||
* ensures that the (shared) signal tree doesn't contain any invalid entries
|
||||
* entries, and that signal handlers are removed when appropriate.
|
||||
* It's safe to use QUEUE_FOREACH here because the handles and the handle
|
||||
* queue are not modified by uv__signal_stop().
|
||||
*/
|
||||
ngx_queue_foreach(q, &loop->handle_queue) {
|
||||
uv_handle_t* handle = ngx_queue_data(q, uv_handle_t, handle_queue);
|
||||
|
||||
@ -403,6 +403,10 @@ failed_malloc:
|
||||
|
||||
|
||||
int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
|
||||
#if defined(__APPLE__)
|
||||
int enable;
|
||||
#endif
|
||||
|
||||
assert(fd >= 0);
|
||||
stream->flags |= flags;
|
||||
|
||||
@ -415,6 +419,15 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
|
||||
return uv__set_sys_error(stream->loop, errno);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
enable = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) &&
|
||||
errno != ENOTSOCK &&
|
||||
errno != EINVAL) {
|
||||
return uv__set_sys_error(stream->loop, errno);
|
||||
}
|
||||
#endif
|
||||
|
||||
stream->io_watcher.fd = fd;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -84,13 +84,12 @@ int uv_mutex_trylock(uv_mutex_t* mutex) {
|
||||
|
||||
r = pthread_mutex_trylock(mutex);
|
||||
|
||||
if (r && r != EBUSY && r != EAGAIN)
|
||||
abort();
|
||||
|
||||
if (r)
|
||||
if (r) {
|
||||
if (r != EBUSY && r != EAGAIN)
|
||||
abort();
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -125,13 +124,12 @@ int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
|
||||
|
||||
r = pthread_rwlock_tryrdlock(rwlock);
|
||||
|
||||
if (r && r != EBUSY && r != EAGAIN)
|
||||
abort();
|
||||
|
||||
if (r)
|
||||
if (r) {
|
||||
if (r != EBUSY && r != EAGAIN)
|
||||
abort();
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -152,13 +150,12 @@ int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
|
||||
|
||||
r = pthread_rwlock_trywrlock(rwlock);
|
||||
|
||||
if (r && r != EBUSY && r != EAGAIN)
|
||||
abort();
|
||||
|
||||
if (r)
|
||||
if (r) {
|
||||
if (r != EBUSY && r != EAGAIN)
|
||||
abort();
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -202,13 +202,8 @@ void uv__work_done(uv_async_t* handle, int status) {
|
||||
int err;
|
||||
|
||||
loop = container_of(handle, uv_loop_t, wq_async);
|
||||
ngx_queue_init(&wq);
|
||||
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
if (!ngx_queue_empty(&loop->wq)) {
|
||||
q = ngx_queue_head(&loop->wq);
|
||||
ngx_queue_split(&loop->wq, q, &wq);
|
||||
}
|
||||
ngx_queue_move(&loop->wq, &wq);
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
|
||||
while (!ngx_queue_empty(&wq)) {
|
||||
|
||||
@ -362,11 +362,18 @@ unsigned long uv_thread_self(void) {
|
||||
|
||||
|
||||
void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
|
||||
ngx_queue_t queue;
|
||||
ngx_queue_t* q;
|
||||
uv_handle_t* h;
|
||||
|
||||
ngx_queue_foreach(q, &loop->handle_queue) {
|
||||
ngx_queue_move(&loop->handle_queue, &queue);
|
||||
while (!ngx_queue_empty(&queue)) {
|
||||
q = ngx_queue_head(&queue);
|
||||
h = ngx_queue_data(q, uv_handle_t, handle_queue);
|
||||
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_tail(&loop->handle_queue, q);
|
||||
|
||||
if (h->flags & UV__HANDLE_INTERNAL) continue;
|
||||
walk_cb(h, arg);
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
#define UV_VERSION_MAJOR 0
|
||||
#define UV_VERSION_MINOR 10
|
||||
#define UV_VERSION_PATCH 34
|
||||
#define UV_VERSION_PATCH 38
|
||||
#define UV_VERSION_IS_RELEASE 0
|
||||
|
||||
|
||||
|
||||
255
src/win/thread.c
255
src/win/thread.c
@ -26,7 +26,6 @@
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
#define HAVE_SRWLOCK_API() (pTryAcquireSRWLockShared != NULL)
|
||||
#define HAVE_CONDVAR_API() (pInitializeConditionVariable != NULL)
|
||||
|
||||
#ifdef _MSC_VER /* msvc */
|
||||
@ -38,25 +37,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
inline static int uv__rwlock_srwlock_init(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_srwlock_destroy(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_srwlock_rdlock(uv_rwlock_t* rwlock);
|
||||
inline static int uv__rwlock_srwlock_tryrdlock(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_srwlock_rdunlock(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_srwlock_wrlock(uv_rwlock_t* rwlock);
|
||||
inline static int uv__rwlock_srwlock_trywrlock(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_srwlock_wrunlock(uv_rwlock_t* rwlock);
|
||||
|
||||
inline static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_fallback_destroy(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock);
|
||||
inline static int uv__rwlock_fallback_tryrdlock(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_fallback_wrlock(uv_rwlock_t* rwlock);
|
||||
inline static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock);
|
||||
inline static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock);
|
||||
|
||||
|
||||
inline static int uv_cond_fallback_init(uv_cond_t* cond);
|
||||
inline static void uv_cond_fallback_destroy(uv_cond_t* cond);
|
||||
inline static void uv_cond_fallback_signal(uv_cond_t* cond);
|
||||
@ -158,68 +138,112 @@ void uv_mutex_unlock(uv_mutex_t* mutex) {
|
||||
|
||||
|
||||
int uv_rwlock_init(uv_rwlock_t* rwlock) {
|
||||
uv__once_init();
|
||||
/* Initialize the semaphore that acts as the write lock. */
|
||||
HANDLE handle = CreateSemaphoreW(NULL, 1, 1, NULL);
|
||||
if (handle == NULL)
|
||||
return -1;
|
||||
rwlock->state_.write_semaphore_ = handle;
|
||||
|
||||
if (HAVE_SRWLOCK_API())
|
||||
return uv__rwlock_srwlock_init(rwlock);
|
||||
else
|
||||
return uv__rwlock_fallback_init(rwlock);
|
||||
/* Initialize the critical section protecting the reader count. */
|
||||
InitializeCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
|
||||
/* Initialize the reader count. */
|
||||
rwlock->state_.num_readers_ = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
uv__rwlock_srwlock_destroy(rwlock);
|
||||
else
|
||||
uv__rwlock_fallback_destroy(rwlock);
|
||||
DeleteCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
CloseHandle(rwlock->state_.write_semaphore_);
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
uv__rwlock_srwlock_rdlock(rwlock);
|
||||
else
|
||||
uv__rwlock_fallback_rdlock(rwlock);
|
||||
/* Acquire the lock that protects the reader count. */
|
||||
EnterCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
|
||||
/* Increase the reader count, and lock for write if this is the first
|
||||
* reader.
|
||||
*/
|
||||
if (++rwlock->state_.num_readers_ == 1) {
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE);
|
||||
if (r != WAIT_OBJECT_0)
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
}
|
||||
|
||||
/* Release the lock that protects the reader count. */
|
||||
LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
return uv__rwlock_srwlock_tryrdlock(rwlock);
|
||||
else
|
||||
return uv__rwlock_fallback_tryrdlock(rwlock);
|
||||
int err;
|
||||
|
||||
if (!TryEnterCriticalSection(&rwlock->state_.num_readers_lock_))
|
||||
return -1;
|
||||
|
||||
err = 0;
|
||||
|
||||
if (rwlock->state_.num_readers_ == 0) {
|
||||
/* Currently there are no other readers, which means that the write lock
|
||||
* needs to be acquired.
|
||||
*/
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0);
|
||||
if (r == WAIT_OBJECT_0)
|
||||
rwlock->state_.num_readers_++;
|
||||
else if (r == WAIT_TIMEOUT)
|
||||
err = -1;
|
||||
else if (r == WAIT_FAILED)
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
|
||||
} else {
|
||||
/* The write lock has already been acquired because there are other
|
||||
* active readers.
|
||||
*/
|
||||
rwlock->state_.num_readers_++;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
uv__rwlock_srwlock_rdunlock(rwlock);
|
||||
else
|
||||
uv__rwlock_fallback_rdunlock(rwlock);
|
||||
EnterCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
|
||||
if (--rwlock->state_.num_readers_ == 0) {
|
||||
if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL))
|
||||
uv_fatal_error(GetLastError(), "ReleaseSemaphore");
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
uv__rwlock_srwlock_wrlock(rwlock);
|
||||
else
|
||||
uv__rwlock_fallback_wrlock(rwlock);
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE);
|
||||
if (r != WAIT_OBJECT_0)
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
return uv__rwlock_srwlock_trywrlock(rwlock);
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0);
|
||||
if (r == WAIT_OBJECT_0)
|
||||
return 0;
|
||||
else if (r == WAIT_TIMEOUT)
|
||||
return -1;
|
||||
else
|
||||
return uv__rwlock_fallback_trywrlock(rwlock);
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
|
||||
if (HAVE_SRWLOCK_API())
|
||||
uv__rwlock_srwlock_wrunlock(rwlock);
|
||||
else
|
||||
uv__rwlock_fallback_wrunlock(rwlock);
|
||||
if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL))
|
||||
uv_fatal_error(GetLastError(), "ReleaseSemaphore");
|
||||
}
|
||||
|
||||
|
||||
@ -261,133 +285,6 @@ int uv_sem_trywait(uv_sem_t* sem) {
|
||||
}
|
||||
|
||||
|
||||
inline static int uv__rwlock_srwlock_init(uv_rwlock_t* rwlock) {
|
||||
pInitializeSRWLock(&rwlock->srwlock_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_srwlock_destroy(uv_rwlock_t* rwlock) {
|
||||
(void) rwlock;
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_srwlock_rdlock(uv_rwlock_t* rwlock) {
|
||||
pAcquireSRWLockShared(&rwlock->srwlock_);
|
||||
}
|
||||
|
||||
|
||||
inline static int uv__rwlock_srwlock_tryrdlock(uv_rwlock_t* rwlock) {
|
||||
if (pTryAcquireSRWLockShared(&rwlock->srwlock_))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_srwlock_rdunlock(uv_rwlock_t* rwlock) {
|
||||
pReleaseSRWLockShared(&rwlock->srwlock_);
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_srwlock_wrlock(uv_rwlock_t* rwlock) {
|
||||
pAcquireSRWLockExclusive(&rwlock->srwlock_);
|
||||
}
|
||||
|
||||
|
||||
inline static int uv__rwlock_srwlock_trywrlock(uv_rwlock_t* rwlock) {
|
||||
if (pTryAcquireSRWLockExclusive(&rwlock->srwlock_))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_srwlock_wrunlock(uv_rwlock_t* rwlock) {
|
||||
pReleaseSRWLockExclusive(&rwlock->srwlock_);
|
||||
}
|
||||
|
||||
|
||||
inline static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock) {
|
||||
if (uv_mutex_init(&rwlock->fallback_.read_mutex_))
|
||||
return -1;
|
||||
|
||||
if (uv_mutex_init(&rwlock->fallback_.write_mutex_)) {
|
||||
uv_mutex_destroy(&rwlock->fallback_.read_mutex_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rwlock->fallback_.num_readers_ = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_fallback_destroy(uv_rwlock_t* rwlock) {
|
||||
uv_mutex_destroy(&rwlock->fallback_.read_mutex_);
|
||||
uv_mutex_destroy(&rwlock->fallback_.write_mutex_);
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock) {
|
||||
uv_mutex_lock(&rwlock->fallback_.read_mutex_);
|
||||
|
||||
if (++rwlock->fallback_.num_readers_ == 1)
|
||||
uv_mutex_lock(&rwlock->fallback_.write_mutex_);
|
||||
|
||||
uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
|
||||
}
|
||||
|
||||
|
||||
inline static int uv__rwlock_fallback_tryrdlock(uv_rwlock_t* rwlock) {
|
||||
int ret;
|
||||
|
||||
ret = -1;
|
||||
|
||||
if (uv_mutex_trylock(&rwlock->fallback_.read_mutex_))
|
||||
goto out;
|
||||
|
||||
if (rwlock->fallback_.num_readers_ == 0)
|
||||
ret = uv_mutex_trylock(&rwlock->fallback_.write_mutex_);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
if (ret == 0)
|
||||
rwlock->fallback_.num_readers_++;
|
||||
|
||||
uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock) {
|
||||
uv_mutex_lock(&rwlock->fallback_.read_mutex_);
|
||||
|
||||
if (--rwlock->fallback_.num_readers_ == 0)
|
||||
uv_mutex_unlock(&rwlock->fallback_.write_mutex_);
|
||||
|
||||
uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_fallback_wrlock(uv_rwlock_t* rwlock) {
|
||||
uv_mutex_lock(&rwlock->fallback_.write_mutex_);
|
||||
}
|
||||
|
||||
|
||||
inline static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock) {
|
||||
return uv_mutex_trylock(&rwlock->fallback_.write_mutex_);
|
||||
}
|
||||
|
||||
|
||||
inline static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock) {
|
||||
uv_mutex_unlock(&rwlock->fallback_.write_mutex_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This condition variable implementation is based on the SetEvent solution
|
||||
* (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
||||
* We could not use the SignalObjectAndWait solution (section 3.4) because
|
||||
|
||||
@ -38,13 +38,6 @@ sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
|
||||
sCreateSymbolicLinkW pCreateSymbolicLinkW;
|
||||
sCancelIoEx pCancelIoEx;
|
||||
sInitializeSRWLock pInitializeSRWLock;
|
||||
sAcquireSRWLockShared pAcquireSRWLockShared;
|
||||
sAcquireSRWLockExclusive pAcquireSRWLockExclusive;
|
||||
sTryAcquireSRWLockShared pTryAcquireSRWLockShared;
|
||||
sTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive;
|
||||
sReleaseSRWLockShared pReleaseSRWLockShared;
|
||||
sReleaseSRWLockExclusive pReleaseSRWLockExclusive;
|
||||
sInitializeConditionVariable pInitializeConditionVariable;
|
||||
sSleepConditionVariableCS pSleepConditionVariableCS;
|
||||
sSleepConditionVariableSRW pSleepConditionVariableSRW;
|
||||
@ -114,27 +107,6 @@ void uv_winapi_init() {
|
||||
pCancelIoEx = (sCancelIoEx)
|
||||
GetProcAddress(kernel32_module, "CancelIoEx");
|
||||
|
||||
pInitializeSRWLock = (sInitializeSRWLock)
|
||||
GetProcAddress(kernel32_module, "InitializeSRWLock");
|
||||
|
||||
pAcquireSRWLockShared = (sAcquireSRWLockShared)
|
||||
GetProcAddress(kernel32_module, "AcquireSRWLockShared");
|
||||
|
||||
pAcquireSRWLockExclusive = (sAcquireSRWLockExclusive)
|
||||
GetProcAddress(kernel32_module, "AcquireSRWLockExclusive");
|
||||
|
||||
pTryAcquireSRWLockShared = (sTryAcquireSRWLockShared)
|
||||
GetProcAddress(kernel32_module, "TryAcquireSRWLockShared");
|
||||
|
||||
pTryAcquireSRWLockExclusive = (sTryAcquireSRWLockExclusive)
|
||||
GetProcAddress(kernel32_module, "TryAcquireSRWLockExclusive");
|
||||
|
||||
pReleaseSRWLockShared = (sReleaseSRWLockShared)
|
||||
GetProcAddress(kernel32_module, "ReleaseSRWLockShared");
|
||||
|
||||
pReleaseSRWLockExclusive = (sReleaseSRWLockExclusive)
|
||||
GetProcAddress(kernel32_module, "ReleaseSRWLockExclusive");
|
||||
|
||||
pInitializeConditionVariable = (sInitializeConditionVariable)
|
||||
GetProcAddress(kernel32_module, "InitializeConditionVariable");
|
||||
|
||||
|
||||
@ -4405,27 +4405,6 @@ typedef BOOL (WINAPI* sCancelIoEx)
|
||||
(HANDLE hFile,
|
||||
LPOVERLAPPED lpOverlapped);
|
||||
|
||||
typedef VOID (WINAPI* sInitializeSRWLock)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef VOID (WINAPI* sAcquireSRWLockShared)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef VOID (WINAPI* sAcquireSRWLockExclusive)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef BOOL (WINAPI* sTryAcquireSRWLockShared)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef BOOL (WINAPI* sTryAcquireSRWLockExclusive)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef VOID (WINAPI* sReleaseSRWLockShared)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef VOID (WINAPI* sReleaseSRWLockExclusive)
|
||||
(PSRWLOCK SRWLock);
|
||||
|
||||
typedef VOID (WINAPI* sInitializeConditionVariable)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
|
||||
@ -4460,13 +4439,6 @@ extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
|
||||
extern sCreateSymbolicLinkW pCreateSymbolicLinkW;
|
||||
extern sCancelIoEx pCancelIoEx;
|
||||
extern sInitializeSRWLock pInitializeSRWLock;
|
||||
extern sAcquireSRWLockShared pAcquireSRWLockShared;
|
||||
extern sAcquireSRWLockExclusive pAcquireSRWLockExclusive;
|
||||
extern sTryAcquireSRWLockShared pTryAcquireSRWLockShared;
|
||||
extern sTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive;
|
||||
extern sReleaseSRWLockShared pReleaseSRWLockShared;
|
||||
extern sReleaseSRWLockExclusive pReleaseSRWLockExclusive;
|
||||
extern sInitializeConditionVariable pInitializeConditionVariable;
|
||||
extern sSleepConditionVariableCS pSleepConditionVariableCS;
|
||||
extern sSleepConditionVariableSRW pSleepConditionVariableSRW;
|
||||
|
||||
@ -196,9 +196,16 @@ static void chown_root_cb(uv_fs_t* req) {
|
||||
/* On windows, chown is a no-op and always succeeds. */
|
||||
ASSERT(req->result == 0);
|
||||
#else
|
||||
/* On unix, chown'ing the root directory is not allowed. */
|
||||
ASSERT(req->result == -1);
|
||||
ASSERT(req->errorno == UV_EPERM);
|
||||
/* On unix, chown'ing the root directory is not allowed -
|
||||
* unless you're root, of course.
|
||||
*/
|
||||
if (geteuid() == 0) {
|
||||
ASSERT(req->result == 0);
|
||||
}
|
||||
else {
|
||||
ASSERT(req->result == -1);
|
||||
ASSERT(req->errorno == UV_EPERM);
|
||||
}
|
||||
#endif
|
||||
chown_cb_count++;
|
||||
uv_fs_req_cleanup(req);
|
||||
|
||||
@ -65,7 +65,10 @@ TEST_DECLARE (tcp_connect_error_fault)
|
||||
TEST_DECLARE (tcp_connect_timeout)
|
||||
TEST_DECLARE (tcp_close_while_connecting)
|
||||
TEST_DECLARE (tcp_close)
|
||||
#ifndef _WIN32
|
||||
TEST_DECLARE (tcp_close_accept)
|
||||
TEST_DECLARE (tcp_oob)
|
||||
#endif
|
||||
TEST_DECLARE (tcp_flags)
|
||||
TEST_DECLARE (tcp_write_to_half_open_connection)
|
||||
TEST_DECLARE (tcp_unexpected_read)
|
||||
@ -208,6 +211,7 @@ TEST_DECLARE (threadpool_cancel_fs)
|
||||
TEST_DECLARE (threadpool_cancel_single)
|
||||
TEST_DECLARE (thread_mutex)
|
||||
TEST_DECLARE (thread_rwlock)
|
||||
TEST_DECLARE (thread_rwlock_trylock)
|
||||
TEST_DECLARE (thread_create)
|
||||
TEST_DECLARE (strlcpy)
|
||||
TEST_DECLARE (strlcat)
|
||||
@ -311,7 +315,10 @@ TASK_LIST_START
|
||||
TEST_ENTRY (tcp_connect_timeout)
|
||||
TEST_ENTRY (tcp_close_while_connecting)
|
||||
TEST_ENTRY (tcp_close)
|
||||
#ifndef _WIN32
|
||||
TEST_ENTRY (tcp_close_accept)
|
||||
TEST_ENTRY (tcp_oob)
|
||||
#endif
|
||||
TEST_ENTRY (tcp_flags)
|
||||
TEST_ENTRY (tcp_write_to_half_open_connection)
|
||||
TEST_ENTRY (tcp_unexpected_read)
|
||||
@ -516,6 +523,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (threadpool_cancel_single)
|
||||
TEST_ENTRY (thread_mutex)
|
||||
TEST_ENTRY (thread_rwlock)
|
||||
TEST_ENTRY (thread_rwlock_trylock)
|
||||
TEST_ENTRY (thread_create)
|
||||
TEST_ENTRY (strlcpy)
|
||||
TEST_ENTRY (strlcat)
|
||||
|
||||
@ -61,3 +61,62 @@ TEST_IMPL(thread_rwlock) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(thread_rwlock_trylock) {
|
||||
uv_rwlock_t rwlock;
|
||||
int r;
|
||||
|
||||
r = uv_rwlock_init(&rwlock);
|
||||
ASSERT(r == 0);
|
||||
|
||||
/* No locks held. */
|
||||
|
||||
r = uv_rwlock_trywrlock(&rwlock);
|
||||
ASSERT(r == 0);
|
||||
|
||||
/* Write lock held. */
|
||||
|
||||
r = uv_rwlock_tryrdlock(&rwlock);
|
||||
ASSERT(r == -1);
|
||||
r = uv_rwlock_trywrlock(&rwlock);
|
||||
ASSERT(r == -1);
|
||||
|
||||
uv_rwlock_wrunlock(&rwlock);
|
||||
|
||||
/* No locks held. */
|
||||
|
||||
r = uv_rwlock_tryrdlock(&rwlock);
|
||||
ASSERT(r == 0);
|
||||
|
||||
/* One read lock held. */
|
||||
|
||||
r = uv_rwlock_tryrdlock(&rwlock);
|
||||
ASSERT(r == 0);
|
||||
|
||||
/* Two read locks held. */
|
||||
|
||||
r = uv_rwlock_trywrlock(&rwlock);
|
||||
ASSERT(r == -1);
|
||||
|
||||
uv_rwlock_rdunlock(&rwlock);
|
||||
|
||||
/* One read lock held. */
|
||||
|
||||
uv_rwlock_rdunlock(&rwlock);
|
||||
|
||||
/* No read locks held. */
|
||||
|
||||
r = uv_rwlock_trywrlock(&rwlock);
|
||||
ASSERT(r == 0);
|
||||
|
||||
/* Write lock held. */
|
||||
|
||||
uv_rwlock_wrunlock(&rwlock);
|
||||
|
||||
/* No locks held. */
|
||||
|
||||
uv_rwlock_destroy(&rwlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -60,12 +60,16 @@ TEST_IMPL(tcp_close_while_connecting) {
|
||||
uv_connect_t connect_req;
|
||||
struct sockaddr_in addr;
|
||||
uv_loop_t* loop;
|
||||
int r;
|
||||
|
||||
addr = uv_ip4_addr("1.2.3.4", TEST_PORT);
|
||||
loop = uv_default_loop();
|
||||
|
||||
ASSERT(0 == uv_tcp_init(loop, &tcp_handle));
|
||||
ASSERT(0 == uv_tcp_connect(&connect_req, &tcp_handle, addr, connect_cb));
|
||||
r = uv_tcp_connect(&connect_req, &tcp_handle, addr, connect_cb);
|
||||
if (r == -1 && uv_last_error(uv_default_loop()).code == UV_ENETUNREACH)
|
||||
RETURN_SKIP("Network unreachable.");
|
||||
ASSERT(r == 0);
|
||||
ASSERT(0 == uv_timer_init(loop, &timer1_handle));
|
||||
ASSERT(0 == uv_timer_start(&timer1_handle, timer1_cb, 50, 0));
|
||||
ASSERT(0 == uv_timer_init(loop, &timer2_handle));
|
||||
|
||||
@ -76,6 +76,8 @@ TEST_IMPL(tcp_connect_timeout) {
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_tcp_connect(&connect_req, &conn, addr, connect_cb);
|
||||
if (r == -1 && uv_last_error(uv_default_loop()).code == UV_ENETUNREACH)
|
||||
RETURN_SKIP("Network unreachable.");
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
|
||||
137
test/test-tcp-oob.c
Normal file
137
test/test-tcp-oob.c
Normal file
@ -0,0 +1,137 @@
|
||||
/* Copyright Fedor Indutny. 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.
|
||||
*/
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include "uv.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static uv_tcp_t server_handle;
|
||||
static uv_tcp_t client_handle;
|
||||
static uv_tcp_t peer_handle;
|
||||
static uv_idle_t idle;
|
||||
static uv_connect_t connect_req;
|
||||
static int ticks;
|
||||
static const int kMaxTicks = 10;
|
||||
|
||||
|
||||
static void set_nonblocking(int fd, int set) {
|
||||
int r;
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
ASSERT(flags >= 0);
|
||||
if (set)
|
||||
flags |= O_NONBLOCK;
|
||||
else
|
||||
flags &= ~O_NONBLOCK;
|
||||
r = fcntl(fd, F_SETFL, flags);
|
||||
ASSERT(r >= 0);
|
||||
}
|
||||
|
||||
|
||||
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) {
|
||||
static char storage[1024];
|
||||
return uv_buf_init(storage, sizeof(storage));
|
||||
}
|
||||
|
||||
|
||||
static void idle_cb(uv_idle_t* idle, int status) {
|
||||
ASSERT(status == 0);
|
||||
if (++ticks < kMaxTicks)
|
||||
return;
|
||||
|
||||
uv_close((uv_handle_t*) &server_handle, NULL);
|
||||
uv_close((uv_handle_t*) &client_handle, NULL);
|
||||
uv_close((uv_handle_t*) &peer_handle, NULL);
|
||||
uv_close((uv_handle_t*) idle, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) {
|
||||
ASSERT(nread > 0);
|
||||
ASSERT(0 == uv_idle_start(&idle, idle_cb));
|
||||
}
|
||||
|
||||
|
||||
static void connect_cb(uv_connect_t* req, int status) {
|
||||
ASSERT(req->handle == (uv_stream_t*) &client_handle);
|
||||
ASSERT(0 == status);
|
||||
}
|
||||
|
||||
|
||||
static void connection_cb(uv_stream_t* handle, int status) {
|
||||
int r;
|
||||
int fd;
|
||||
|
||||
ASSERT(0 == status);
|
||||
ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle));
|
||||
ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb));
|
||||
|
||||
/* Send some OOB data */
|
||||
fd = client_handle.io_watcher.fd;
|
||||
set_nonblocking(fd, 0);
|
||||
|
||||
/* The problem triggers only on a second message, it seem that xnu is not
|
||||
* triggering `kevent()` for the first one
|
||||
*/
|
||||
do {
|
||||
r = send(fd, "hello", 5, MSG_OOB);
|
||||
} while (r < 0 && errno == EINTR);
|
||||
ASSERT(5 == r);
|
||||
|
||||
do {
|
||||
r = send(fd, "hello", 5, MSG_OOB);
|
||||
} while (r < 0 && errno == EINTR);
|
||||
ASSERT(5 == r);
|
||||
|
||||
set_nonblocking(fd, 1);
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(tcp_oob) {
|
||||
uv_loop_t* loop;
|
||||
loop = uv_default_loop();
|
||||
|
||||
ASSERT(0 == uv_tcp_init(loop, &server_handle));
|
||||
ASSERT(0 == uv_tcp_init(loop, &client_handle));
|
||||
ASSERT(0 == uv_tcp_init(loop, &peer_handle));
|
||||
ASSERT(0 == uv_idle_init(loop, &idle));
|
||||
ASSERT(0 == uv_tcp_bind(&server_handle, uv_ip4_addr("127.0.0.1", TEST_PORT)));
|
||||
ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb));
|
||||
|
||||
/* Ensure two separate packets */
|
||||
ASSERT(0 == uv_tcp_nodelay(&client_handle, 1));
|
||||
|
||||
ASSERT(0 == uv_tcp_connect(&connect_req,
|
||||
&client_handle,
|
||||
uv_ip4_addr("127.0.0.1", TEST_PORT),
|
||||
connect_cb));
|
||||
ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
|
||||
|
||||
ASSERT(ticks == kMaxTicks);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -113,6 +113,8 @@ TEST_IMPL(udp_multicast_join) {
|
||||
|
||||
/* join the multicast channel */
|
||||
r = uv_udp_set_membership(&client, "239.255.0.1", NULL, UV_JOIN_GROUP);
|
||||
if (r == -1 && uv_last_error(uv_default_loop()).code == UV_ENODEV)
|
||||
RETURN_SKIP("No multicast support.");
|
||||
ASSERT(r == 0);
|
||||
|
||||
r = uv_udp_recv_start(&client, alloc_cb, cl_recv_cb);
|
||||
|
||||
@ -44,7 +44,11 @@ static void close_cb(uv_handle_t* handle) {
|
||||
|
||||
static void sv_send_cb(uv_udp_send_t* req, int status) {
|
||||
ASSERT(req != NULL);
|
||||
ASSERT(status == 0);
|
||||
if (status == -1) {
|
||||
ASSERT(uv_last_error(uv_default_loop()).code == UV_ENETUNREACH);
|
||||
} else {
|
||||
ASSERT(status == 0);
|
||||
}
|
||||
CHECK_HANDLE(req->handle);
|
||||
|
||||
sv_send_cb_called++;
|
||||
|
||||
2
uv.gyp
2
uv.gyp
@ -133,6 +133,7 @@
|
||||
'include/uv-private/uv-darwin.h',
|
||||
'include/uv-private/uv-bsd.h',
|
||||
'src/unix/async.c',
|
||||
'src/unix/atomic-ops.h',
|
||||
'src/unix/core.c',
|
||||
'src/unix/dl.c',
|
||||
'src/unix/error.c',
|
||||
@ -354,6 +355,7 @@
|
||||
'test/test-tcp-write-to-half-open-connection.c',
|
||||
'test/test-tcp-writealot.c',
|
||||
'test/test-tcp-unexpected-read.c',
|
||||
'test/test-tcp-oob.c',
|
||||
'test/test-tcp-read-stop.c',
|
||||
'test/test-threadpool.c',
|
||||
'test/test-threadpool-cancel.c',
|
||||
|
||||
@ -90,8 +90,8 @@ if defined noprojgen goto msbuild
|
||||
|
||||
@rem Generate the VS project.
|
||||
if exist build\gyp goto have_gyp
|
||||
echo git clone https://git.chromium.org/external/gyp.git build/gyp
|
||||
git clone https://git.chromium.org/external/gyp.git build/gyp
|
||||
echo git clone https://chromium.googlesource.com/external/gyp build/gyp
|
||||
git clone https://chromium.googlesource.com/external/gyp build/gyp
|
||||
if errorlevel 1 goto gyp_install_failed
|
||||
goto have_gyp
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user