#ifdef _EVENT_HAVE_SYS_SENDFILE_H
#include <sys/sendfile.h>
#endif
+#ifdef _EVENT_HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
#include <errno.h>
#include <stdio.h>
#define SENDFILE_IS_SOLARIS 1
#endif
-#ifdef USE_SENDFILE
-static int use_sendfile = 1;
-#endif
-#ifdef _EVENT_HAVE_MMAP
-static int use_mmap = 1;
-#endif
-
-
/* Mask of user-selectable callback flags. */
#define EVBUFFER_CB_USER_FLAGS 0xffff
/* Mask of all internal-use-only flags. */
static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
size_t datlen);
-#ifdef WIN32
-static int evbuffer_readfile(struct evbuffer *buf, evutil_socket_t fd,
- ev_ssize_t howmuch);
-#else
-#define evbuffer_readfile evbuffer_read
-#endif
-
static struct evbuffer_chain *
evbuffer_chain_new(size_t size)
{
chain->flags |= EVBUFFER_DANGLING;
return;
}
- if (chain->flags & (EVBUFFER_MMAP|EVBUFFER_SENDFILE|
- EVBUFFER_REFERENCE)) {
- if (chain->flags & EVBUFFER_REFERENCE) {
- struct evbuffer_chain_reference *info =
- EVBUFFER_CHAIN_EXTRA(
- struct evbuffer_chain_reference,
- chain);
- if (info->cleanupfn)
- (*info->cleanupfn)(chain->buffer,
- chain->buffer_len,
- info->extra);
- }
-#ifdef _EVENT_HAVE_MMAP
- if (chain->flags & EVBUFFER_MMAP) {
- struct evbuffer_chain_fd *info =
- EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd,
- chain);
- if (munmap(chain->buffer, chain->buffer_len) == -1)
- event_warn("%s: munmap failed", __func__);
- if (close(info->fd) == -1)
- event_warn("%s: close(%d) failed",
- __func__, info->fd);
- }
-#endif
-#ifdef USE_SENDFILE
- if (chain->flags & EVBUFFER_SENDFILE) {
- struct evbuffer_chain_fd *info =
- EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd,
- chain);
- if (close(info->fd) == -1)
- event_warn("%s: close(%d) failed",
- __func__, info->fd);
- }
-#endif
+
+ if (chain->flags & EVBUFFER_REFERENCE) {
+ struct evbuffer_chain_reference *info =
+ EVBUFFER_CHAIN_EXTRA(
+ struct evbuffer_chain_reference,
+ chain);
+ if (info->cleanupfn)
+ (*info->cleanupfn)(chain->buffer,
+ chain->buffer_len,
+ info->extra);
+ }
+ if (chain->flags & EVBUFFER_FILESEGMENT) {
+ struct evbuffer_chain_file_segment *info =
+ EVBUFFER_CHAIN_EXTRA(
+ struct evbuffer_chain_file_segment,
+ chain);
+ if (info->segment)
+ evbuffer_file_segment_free(info->segment);
}
mm_free(chain);
return result;
}
-#ifdef WIN32
-static int
-evbuffer_readfile(struct evbuffer *buf, evutil_socket_t fd, ev_ssize_t howmuch)
-{
- int result;
- int nchains, n;
- struct evbuffer_iovec v[2];
-
- EVBUFFER_LOCK(buf);
-
- if (buf->freeze_end) {
- result = -1;
- goto done;
- }
-
- if (howmuch < 0)
- howmuch = 16384;
-
-
- /* XXX we _will_ waste some space here if there is any space left
- * over on buf->last. */
- nchains = evbuffer_reserve_space(buf, howmuch, v, 2);
- if (nchains < 1 || nchains > 2) {
- result = -1;
- goto done;
- }
- n = read((int)fd, v[0].iov_base, (unsigned int)v[0].iov_len);
- if (n <= 0) {
- result = n;
- goto done;
- }
- v[0].iov_len = (IOV_LEN_TYPE) n; /* XXXX another problem with big n.*/
- if (nchains > 1) {
- n = read((int)fd, v[1].iov_base, (unsigned int)v[1].iov_len);
- if (n <= 0) {
- result = (unsigned long) v[0].iov_len;
- evbuffer_commit_space(buf, v, 1);
- goto done;
- }
- v[1].iov_len = n;
- }
- evbuffer_commit_space(buf, v, nchains);
-
- result = n;
-done:
- EVBUFFER_UNLOCK(buf);
- return result;
-}
-#endif
-
#ifdef USE_IOVEC_IMPL
static inline int
evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
#ifdef USE_SENDFILE
static inline int
-evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t fd,
+evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t dest_fd,
ev_ssize_t howmuch)
{
struct evbuffer_chain *chain = buffer->first;
- struct evbuffer_chain_fd *info =
- EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
+ struct evbuffer_chain_file_segment *info =
+ EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment,
+ chain);
+ const int source_fd = info->segment->fd;
#if defined(SENDFILE_IS_MACOSX) || defined(SENDFILE_IS_FREEBSD)
int res;
- off_t len = chain->off;
+ ev_off_t len = chain->off;
#elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS)
ev_ssize_t res;
- off_t offset = chain->misalign;
+ ev_off_t offset = chain->misalign;
#endif
ASSERT_EVBUFFER_LOCKED(buffer);
#if defined(SENDFILE_IS_MACOSX)
- res = sendfile(info->fd, fd, chain->misalign, &len, NULL, 0);
+ res = sendfile(source_fd, dest_fd, chain->misalign, &len, NULL, 0);
if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
return (-1);
return (len);
#elif defined(SENDFILE_IS_FREEBSD)
- res = sendfile(info->fd, fd, chain->misalign, chain->off, NULL, &len, 0);
+ res = sendfile(source_fd, dest_fd, chain->misalign, chain->off, NULL, &len, 0);
if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
return (-1);
return (len);
#elif defined(SENDFILE_IS_LINUX)
/* TODO(niels): implement splice */
- res = sendfile(fd, info->fd, &offset, chain->off);
+ res = sendfile(dest_fd, source_fd, &offset, chain->off);
if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
return (0);
}
return (res);
#elif defined(SENDFILE_IS_SOLARIS)
- res = sendfile(fd, info->fd, &offset, chain->off);
+ res = sendfile(dest_fd, source_fd, &offset, chain->off);
if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
return (0);
return result;
}
-/* TODO(niels): maybe we don't want to own the fd, however, in that
- * case, we should dup it - dup is cheap. Perhaps, we should use a
- * callback instead?
- */
/* TODO(niels): we may want to add to automagically convert to mmap, in
* case evbuffer_remove() or evbuffer_pullup() are being used.
*/
-int
-evbuffer_add_file(struct evbuffer *outbuf, int fd,
- ev_off_t offset, ev_off_t length)
+struct evbuffer_file_segment *
+evbuffer_file_segment_new(
+ int fd, ev_off_t offset, ev_off_t length, unsigned flags)
{
-#if defined(USE_SENDFILE) || defined(_EVENT_HAVE_MMAP)
- struct evbuffer_chain *chain;
- struct evbuffer_chain_fd *info;
+ struct evbuffer_file_segment *seg =
+ mm_calloc(sizeof(struct evbuffer_file_segment), 1);
+ if (!seg)
+ return NULL;
+ seg->refcnt = 1;
+ seg->fd = fd;
+ seg->flags = flags;
+
+#ifdef WIN32
+#define lseek _lseeki64
+#define fstat _fstat
+#define stat _stat
#endif
- int ok = 1;
+ if (length == -1) {
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ goto err;
+ length = st.st_size;
+ }
+ seg->length = length;
#if defined(USE_SENDFILE)
- if (use_sendfile) {
- chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
- if (chain == NULL) {
- event_warn("%s: out of memory", __func__);
- return (-1);
- }
-
- chain->flags |= EVBUFFER_SENDFILE | EVBUFFER_IMMUTABLE;
- chain->buffer = NULL; /* no reading possible */
- chain->buffer_len = length + offset;
- chain->off = length;
- chain->misalign = offset;
-
- info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
- info->fd = fd;
-
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- mm_free(chain);
- ok = 0;
- } else {
- outbuf->n_add_for_cb += length;
- evbuffer_chain_insert(outbuf, chain);
- }
- } else
+ if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) {
+ seg->offset = offset;
+ seg->type = EVBUF_FS_SENDFILE;
+ goto done;
+ }
#endif
#if defined(_EVENT_HAVE_MMAP)
- if (use_mmap) {
+ /* TODO: Implement an mmap-alike for windows. */
+ if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
+ /* some mmap implementations require offset to be a multiple of
+ * the page size. most users of this api, are likely to use 0
+ * so mapping everything is not likely to be a problem.
+ * TODO(niels): determine page size and round offset to that
+ * page size to avoid mapping too much memory.
+ */
void *mapped = mmap(NULL, length + offset, PROT_READ,
#ifdef MAP_NOCACHE
- MAP_NOCACHE |
+ MAP_NOCACHE | /* ??? */
#endif
#ifdef MAP_FILE
MAP_FILE |
#endif
MAP_PRIVATE,
fd, 0);
- /* some mmap implementations require offset to be a multiple of
- * the page size. most users of this api, are likely to use 0
- * so mapping everything is not likely to be a problem.
- * TODO(niels): determine page size and round offset to that
- * page size to avoid mapping too much memory.
- */
if (mapped == MAP_FAILED) {
event_warn("%s: mmap(%d, %d, %zu) failed",
__func__, fd, 0, (size_t)(offset + length));
- return (-1);
+ } else {
+ seg->mapping = mapped;
+ seg->contents = ((char*)mapped)+offset;
+ seg->offset = offset;
+ seg->type = EVBUF_FS_MMAP;
+ goto done;
}
- chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
- if (chain == NULL) {
- event_warn("%s: out of memory", __func__);
- munmap(mapped, length);
- return (-1);
+ }
+#endif
+
+ {
+ ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos;
+ ev_off_t read_so_far = 0;
+ char *mem;
+ int e;
+ ev_ssize_t n = 0;
+ if (!(mem = mm_malloc(length)))
+ goto err;
+ if (start_pos < 0) {
+ mm_free(mem);
+ goto err;
+ }
+ if (lseek(fd, offset, SEEK_SET) < 0) {
+ mm_free(mem);
+ goto err;
+ }
+ while (read_so_far < length) {
+ n = read(fd, mem+read_so_far, length-read_so_far);
+ if (n <= 0)
+ break;
+ read_so_far += n;
}
- chain->flags |= EVBUFFER_MMAP | EVBUFFER_IMMUTABLE;
- chain->buffer = mapped;
- chain->buffer_len = length + offset;
- chain->off = length + offset;
+ e = errno;
+ pos = lseek(fd, start_pos, SEEK_SET);
+ if (n < 0 || (n == 0 && length > read_so_far)) {
+ mm_free(mem);
+ errno = e;
+ goto err;
+ } else if (pos < 0) {
+ mm_free(mem);
+ goto err;
+ }
- info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
- info->fd = fd;
+ seg->contents = mem;
+ seg->type = EVBUF_FS_IO;
+ }
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- info->fd = -1;
- evbuffer_chain_free(chain);
- ok = 0;
- } else {
- outbuf->n_add_for_cb += length;
+done:
+ if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
+ EVTHREAD_ALLOC_LOCK(seg->lock, 0);
+ }
+ return seg;
+err:
+ mm_free(seg);
+ return NULL;
+}
- evbuffer_chain_insert(outbuf, chain);
+void
+evbuffer_file_segment_free(struct evbuffer_file_segment *seg)
+{
+ int refcnt;
+ EVLOCK_LOCK(seg->lock, 0);
+ refcnt = --seg->refcnt;
+ EVLOCK_UNLOCK(seg->lock, 0);
+ if (refcnt > 0)
+ return;
+ EVUTIL_ASSERT(refcnt == 0);
- /* we need to subtract whatever we don't need */
- evbuffer_drain(outbuf, offset);
- }
- } else
-#endif
- {
- /* the default implementation */
- struct evbuffer *tmp = evbuffer_new();
- ev_ssize_t read;
+ if (seg->type == EVBUF_FS_SENDFILE) {
+ ;
+ } else if (seg->type == EVBUF_FS_MMAP) {
+ if (munmap(seg->mapping, seg->length) == -1)
+ event_warn("%s: munmap failed", __func__);
+ } else {
+ EVUTIL_ASSERT(seg->type == EVBUF_FS_IO);
+ mm_free(seg->contents);
+ }
- if (tmp == NULL)
- return (-1);
+ if ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) {
+ close(seg->fd);
+ }
-#ifdef WIN32
-#define lseek _lseeki64
-#endif
- if (lseek(fd, offset, SEEK_SET) == -1) {
- evbuffer_free(tmp);
- return (-1);
- }
+ EVTHREAD_FREE_LOCK(seg->lock, 0);
+ mm_free(seg);
+}
- /* we add everything to a temporary buffer, so that we
- * can abort without side effects if the read fails.
- */
- while (length) {
- read = evbuffer_readfile(tmp, fd, (ev_ssize_t)length);
- if (read == -1) {
- evbuffer_free(tmp);
- return (-1);
- }
+int
+evbuffer_add_file_segment(struct evbuffer *buf,
+ struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length)
+{
+ struct evbuffer_chain *chain;
+ struct evbuffer_chain_file_segment *extra;
- length -= read;
- }
+ EVLOCK_LOCK(seg->lock, 0);
+ ++seg->refcnt;
+ EVLOCK_UNLOCK(seg->lock, 0);
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- evbuffer_free(tmp);
- ok = 0;
- } else {
- evbuffer_add_buffer(outbuf, tmp);
- evbuffer_free(tmp);
+ EVBUFFER_LOCK(buf);
-#ifdef WIN32
-#define close _close
-#endif
- close(fd);
- }
+ if (buf->freeze_end)
+ goto err;
+
+ if (length < 0) {
+ if (offset > seg->length)
+ goto err;
+ length = seg->length - offset;
}
- if (ok)
- evbuffer_invoke_callbacks(outbuf);
- EVBUFFER_UNLOCK(outbuf);
+ /* Can we actually add this? */
+ if (offset+length > seg->length)
+ goto err;
- return ok ? 0 : -1;
+ chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_file_segment));
+ if (!chain)
+ goto err;
+ extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain);
+
+ chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT;
+ if (seg->type == EVBUF_FS_SENDFILE) {
+ chain->flags |= EVBUFFER_SENDFILE;
+ chain->misalign = seg->offset + offset;
+ chain->off = length;
+ chain->buffer_len = chain->misalign + length;
+ } else if (seg->type == EVBUF_FS_MMAP) {
+ chain->buffer = (unsigned char*)(seg->contents + offset);
+ chain->buffer_len = length;
+ chain->off = length;
+ } else {
+ EVUTIL_ASSERT(seg->type == EVBUF_FS_IO);
+ chain->buffer = (unsigned char*)(seg->contents + offset);
+ chain->buffer_len = length;
+ chain->off = length;
+ }
+
+ extra->segment = seg;
+ buf->n_add_for_cb += length;
+ evbuffer_chain_insert(buf, chain);
+
+ evbuffer_invoke_callbacks(buf);
+
+ EVBUFFER_UNLOCK(buf);
+
+ return 0;
+err:
+ EVBUFFER_UNLOCK(buf);
+ evbuffer_file_segment_free(seg);
+ return -1;
}
+int
+evbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length)
+{
+ struct evbuffer_file_segment *seg;
+ unsigned flags = EVBUF_FS_CLOSE_ON_FREE;
+ int r;
+
+ seg = evbuffer_file_segment_new(fd, offset, length, flags);
+ if (!seg)
+ return -1;
+ r = evbuffer_add_file_segment(buf, seg, 0, length);
+ evbuffer_file_segment_free(seg);
+ return r;
+}
void
evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
}
#endif
-/* These hooks are exposed so that the unit tests can temporarily disable
- * sendfile support in order to test mmap, or both to test linear
- * access. Don't use it; if we need to add a way to disable sendfile support
- * in the future, it will probably be via an alternate version of
- * evbuffer_add_file() with a 'flags' argument.
- */
-int _evbuffer_testing_use_sendfile(void);
-int _evbuffer_testing_use_mmap(void);
-int _evbuffer_testing_use_linear_file_access(void);
-
-int
-_evbuffer_testing_use_sendfile(void)
-{
- int ok = 0;
-#ifdef USE_SENDFILE
- use_sendfile = 1;
- ok = 1;
-#endif
-#ifdef _EVENT_HAVE_MMAP
- use_mmap = 0;
-#endif
- return ok;
-}
-int
-_evbuffer_testing_use_mmap(void)
-{
- int ok = 0;
-#ifdef USE_SENDFILE
- use_sendfile = 0;
-#endif
-#ifdef _EVENT_HAVE_MMAP
- use_mmap = 1;
- ok = 1;
-#endif
- return ok;
-}
-int
-_evbuffer_testing_use_linear_file_access(void)
-{
-#ifdef USE_SENDFILE
- use_sendfile = 0;
-#endif
-#ifdef _EVENT_HAVE_MMAP
- use_mmap = 0;
-#endif
- return 1;
-}
The results of using evbuffer_remove() or evbuffer_pullup() are
undefined.
+ For more fine-grained control, use evbuffer_add_file_segment.
+
@param outbuf the output buffer
@param fd the file descriptor
@param off the offset from which to read data
- @param length how much data to read
+ @param length how much data to read, or -1 to read as much as possible.
+ (-1 requires that 'fd' support fstat.)
@return 0 if successful, or -1 if an error occurred
*/
int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset,
ev_off_t length);
+/**
+ An evbuffer_file_segment holds a reference to a range of a file --
+ possibly the whole file! -- for use in writing from an evbuffer to a
+ socket. It could be implemented with mmap, sendfile, splice, or (if all
+ else fails) by just pulling all the data into RAM. A single
+ evbuffer_file_segment can be added more than once, and to more than one
+ evbuffer.
+ */
+struct evbuffer_file_segment;
+
+/**
+ Flag for creating evbuffer_file_segment: If this flag is set, then when
+ the evbuffer_file_segment is freed and no longer in use by any
+ evbuffer, the underlying fd is closed.
+ */
+#define EVBUF_FS_CLOSE_ON_FREE 0x01
+/**
+ Flag for creating evbuffer_file_segment: Disable memory-map based
+ implementations.
+ */
+#define EVBUF_FS_DISABLE_MMAP 0x02
+/**
+ Flag for creating evbuffer_file_segment: Disable direct fd-to-fd
+ implementations (including sendfile and splice).
+
+ You might want to use this option if data needs to be taken from the
+ evbuffer by any means other than writing it to the network: the sendfile
+ backend is fast, but it only works for sending files directly to the
+ network.
+ */
+#define EVBUF_FS_DISABLE_SENDFILE 0x04
+/**
+ Flag for creating evbuffer_file_segment: Do not allocate a lock for this
+ segment. If this option is set, then neither the segment nor any
+ evbuffer it is added to may ever be accessed from more than one thread
+ at a time.
+ */
+#define EVBUF_FS_DISABLE_LOCKING 0x08
+
+/**
+ Create and return a new evbuffer_file_segment for reading data from a
+ file and sending it out via an evbuffer.
+
+ This function avoids unnecessary data copies between userland and
+ kernel. Where available, it uses sendfile or splice.
+
+ The file descriptor must not be closed so long as any evbuffer is using
+ this segment.
+
+ The results of using evbuffer_remove() or evbuffer_pullup() or any other
+ function that reads bytes from an evbuffer on any evbuffer containing
+ the newly returned segment are undefined, unless you pass the
+ EVBUF_FS_DISABLE_SENDFILE flag to this function.
+
+ @param fd an open file to read from.
+ @param offset an index within the file at which to start reading
+ @param length how much data to read, or -1 to read as much as possible.
+ (-1 requires that 'fd' support fstat.)
+ @param flags any number of the EVBUF_FS_* flags
+ @return a new evbuffer_file_segment, or NULL on failure.
+ **/
+struct evbuffer_file_segment *evbuffer_file_segment_new(
+ int fd, ev_off_t offset, ev_off_t length, unsigned flags);
+
+/**
+ Free an evbuffer_file_segment
+
+ It is safe to call this function even if the segment has been added to
+ one or more evbuffers. The evbuffer_file_segment will not be freed
+ until no more references to it exist.
+ */
+void evbuffer_file_segment_free(struct evbuffer_file_segment *seg);
+
+/**
+ Insert some or all of an evbuffer_file_segment at the end of an evbuffer
+
+ Note that the offset and length parameters of this function have a
+ different meaning from those provided to evbuffer_file_segment_new: When
+ you create the segment, the offset is the offset _within the file_, and
+ the length is the length _of the segment_, whereas when you add a
+ segment to an evbuffer, the offset is _within the segment_ and the
+ length is the length of the _part of the segment you want to use.
+
+ In other words, if you have a 10 KiB file, and you create an
+ evbuffer_file_segment for it with offset 20 and length 1000, it will
+ refer to bytes 20..1019 inclusive. If you then pass this segment to
+ evbuffer_add_file_segment and specify an offset of 20 and a length of
+ 50, you will be adding bytes 40..99 inclusive.
+
+ @param buf the evbuffer to append to
+ @param seg the segment to add
+ @param offset the offset within the segment to start from
+ @param length the amount of data to add, or -1 to add it all.
+ @return 0 on success, -1 on failure.
+ */
+int evbuffer_add_file_segment(struct evbuffer *buf,
+ struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length);
+
/**
Append a formatted string to the end of an evbuffer.
evbuffer_free(src);
}
-int _evbuffer_testing_use_sendfile(void);
-int _evbuffer_testing_use_mmap(void);
-int _evbuffer_testing_use_linear_file_access(void);
-
static void
test_evbuffer_add_file(void *ptr)
{
int fd = -1;
evutil_socket_t pair[2] = {-1, -1};
int r=0, n_written=0;
+ int want_type = 0;
+ unsigned flags = 0;
+ int use_segment = 1;
+ struct evbuffer_file_segment *seg = NULL;
/* Add a test for a big file. XXXX */
tt_assert(impl);
- if (!strcmp(impl, "sendfile")) {
- if (!_evbuffer_testing_use_sendfile())
- tt_skip();
- TT_BLATHER(("Using sendfile-based implementaion"));
+ if (!strcmp(impl, "nosegment")) {
+ use_segment = 0;
+ } else if (!strcmp(impl, "sendfile")) {
+ flags = EVBUF_FS_DISABLE_MMAP;
+ want_type = EVBUF_FS_SENDFILE;
} else if (!strcmp(impl, "mmap")) {
- if (!_evbuffer_testing_use_mmap())
- tt_skip();
- TT_BLATHER(("Using mmap-based implementaion"));
+ flags = EVBUF_FS_DISABLE_SENDFILE;
+ want_type = EVBUF_FS_MMAP;
} else if (!strcmp(impl, "linear")) {
- if (!_evbuffer_testing_use_linear_file_access())
- tt_skip();
- TT_BLATHER(("Using read-based implementaion"));
+ flags = EVBUF_FS_DISABLE_SENDFILE|EVBUF_FS_DISABLE_MMAP;
+ want_type = EVBUF_FS_IO;
} else {
TT_DIE(("Didn't recognize the implementation"));
}
+ datalen = strlen(data);
+ fd = regress_make_tmpfile(data, datalen);
+
+ if (use_segment) {
+ seg = evbuffer_file_segment_new(fd, 0, datalen, flags);
+ tt_assert(seg);
+ if ((int)seg->type != (int)want_type)
+ tt_skip();
+ }
+
#if defined(_EVENT_HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__)
/* We need to use a pair of AF_INET sockets, since Solaris
doesn't support sendfile() over AF_UNIX. */
tt_abort_msg("socketpair failed");
#endif
- datalen = strlen(data);
- fd = regress_make_tmpfile(data, datalen);
-
tt_assert(fd != -1);
- tt_assert(evbuffer_add_file(src, fd, 0, datalen) != -1);
+ if (use_segment) {
+ tt_assert(evbuffer_add_file_segment(src, seg, 0, -1)!=-1);
+ } else {
+ tt_assert(evbuffer_add_file(src, fd, 0, -1) != -1);
+ }
evbuffer_validate(src);
evbuffer_validate(src);
compare = (char *)evbuffer_pullup(src, datalen);
tt_assert(compare != NULL);
- if (memcmp(compare, data, datalen))
+ if (memcmp(compare, data, datalen)) {
tt_abort_msg("Data from add_file differs.");
+ }
evbuffer_validate(src);
end:
if (pair[1] >= 0)
evutil_closesocket(pair[1]);
evbuffer_free(src);
+ if (seg)
+ evbuffer_file_segment_free(seg);
}
#ifndef _EVENT_DISABLE_MM_REPLACEMENT
(void*)"mmap" },
{ "add_file_linear", test_evbuffer_add_file, TT_FORK, &nil_setup,
(void*)"linear" },
+ { "add_file_nosegment", test_evbuffer_add_file, TT_FORK, &nil_setup,
+ (void*)"nosegment" },
END_OF_TESTCASES
};