mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-11 05:14:46 -04:00
Add evbuffer_add_file_segment() so one fd can be used efficiently in more than one evbuffer_add_file at a time
This commit is contained in:
parent
076680abe4
commit
e72afae068
505
buffer.c
505
buffer.c
@ -63,6 +63,10 @@
|
|||||||
#ifdef _EVENT_HAVE_SYS_SENDFILE_H
|
#ifdef _EVENT_HAVE_SYS_SENDFILE_H
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef _EVENT_HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -111,14 +115,6 @@
|
|||||||
#define SENDFILE_IS_SOLARIS 1
|
#define SENDFILE_IS_SOLARIS 1
|
||||||
#endif
|
#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. */
|
/* Mask of user-selectable callback flags. */
|
||||||
#define EVBUFFER_CB_USER_FLAGS 0xffff
|
#define EVBUFFER_CB_USER_FLAGS 0xffff
|
||||||
/* Mask of all internal-use-only flags. */
|
/* Mask of all internal-use-only flags. */
|
||||||
@ -144,13 +140,6 @@ static int evbuffer_ptr_memcmp(const struct evbuffer *buf,
|
|||||||
static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
|
static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
|
||||||
size_t datlen);
|
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 *
|
static struct evbuffer_chain *
|
||||||
evbuffer_chain_new(size_t size)
|
evbuffer_chain_new(size_t size)
|
||||||
{
|
{
|
||||||
@ -187,40 +176,24 @@ evbuffer_chain_free(struct evbuffer_chain *chain)
|
|||||||
chain->flags |= EVBUFFER_DANGLING;
|
chain->flags |= EVBUFFER_DANGLING;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (chain->flags & (EVBUFFER_MMAP|EVBUFFER_SENDFILE|
|
|
||||||
EVBUFFER_REFERENCE)) {
|
if (chain->flags & EVBUFFER_REFERENCE) {
|
||||||
if (chain->flags & EVBUFFER_REFERENCE) {
|
struct evbuffer_chain_reference *info =
|
||||||
struct evbuffer_chain_reference *info =
|
EVBUFFER_CHAIN_EXTRA(
|
||||||
EVBUFFER_CHAIN_EXTRA(
|
struct evbuffer_chain_reference,
|
||||||
struct evbuffer_chain_reference,
|
chain);
|
||||||
chain);
|
if (info->cleanupfn)
|
||||||
if (info->cleanupfn)
|
(*info->cleanupfn)(chain->buffer,
|
||||||
(*info->cleanupfn)(chain->buffer,
|
chain->buffer_len,
|
||||||
chain->buffer_len,
|
info->extra);
|
||||||
info->extra);
|
}
|
||||||
}
|
if (chain->flags & EVBUFFER_FILESEGMENT) {
|
||||||
#ifdef _EVENT_HAVE_MMAP
|
struct evbuffer_chain_file_segment *info =
|
||||||
if (chain->flags & EVBUFFER_MMAP) {
|
EVBUFFER_CHAIN_EXTRA(
|
||||||
struct evbuffer_chain_fd *info =
|
struct evbuffer_chain_file_segment,
|
||||||
EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd,
|
chain);
|
||||||
chain);
|
if (info->segment)
|
||||||
if (munmap(chain->buffer, chain->buffer_len) == -1)
|
evbuffer_file_segment_free(info->segment);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mm_free(chain);
|
mm_free(chain);
|
||||||
@ -2124,56 +2097,6 @@ done:
|
|||||||
return result;
|
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
|
#ifdef USE_IOVEC_IMPL
|
||||||
static inline int
|
static inline int
|
||||||
evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
|
evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
|
||||||
@ -2225,44 +2148,46 @@ evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
|
|||||||
|
|
||||||
#ifdef USE_SENDFILE
|
#ifdef USE_SENDFILE
|
||||||
static inline int
|
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)
|
ev_ssize_t howmuch)
|
||||||
{
|
{
|
||||||
struct evbuffer_chain *chain = buffer->first;
|
struct evbuffer_chain *chain = buffer->first;
|
||||||
struct evbuffer_chain_fd *info =
|
struct evbuffer_chain_file_segment *info =
|
||||||
EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
|
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)
|
#if defined(SENDFILE_IS_MACOSX) || defined(SENDFILE_IS_FREEBSD)
|
||||||
int res;
|
int res;
|
||||||
off_t len = chain->off;
|
ev_off_t len = chain->off;
|
||||||
#elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS)
|
#elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS)
|
||||||
ev_ssize_t res;
|
ev_ssize_t res;
|
||||||
off_t offset = chain->misalign;
|
ev_off_t offset = chain->misalign;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ASSERT_EVBUFFER_LOCKED(buffer);
|
ASSERT_EVBUFFER_LOCKED(buffer);
|
||||||
|
|
||||||
#if defined(SENDFILE_IS_MACOSX)
|
#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))
|
if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
return (len);
|
return (len);
|
||||||
#elif defined(SENDFILE_IS_FREEBSD)
|
#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))
|
if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
return (len);
|
return (len);
|
||||||
#elif defined(SENDFILE_IS_LINUX)
|
#elif defined(SENDFILE_IS_LINUX)
|
||||||
/* TODO(niels): implement splice */
|
/* 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 (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
|
||||||
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
|
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
return (res);
|
return (res);
|
||||||
#elif defined(SENDFILE_IS_SOLARIS)
|
#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 (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
|
||||||
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
|
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
|
||||||
return (0);
|
return (0);
|
||||||
@ -2654,153 +2579,226 @@ done:
|
|||||||
return result;
|
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
|
/* TODO(niels): we may want to add to automagically convert to mmap, in
|
||||||
* case evbuffer_remove() or evbuffer_pullup() are being used.
|
* case evbuffer_remove() or evbuffer_pullup() are being used.
|
||||||
*/
|
*/
|
||||||
int
|
struct evbuffer_file_segment *
|
||||||
evbuffer_add_file(struct evbuffer *outbuf, int fd,
|
evbuffer_file_segment_new(
|
||||||
ev_off_t offset, ev_off_t length)
|
int fd, ev_off_t offset, ev_off_t length, unsigned flags)
|
||||||
{
|
{
|
||||||
#if defined(USE_SENDFILE) || defined(_EVENT_HAVE_MMAP)
|
struct evbuffer_file_segment *seg =
|
||||||
struct evbuffer_chain *chain;
|
mm_calloc(sizeof(struct evbuffer_file_segment), 1);
|
||||||
struct evbuffer_chain_fd *info;
|
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
|
#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 defined(USE_SENDFILE)
|
||||||
if (use_sendfile) {
|
if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) {
|
||||||
chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
|
seg->offset = offset;
|
||||||
if (chain == NULL) {
|
seg->type = EVBUF_FS_SENDFILE;
|
||||||
event_warn("%s: out of memory", __func__);
|
goto done;
|
||||||
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
|
|
||||||
#endif
|
#endif
|
||||||
#if defined(_EVENT_HAVE_MMAP)
|
#if defined(_EVENT_HAVE_MMAP)
|
||||||
if (use_mmap) {
|
/* TODO: Implement an mmap-alike for windows. */
|
||||||
void *mapped = mmap(NULL, length + offset, PROT_READ,
|
if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
|
||||||
#ifdef 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
|
/* some mmap implementations require offset to be a multiple of
|
||||||
* the page size. most users of this api, are likely to use 0
|
* the page size. most users of this api, are likely to use 0
|
||||||
* so mapping everything is not likely to be a problem.
|
* so mapping everything is not likely to be a problem.
|
||||||
* TODO(niels): determine page size and round offset to that
|
* TODO(niels): determine page size and round offset to that
|
||||||
* page size to avoid mapping too much memory.
|
* page size to avoid mapping too much memory.
|
||||||
*/
|
*/
|
||||||
|
void *mapped = mmap(NULL, length + offset, PROT_READ,
|
||||||
|
#ifdef MAP_NOCACHE
|
||||||
|
MAP_NOCACHE | /* ??? */
|
||||||
|
#endif
|
||||||
|
#ifdef MAP_FILE
|
||||||
|
MAP_FILE |
|
||||||
|
#endif
|
||||||
|
MAP_PRIVATE,
|
||||||
|
fd, 0);
|
||||||
if (mapped == MAP_FAILED) {
|
if (mapped == MAP_FAILED) {
|
||||||
event_warn("%s: mmap(%d, %d, %zu) failed",
|
event_warn("%s: mmap(%d, %d, %zu) failed",
|
||||||
__func__, fd, 0, (size_t)(offset + length));
|
__func__, fd, 0, (size_t)(offset + length));
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
|
|
||||||
if (chain == NULL) {
|
|
||||||
event_warn("%s: out of memory", __func__);
|
|
||||||
munmap(mapped, length);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
chain->flags |= EVBUFFER_MMAP | EVBUFFER_IMMUTABLE;
|
|
||||||
chain->buffer = mapped;
|
|
||||||
chain->buffer_len = length + offset;
|
|
||||||
chain->off = length + offset;
|
|
||||||
|
|
||||||
info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
|
|
||||||
info->fd = fd;
|
|
||||||
|
|
||||||
EVBUFFER_LOCK(outbuf);
|
|
||||||
if (outbuf->freeze_end) {
|
|
||||||
info->fd = -1;
|
|
||||||
evbuffer_chain_free(chain);
|
|
||||||
ok = 0;
|
|
||||||
} else {
|
} else {
|
||||||
outbuf->n_add_for_cb += length;
|
seg->mapping = mapped;
|
||||||
|
seg->contents = ((char*)mapped)+offset;
|
||||||
evbuffer_chain_insert(outbuf, chain);
|
seg->offset = offset;
|
||||||
|
seg->type = EVBUF_FS_MMAP;
|
||||||
/* we need to subtract whatever we don't need */
|
goto done;
|
||||||
evbuffer_drain(outbuf, offset);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
/* the default implementation */
|
|
||||||
struct evbuffer *tmp = evbuffer_new();
|
|
||||||
ev_ssize_t read;
|
|
||||||
|
|
||||||
if (tmp == NULL)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#define lseek _lseeki64
|
|
||||||
#endif
|
|
||||||
if (lseek(fd, offset, SEEK_SET) == -1) {
|
|
||||||
evbuffer_free(tmp);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
length -= read;
|
|
||||||
}
|
|
||||||
|
|
||||||
EVBUFFER_LOCK(outbuf);
|
|
||||||
if (outbuf->freeze_end) {
|
|
||||||
evbuffer_free(tmp);
|
|
||||||
ok = 0;
|
|
||||||
} else {
|
|
||||||
evbuffer_add_buffer(outbuf, tmp);
|
|
||||||
evbuffer_free(tmp);
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#define close _close
|
|
||||||
#endif
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ok)
|
{
|
||||||
evbuffer_invoke_callbacks(outbuf);
|
ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos;
|
||||||
EVBUFFER_UNLOCK(outbuf);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
return ok ? 0 : -1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
seg->contents = mem;
|
||||||
|
seg->type = EVBUF_FS_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
|
||||||
|
EVTHREAD_ALLOC_LOCK(seg->lock, 0);
|
||||||
|
}
|
||||||
|
return seg;
|
||||||
|
err:
|
||||||
|
mm_free(seg);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) {
|
||||||
|
close(seg->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
EVTHREAD_FREE_LOCK(seg->lock, 0);
|
||||||
|
mm_free(seg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
EVLOCK_LOCK(seg->lock, 0);
|
||||||
|
++seg->refcnt;
|
||||||
|
EVLOCK_UNLOCK(seg->lock, 0);
|
||||||
|
|
||||||
|
EVBUFFER_LOCK(buf);
|
||||||
|
|
||||||
|
if (buf->freeze_end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
if (offset > seg->length)
|
||||||
|
goto err;
|
||||||
|
length = seg->length - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can we actually add this? */
|
||||||
|
if (offset+length > seg->length)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
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
|
void
|
||||||
evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
|
evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
|
||||||
@ -2936,50 +2934,3 @@ evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb)
|
|||||||
}
|
}
|
||||||
#endif
|
#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;
|
|
||||||
}
|
|
||||||
|
@ -170,8 +170,8 @@ struct evbuffer_chain {
|
|||||||
|
|
||||||
/** Set if special handling is required for this chain */
|
/** Set if special handling is required for this chain */
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
#define EVBUFFER_MMAP 0x0001 /**< memory in buffer is mmaped */
|
#define EVBUFFER_FILESEGMENT 0x0001 /**< A chain used for a file segment */
|
||||||
#define EVBUFFER_SENDFILE 0x0002 /**< a chain used for sendfile */
|
#define EVBUFFER_SENDFILE 0x0002 /**< a chain used with sendfile */
|
||||||
#define EVBUFFER_REFERENCE 0x0004 /**< a chain with a mem reference */
|
#define EVBUFFER_REFERENCE 0x0004 /**< a chain with a mem reference */
|
||||||
#define EVBUFFER_IMMUTABLE 0x0008 /**< read-only chain */
|
#define EVBUFFER_IMMUTABLE 0x0008 /**< read-only chain */
|
||||||
/** a chain that mustn't be reallocated or freed, or have its contents
|
/** a chain that mustn't be reallocated or freed, or have its contents
|
||||||
@ -192,21 +192,45 @@ struct evbuffer_chain {
|
|||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* this is currently used by both mmap and sendfile */
|
/** callback for a reference chain; lets us know what to do with it when
|
||||||
/* TODO(niels): something strange needs to happen for Windows here, I am not
|
* we're done with it. Lives at the end of an evbuffer_chain with the
|
||||||
* sure what that is, but it needs to get looked into.
|
* EVBUFFER_REFERENCE flag set */
|
||||||
*/
|
|
||||||
struct evbuffer_chain_fd {
|
|
||||||
int fd; /**< the fd associated with this chain */
|
|
||||||
};
|
|
||||||
|
|
||||||
/** callback for a reference buffer; lets us know what to do with it when
|
|
||||||
* we're done with it. */
|
|
||||||
struct evbuffer_chain_reference {
|
struct evbuffer_chain_reference {
|
||||||
evbuffer_ref_cleanup_cb cleanupfn;
|
evbuffer_ref_cleanup_cb cleanupfn;
|
||||||
void *extra;
|
void *extra;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** File segment for a file-segment chain. Lives at the end of an
|
||||||
|
* evbuffer_chain with the EVBUFFER_FILESEGMENT flag set. */
|
||||||
|
struct evbuffer_chain_file_segment {
|
||||||
|
struct evbuffer_file_segment *segment;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Declared in event2/buffer.h; defined here. */
|
||||||
|
struct evbuffer_file_segment {
|
||||||
|
void *lock; /**< lock prevent concurrent access to refcnt */
|
||||||
|
int refcnt; /**< Reference count for this file segment */
|
||||||
|
unsigned flags; /**< combination of EVBUF_FS_* flags */
|
||||||
|
|
||||||
|
/** What kind of file segment is this? */
|
||||||
|
enum {EVBUF_FS_MMAP, EVBUF_FS_SENDFILE, EVBUF_FS_IO} type;
|
||||||
|
|
||||||
|
/** The fd that we read the data from. */
|
||||||
|
int fd;
|
||||||
|
/** If we're using mmap, this is the raw mapped memory. */
|
||||||
|
void *mapping;
|
||||||
|
/** If we're using mmap or IO, this is the content of the file
|
||||||
|
* segment. */
|
||||||
|
char *contents;
|
||||||
|
/** If we're using mmap, this is the offset within 'mapping' where
|
||||||
|
* this data segment begins. If we're using sendfile, this is the
|
||||||
|
* offset within the file where this data begins. If we're using IO,
|
||||||
|
* this is 0. */
|
||||||
|
off_t offset;
|
||||||
|
/** The length of this segment. */
|
||||||
|
off_t length;
|
||||||
|
};
|
||||||
|
|
||||||
#define EVBUFFER_CHAIN_SIZE sizeof(struct evbuffer_chain)
|
#define EVBUFFER_CHAIN_SIZE sizeof(struct evbuffer_chain)
|
||||||
/** Return a pointer to extra data allocated along with an evbuffer. */
|
/** Return a pointer to extra data allocated along with an evbuffer. */
|
||||||
#define EVBUFFER_CHAIN_EXTRA(t, c) (t *)((struct evbuffer_chain *)(c) + 1)
|
#define EVBUFFER_CHAIN_EXTRA(t, c) (t *)((struct evbuffer_chain *)(c) + 1)
|
||||||
|
@ -368,16 +368,117 @@ int evbuffer_add_reference(struct evbuffer *outbuf,
|
|||||||
The results of using evbuffer_remove() or evbuffer_pullup() are
|
The results of using evbuffer_remove() or evbuffer_pullup() are
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
For more fine-grained control, use evbuffer_add_file_segment.
|
||||||
|
|
||||||
@param outbuf the output buffer
|
@param outbuf the output buffer
|
||||||
@param fd the file descriptor
|
@param fd the file descriptor
|
||||||
@param off the offset from which to read data
|
@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
|
@return 0 if successful, or -1 if an error occurred
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset,
|
int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset,
|
||||||
ev_off_t length);
|
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.
|
Append a formatted string to the end of an evbuffer.
|
||||||
|
|
||||||
|
@ -583,10 +583,6 @@ test_evbuffer_reference(void *ptr)
|
|||||||
evbuffer_free(src);
|
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
|
static void
|
||||||
test_evbuffer_add_file(void *ptr)
|
test_evbuffer_add_file(void *ptr)
|
||||||
{
|
{
|
||||||
@ -598,26 +594,39 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
int fd = -1;
|
int fd = -1;
|
||||||
evutil_socket_t pair[2] = {-1, -1};
|
evutil_socket_t pair[2] = {-1, -1};
|
||||||
int r=0, n_written=0;
|
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 */
|
/* Add a test for a big file. XXXX */
|
||||||
|
|
||||||
tt_assert(impl);
|
tt_assert(impl);
|
||||||
if (!strcmp(impl, "sendfile")) {
|
if (!strcmp(impl, "nosegment")) {
|
||||||
if (!_evbuffer_testing_use_sendfile())
|
use_segment = 0;
|
||||||
tt_skip();
|
} else if (!strcmp(impl, "sendfile")) {
|
||||||
TT_BLATHER(("Using sendfile-based implementaion"));
|
flags = EVBUF_FS_DISABLE_MMAP;
|
||||||
|
want_type = EVBUF_FS_SENDFILE;
|
||||||
} else if (!strcmp(impl, "mmap")) {
|
} else if (!strcmp(impl, "mmap")) {
|
||||||
if (!_evbuffer_testing_use_mmap())
|
flags = EVBUF_FS_DISABLE_SENDFILE;
|
||||||
tt_skip();
|
want_type = EVBUF_FS_MMAP;
|
||||||
TT_BLATHER(("Using mmap-based implementaion"));
|
|
||||||
} else if (!strcmp(impl, "linear")) {
|
} else if (!strcmp(impl, "linear")) {
|
||||||
if (!_evbuffer_testing_use_linear_file_access())
|
flags = EVBUF_FS_DISABLE_SENDFILE|EVBUF_FS_DISABLE_MMAP;
|
||||||
tt_skip();
|
want_type = EVBUF_FS_IO;
|
||||||
TT_BLATHER(("Using read-based implementaion"));
|
|
||||||
} else {
|
} else {
|
||||||
TT_DIE(("Didn't recognize the implementation"));
|
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__)
|
#if defined(_EVENT_HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__)
|
||||||
/* We need to use a pair of AF_INET sockets, since Solaris
|
/* We need to use a pair of AF_INET sockets, since Solaris
|
||||||
doesn't support sendfile() over AF_UNIX. */
|
doesn't support sendfile() over AF_UNIX. */
|
||||||
@ -628,12 +637,13 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
tt_abort_msg("socketpair failed");
|
tt_abort_msg("socketpair failed");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
datalen = strlen(data);
|
|
||||||
fd = regress_make_tmpfile(data, datalen);
|
|
||||||
|
|
||||||
tt_assert(fd != -1);
|
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);
|
||||||
|
|
||||||
@ -650,8 +660,9 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
evbuffer_validate(src);
|
evbuffer_validate(src);
|
||||||
compare = (char *)evbuffer_pullup(src, datalen);
|
compare = (char *)evbuffer_pullup(src, datalen);
|
||||||
tt_assert(compare != NULL);
|
tt_assert(compare != NULL);
|
||||||
if (memcmp(compare, data, datalen))
|
if (memcmp(compare, data, datalen)) {
|
||||||
tt_abort_msg("Data from add_file differs.");
|
tt_abort_msg("Data from add_file differs.");
|
||||||
|
}
|
||||||
|
|
||||||
evbuffer_validate(src);
|
evbuffer_validate(src);
|
||||||
end:
|
end:
|
||||||
@ -660,6 +671,8 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
if (pair[1] >= 0)
|
if (pair[1] >= 0)
|
||||||
evutil_closesocket(pair[1]);
|
evutil_closesocket(pair[1]);
|
||||||
evbuffer_free(src);
|
evbuffer_free(src);
|
||||||
|
if (seg)
|
||||||
|
evbuffer_file_segment_free(seg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _EVENT_DISABLE_MM_REPLACEMENT
|
#ifndef _EVENT_DISABLE_MM_REPLACEMENT
|
||||||
@ -1562,6 +1575,8 @@ struct testcase_t evbuffer_testcases[] = {
|
|||||||
(void*)"mmap" },
|
(void*)"mmap" },
|
||||||
{ "add_file_linear", test_evbuffer_add_file, TT_FORK, &nil_setup,
|
{ "add_file_linear", test_evbuffer_add_file, TT_FORK, &nil_setup,
|
||||||
(void*)"linear" },
|
(void*)"linear" },
|
||||||
|
{ "add_file_nosegment", test_evbuffer_add_file, TT_FORK, &nil_setup,
|
||||||
|
(void*)"nosegment" },
|
||||||
|
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user