From c60d6af011646ed40be93fdf1d83ed2d6d7cc786 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sun, 7 Sep 2014 23:56:15 +0100 Subject: [PATCH] darwin: allocate enough space for select() hack `fd_set`s are way too small for `select()` hack when stream's fd is bigger than 1023. Make `fd_set`s a part of `uv__stream_select_t` structure. fix #1461 Conflicts: src/unix/stream.c --- config-unix.mk | 1 + src/unix/stream.c | 115 +++++++++++++++++++++++++++-------------- test/test-list.h | 2 + test/test-osx-select.c | 47 +++++++++++++++++ uv.gyp | 1 + 5 files changed, 128 insertions(+), 38 deletions(-) diff --git a/config-unix.mk b/config-unix.mk index d230bb25..37c4a72f 100644 --- a/config-unix.mk +++ b/config-unix.mk @@ -85,6 +85,7 @@ ifeq (__clang__,$(shell sh -c "$(CC) -dM -E - sread, 0, s->sread_sz); + memset(s->swrite, 0, s->swrite_sz); if (uv_is_readable(stream)) - FD_SET(fd, &sread); + FD_SET(fd, s->sread); if (uv_is_writable(stream)) - FD_SET(fd, &swrite); - FD_SET(s->int_fd, &sread); + FD_SET(fd, s->swrite); + FD_SET(s->int_fd, s->sread); /* Wait indefinitely for fd events */ - r = select(max_fd + 1, &sread, &swrite, NULL, NULL); + r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL); if (r == -1) { if (errno == EINTR) continue; @@ -177,7 +179,7 @@ static void uv__stream_osx_select(void* arg) { continue; /* Empty socketpair's buffer in case of interruption */ - if (FD_ISSET(s->int_fd, &sread)) + if (FD_ISSET(s->int_fd, s->sread)) while (1) { r = read(s->int_fd, buf, sizeof(buf)); @@ -198,12 +200,12 @@ static void uv__stream_osx_select(void* arg) { /* Handle events */ events = 0; - if (FD_ISSET(fd, &sread)) + if (FD_ISSET(fd, s->sread)) events |= UV__POLLIN; - if (FD_ISSET(fd, &swrite)) + if (FD_ISSET(fd, s->swrite)) events |= UV__POLLOUT; - assert(events != 0 || FD_ISSET(s->int_fd, &sread)); + assert(events != 0 || FD_ISSET(s->int_fd, s->sread)); if (events != 0) { ACCESS_ONCE(int, s->events) = events; @@ -283,6 +285,10 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { int ret; int kq; int old_fd; + int max_fd; + size_t sread_sz; + size_t swrite_sz; + int err; kq = kqueue(); if (kq == -1) { @@ -306,30 +312,52 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { return 0; /* At this point we definitely know that this fd won't work with kqueue */ - s = malloc(sizeof(*s)); - if (s == NULL) - return uv__set_artificial_error(stream->loop, UV_ENOMEM); + + /* + * Create fds for io watcher and to interrupt the select() loop. + * NOTE: do it ahead of malloc below to allocate enough space for fd_sets + */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) + return uv__set_sys_error(stream->loop, errno); + + max_fd = *fd; + if (fds[1] > max_fd) + max_fd = fds[1]; + + sread_sz = (max_fd + NBBY) / NBBY; + swrite_sz = sread_sz; + + s = malloc(sizeof(*s) + sread_sz + swrite_sz); + if (s == NULL) { + err = uv__set_artificial_error(stream->loop, UV_ENOMEM); + goto failed_malloc; + } s->events = 0; s->fd = *fd; + s->sread = (fd_set*) ((char*) s + sizeof(*s)); + s->sread_sz = sread_sz; + s->swrite = (fd_set*) ((char*) s->sread + sread_sz); + s->swrite_sz = swrite_sz; - if (uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb)) { - SAVE_ERRNO(free(s)); - return uv__set_sys_error(stream->loop, errno); - } + err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb); + if (err) + goto failed_async_init; s->async.flags |= UV__HANDLE_INTERNAL; uv__handle_unref(&s->async); - if (uv_sem_init(&s->close_sem, 0)) - goto fatal1; + err = uv_sem_init(&s->close_sem, 0); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_close_sem_init; + } - if (uv_sem_init(&s->async_sem, 0)) - goto fatal2; - - /* Create fds for io watcher and to interrupt the select() loop. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - goto fatal3; + err = uv_sem_init(&s->async_sem, 0); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_async_sem_init; + } s->fake_fd = fds[0]; s->int_fd = fds[1]; @@ -339,26 +367,37 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { stream->select = s; *fd = s->fake_fd; - if (uv_thread_create(&s->thread, uv__stream_osx_select, stream)) - goto fatal4; + err = uv_thread_create(&s->thread, uv__stream_osx_select, stream); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_thread_create; + } return 0; -fatal4: +failed_thread_create: s->stream = NULL; stream->select = NULL; *fd = old_fd; - close(s->fake_fd); - close(s->int_fd); - s->fake_fd = -1; - s->int_fd = -1; -fatal3: uv_sem_destroy(&s->async_sem); -fatal2: + +failed_async_sem_init: uv_sem_destroy(&s->close_sem); -fatal1: + +failed_close_sem_init: + close(fds[0]); + close(fds[1]); uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); - return uv__set_sys_error(stream->loop, errno); + return err; + +failed_async_init: + free(s); + +failed_malloc: + close(fds[0]); + close(fds[1]); + + return err; } #endif /* defined(__APPLE__) */ diff --git a/test/test-list.h b/test/test-list.h index 7930c1c3..47444258 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -232,6 +232,7 @@ TEST_DECLARE (closed_fd_events) #endif #ifdef __APPLE__ TEST_DECLARE (osx_select) +TEST_DECLARE (osx_select_many_fds) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) @@ -468,6 +469,7 @@ TASK_LIST_START #ifdef __APPLE__ TEST_ENTRY (osx_select) + TEST_ENTRY (osx_select_many_fds) #endif TEST_ENTRY (fs_file_noent) diff --git a/test/test-osx-select.c b/test/test-osx-select.c index bf4c3952..249614a1 100644 --- a/test/test-osx-select.c +++ b/test/test-osx-select.c @@ -79,4 +79,51 @@ TEST_IMPL(osx_select) { return 0; } + +TEST_IMPL(osx_select_many_fds) { + int r; + int fd; + size_t i; + size_t len; + const char* str; + struct sockaddr_in addr; + uv_tty_t tty; + uv_tcp_t tcps[1500]; + + addr = uv_ip4_addr("127.0.0.1", 0); + + for (i = 0; i < ARRAY_SIZE(tcps); i++) { + r = uv_tcp_init(uv_default_loop(), &tcps[i]); + ASSERT(r == 0); + r = uv_tcp_bind(&tcps[i], addr); + ASSERT(r == 0); + uv_unref((uv_handle_t*) &tcps[i]); + } + + fd = open("/dev/tty", O_RDONLY); + ASSERT(fd >= 0); + + r = uv_tty_init(uv_default_loop(), &tty, fd, 1); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); + ASSERT(r == 0); + + /* Emulate user-input */ + str = "got some input\n" + "with a couple of lines\n" + "feel pretty happy\n"; + for (i = 0, len = strlen(str); i < len; i++) { + r = ioctl(fd, TIOCSTI, str + i); + ASSERT(r == 0); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(read_count == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + #endif /* __APPLE__ */ diff --git a/uv.gyp b/uv.gyp index 7cd0f7d4..5af99bd5 100644 --- a/uv.gyp +++ b/uv.gyp @@ -194,6 +194,7 @@ }, 'defines': [ '_DARWIN_USE_64_BIT_INODE=1', + '_DARWIN_UNLIMITED_SELECT=1', ] }], [ 'OS!="mac"', {