Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
unix: emulate sendfile if necessary
Browse files Browse the repository at this point in the history
Some platforms don't support sendfile() (netbsd, openbsd), others don't support
arbitrary file descriptors (freebsd, darwin). Emulate sendfile() if need be.
  • Loading branch information
bnoordhuis committed Oct 2, 2012
1 parent 8399cb1 commit b77b648
Showing 1 changed file with 121 additions and 11 deletions.
132 changes: 121 additions & 11 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,21 +245,131 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
}


__attribute__((unused))
static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) {
int reset_nbio;
int use_pread;
off_t offset;
ssize_t nsent;
ssize_t nread;
ssize_t nwritten;
size_t buflen;
size_t len;
ssize_t n;
int in_fd;
int out_fd;
char buf[8192];

len = req->len;
in_fd = req->flags;
out_fd = req->file;
offset = req->off;
use_pread = 1;
reset_nbio = 0;

/* Here are the rules regarding errors:
*
* 1. Read errors are reported only if nsent==0, otherwise we return nsent.
* The user needs to know that some data has already been sent, to stop
* him from sending it twice.
*
* 2. Write errors are always reported. Write errors are bad because they
* mean data loss: we've read data but we can't write it out...

This comment has been minimized.

Copy link
@piscisaureus

piscisaureus Oct 2, 2012

Is it not an idea to return nwritten iff use_pread is true?

This comment has been minimized.

Copy link
@bnoordhuis

bnoordhuis Oct 2, 2012

Author Contributor

No, if I understand what you're saying. use_pread is only to track if in_fd supports pread() (which files do but pipes, for example, don't).

This comment has been minimized.

Copy link
@piscisaureus

piscisaureus Oct 2, 2012

My idea was that people could possibly try to send the remainder of the data at a later time, because if pread is used they could probably re-read it. But thinking about it, shrug, it doesn't make much sense.

*/
for (nsent = 0; (size_t) nsent < len; ) {
buflen = len - nsent;

if (buflen > sizeof(buf))
buflen = sizeof(buf);

do {
if (use_pread)
nread = pread(in_fd, buf, buflen, offset);
else
nread = read(in_fd, buf, buflen);
}
while (nread == -1 && errno == EINTR);

if (nread == 0)
goto out;

if (nread == -1) {
if (use_pread && nsent == 0 && (errno == EIO || errno == ESPIPE)) {
use_pread = 0;
continue;
}

if (nsent == 0)
nsent = -1;

goto out;
}

for (nwritten = 0; nwritten < nread; ) {
do

This comment has been minimized.

Copy link
@piscisaureus

piscisaureus Oct 2, 2012

I'd prefer to use curlys here.

n = write(out_fd, buf + nwritten, nread - nwritten);
while (n == -1 && errno == EINTR);

if (n == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {

This comment has been minimized.

Copy link
@piscisaureus

piscisaureus Oct 2, 2012

Thread safety - what if the main thread is reading from the socket in the meantime, but there's no data available. dup() the fd instead?

uv__nonblock(out_fd, 0);
reset_nbio = 1;
continue;
}

nsent = -1;
goto out;
}

nwritten += n;
}

offset += nread;
nsent += nread;
}

out:
if (reset_nbio)
uv__nonblock(out_fd, 1);

if (nsent != -1)
req->off = offset;

return nsent;
}


static ssize_t uv__fs_sendfile(uv_fs_t* req) {
/* req->file is the out_fd, req->flags the in_fd */
int in_fd;
int out_fd;

in_fd = req->flags;
out_fd = req->file;

#if defined(__sun)
{
struct stat s;

if (fstat(in_fd, &s) == -1)
return -1;

if (!S_ISREG(s.st_mode))
return uv__fs_sendfile_emul(req);

if (fstat(out_fd, &s) == -1)
return -1;

if (!S_ISSOCK(s.st_mode))
return uv__fs_sendfile_emul(req);
}
#endif

#if defined(__linux__) || defined(__sun)
return sendfile(req->file, req->flags, &req->off, req->len);
return sendfile(out_fd, in_fd, &req->off, req->len);
#elif defined(__APPLE__) || defined(__FreeBSD__)
return sendfile(req->flags,
req->file,
req->off,
req->len,
NULL,
&req->off,
0);
return sendfile(in_fd, out_fd, req->off, req->len, NULL, &req->off, 0);
#else
errno = ENOSYS;
return -1;
return uv__fs_sendfile_emul(req);
#endif
}

Expand Down

1 comment on commit b77b648

@piscisaureus
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly lgtm, take care of the thread safety issue

Please sign in to comment.