Skip to content

Commit b36d4ff

Browse files
committed
unix: implement uv_stream_set_blocking()
Commit 393c1c5 ("unix: set non-block mode in uv_{pipe,tcp,udp}_open") causes a regression in the io.js cluster module. The io.js documentation states that `worker.send()` and `process.send()` are synchronous but they no longer were after upgrading to libuv v1.2.1. The reason they are synchronous is because of backpressure - or rather, lack of backpressure: a slow consumer eventually causes a fast producer to run out of memory because the backlog of pending messages in the producer can grow unchecked. Ergo, implement uv_stream_set_blocking() on UNIX platforms to let io.js enable the old blocking behavior for pipes again. Refs: nodejs/node#760 PR-URL: #187 Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
1 parent e5bdea8 commit b36d4ff

File tree

6 files changed

+111
-3
lines changed

6 files changed

+111
-3
lines changed

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
189189
test/test-pipe-sendmsg.c \
190190
test/test-pipe-server-close.c \
191191
test/test-pipe-close-stdout-read-stdin.c \
192+
test/test-pipe-set-non-blocking.c \
192193
test/test-platform-output.c \
193194
test/test-poll-close.c \
194195
test/test-poll-close-doesnt-corrupt-stack.c \

docs/src/stream.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,14 @@ API
206206
Relying too much on this API is not recommended. It is likely to change
207207
significantly in the future.
208208
209-
Currently this only works on Windows and only for
210-
:c:type:`uv_pipe_t` handles.
209+
Currently only works on Windows for :c:type:`uv_pipe_t` handles.
210+
On UNIX platforms, all :c:type:`uv_stream_t` handles are supported.
211211
212212
Also libuv currently makes no ordering guarantee when the blocking mode
213213
is changed after write requests have already been submitted. Therefore it is
214214
recommended to set the blocking mode immediately after opening or creating
215215
the stream.
216216
217+
.. versionchanged:: 1.4.0 UNIX implementation added.
218+
217219
.. seealso:: The :c:type:`uv_handle_t` API functions also apply.

src/unix/stream.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,5 +1573,8 @@ void uv__stream_close(uv_stream_t* handle) {
15731573

15741574

15751575
int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
1576-
return UV_ENOSYS;
1576+
/* Don't need to check the file descriptor, uv__nonblock()
1577+
* will fail with EBADF if it's not valid.
1578+
*/
1579+
return uv__nonblock(uv__stream_fd(handle), !blocking);
15771580
}

test/test-list.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ TEST_DECLARE (pipe_ref4)
167167
#ifndef _WIN32
168168
TEST_DECLARE (pipe_close_stdout_read_stdin)
169169
#endif
170+
TEST_DECLARE (pipe_set_non_blocking)
170171
TEST_DECLARE (process_ref)
171172
TEST_DECLARE (has_ref)
172173
TEST_DECLARE (active)
@@ -339,6 +340,7 @@ TASK_LIST_START
339340
#ifndef _WIN32
340341
TEST_ENTRY (pipe_close_stdout_read_stdin)
341342
#endif
343+
TEST_ENTRY (pipe_set_non_blocking)
342344
TEST_ENTRY (tty)
343345
TEST_ENTRY (stdio_over_pipes)
344346
TEST_ENTRY (ip6_pton)

test/test-pipe-set-non-blocking.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* Copyright (c) 2015, Ben Noordhuis <[email protected]>
2+
*
3+
* Permission to use, copy, modify, and/or distribute this software for any
4+
* purpose with or without fee is hereby granted, provided that the above
5+
* copyright notice and this permission notice appear in all copies.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
*/
15+
16+
#include "uv.h"
17+
#include "task.h"
18+
19+
#ifdef _WIN32
20+
21+
TEST_IMPL(pipe_set_non_blocking) {
22+
RETURN_SKIP("Test not implemented on Windows.");
23+
}
24+
25+
#else /* !_WIN32 */
26+
27+
#include <errno.h>
28+
#include <string.h>
29+
#include <sys/socket.h>
30+
#include <sys/types.h>
31+
#include <sys/un.h>
32+
#include <unistd.h>
33+
34+
struct thread_ctx {
35+
uv_barrier_t barrier;
36+
int fd;
37+
};
38+
39+
static void thread_main(void* arg) {
40+
struct thread_ctx* ctx;
41+
char buf[4096];
42+
ssize_t n;
43+
44+
ctx = arg;
45+
uv_barrier_wait(&ctx->barrier);
46+
47+
do
48+
n = read(ctx->fd, buf, sizeof(buf));
49+
while (n > 0 || (n == -1 && errno == EINTR));
50+
51+
ASSERT(n == 0);
52+
}
53+
54+
TEST_IMPL(pipe_set_non_blocking) {
55+
struct thread_ctx ctx;
56+
uv_pipe_t pipe_handle;
57+
uv_thread_t thread;
58+
size_t nwritten;
59+
char data[4096];
60+
uv_buf_t buf;
61+
int fd[2];
62+
int n;
63+
64+
ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0));
65+
ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
66+
ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0]));
67+
ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1));
68+
69+
ctx.fd = fd[1];
70+
ASSERT(0 == uv_barrier_init(&ctx.barrier, 2));
71+
ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx));
72+
uv_barrier_wait(&ctx.barrier);
73+
74+
buf.len = sizeof(data);
75+
buf.base = data;
76+
memset(data, '.', sizeof(data));
77+
78+
nwritten = 0;
79+
while (nwritten < 10 << 20) {
80+
/* The stream is in blocking mode so uv_try_write() should always succeed
81+
* with the exact number of bytes that we wanted written.
82+
*/
83+
n = uv_try_write((uv_stream_t*) &pipe_handle, &buf, 1);
84+
ASSERT(n == sizeof(data));
85+
nwritten += n;
86+
}
87+
88+
uv_close((uv_handle_t*) &pipe_handle, NULL);
89+
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
90+
91+
ASSERT(0 == close(fd[1])); /* fd[0] is closed by uv_close(). */
92+
ASSERT(0 == uv_thread_join(&thread));
93+
uv_barrier_destroy(&ctx.barrier);
94+
95+
MAKE_VALGRIND_HAPPY();
96+
return 0;
97+
}
98+
99+
#endif /* !_WIN32 */

uv.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@
329329
'test/test-pipe-sendmsg.c',
330330
'test/test-pipe-server-close.c',
331331
'test/test-pipe-close-stdout-read-stdin.c',
332+
'test/test-pipe-set-non-blocking.c',
332333
'test/test-platform-output.c',
333334
'test/test-poll.c',
334335
'test/test-poll-close.c',

0 commit comments

Comments
 (0)