uv-unix: handle EINTR properly

Fixes #113.
This commit is contained in:
Ben Noordhuis 2011-07-21 15:22:05 +02:00
parent 5b532d5f60
commit 1028a9c6a7

View File

@ -88,6 +88,7 @@ static int uv__nonblock(int fd, int set) __attribute__((unused));
static int uv__socket(int domain, int type, int protocol);
static int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t len);
static int uv__close(int fd);
size_t uv__strlcpy(char* dst, const char* src, size_t size);
@ -329,7 +330,7 @@ int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, int addrsize) {
if (uv__stream_open((uv_stream_t*)tcp, fd)) {
status = -2;
close(fd);
uv__close(fd);
goto out;
}
}
@ -464,7 +465,7 @@ int uv_accept(uv_handle_t* server, uv_stream_t* client) {
if (uv__stream_open(streamClient, streamServer->accepted_fd)) {
/* TODO handle error */
streamServer->accepted_fd = -1;
close(streamServer->accepted_fd);
uv__close(streamServer->accepted_fd);
goto out;
}
@ -494,7 +495,7 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
}
if (uv__stream_open((uv_stream_t*)tcp, fd)) {
close(fd);
uv__close(fd);
return -1;
}
}
@ -554,11 +555,11 @@ void uv__finish_close(uv_handle_t* handle) {
assert(!ev_is_active(&stream->read_watcher));
assert(!ev_is_active(&stream->write_watcher));
close(stream->fd);
uv__close(stream->fd);
stream->fd = -1;
if (stream->accepted_fd >= 0) {
close(stream->accepted_fd);
uv__close(stream->accepted_fd);
stream->accepted_fd = -1;
}
break;
@ -678,12 +679,14 @@ static uv_write_t* uv__write(uv_stream_t* stream) {
* inside the iov each time we write. So there is no need to offset it.
*/
if (iovcnt == 1) {
n = write(stream->fd, iov[0].iov_base, iov[0].iov_len);
}
else {
n = writev(stream->fd, iov, iovcnt);
do {
if (iovcnt == 1) {
n = write(stream->fd, iov[0].iov_base, iov[0].iov_len);
} else {
n = writev(stream->fd, iov, iovcnt);
}
}
while (n == -1 && errno == EINTR);
if (n < 0) {
if (errno != EAGAIN) {
@ -799,7 +802,10 @@ static void uv__read(uv_stream_t* stream) {
iov = (struct iovec*) &buf;
nread = read(stream->fd, buf.base, buf.len);
do {
nread = read(stream->fd, buf.base, buf.len);
}
while (nread == -1 && errno == EINTR);
if (nread < 0) {
/* Error */
@ -964,7 +970,7 @@ static int uv__connect(uv_connect_t* req,
}
if (uv__stream_open(stream, sockfd)) {
close(sockfd);
uv__close(sockfd);
return -2;
}
}
@ -987,7 +993,10 @@ static int uv__connect(uv_connect_t* req,
stream->connect_req = req;
r = connect(stream->fd, addr, addrlen);
do {
r = connect(stream->fd, addr, addrlen);
}
while (r == -1 && errno == EINTR);
stream->delayed_error = 0;
@ -1839,7 +1848,7 @@ out:
/* unlink() before close() to avoid races. */
unlink(name);
}
close(sockfd);
uv__close(sockfd);
free((void*)name);
}
@ -1875,6 +1884,7 @@ int uv_pipe_connect(uv_connect_t* req,
int saved_errno;
int sockfd;
int status;
int r;
saved_errno = errno;
sockfd = -1;
@ -1892,9 +1902,14 @@ int uv_pipe_connect(uv_connect_t* req,
/* We don't check for EINPROGRESS. Think about it: the socket
* is either there or not.
*/
if (connect(sockfd, (struct sockaddr*)&sun, sizeof sun) == -1) {
do {
r = connect(sockfd, (struct sockaddr*)&sun, sizeof sun);
}
while (r == -1 && errno == EINTR);
if (r == -1) {
uv_err_new((uv_handle_t*)handle, errno);
close(sockfd);
uv__close(sockfd);
goto out;
}
@ -1971,7 +1986,7 @@ static int uv__socket(int domain, int type, int protocol) {
}
if (uv__nonblock(sockfd, 1) == -1 || uv__cloexec(sockfd, 1) == -1) {
close(sockfd);
uv__close(sockfd);
return -1;
}
@ -1981,22 +1996,40 @@ static int uv__socket(int domain, int type, int protocol) {
static int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t slen) {
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
return accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#else
int peerfd;
if ((peerfd = accept(sockfd, saddr, &slen)) == -1) {
return -1;
}
if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) {
close(peerfd);
return -1;
do {
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
peerfd = accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#else
if ((peerfd = accept(sockfd, saddr, &slen)) != -1) {
if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) {
uv__close(peerfd);
return -1;
}
}
#endif
}
while (peerfd == -1 && errno == EINTR);
return peerfd;
#endif
}
static int uv__close(int fd) {
int status;
/*
* Retry on EINTR. You may think this is academic but on linux
* and probably other Unices too, close(2) is interruptible.
* Failing to handle EINTR is a common source of fd leaks.
*/
do {
status = close(fd);
}
while (status == -1 && errno == EINTR);
return status;
}