Make evbuffer_file_segment_types adaptable

Instead of having a file segment born as one type and stay that way
forever, let them start out unmapped, but map themselves as needed
if they need to get written out on a non-drains_to_fd evbuffer.
This commit is contained in:
Nick Mathewson 2011-10-06 18:02:22 -04:00
parent 8358877768
commit c6bbbf1b67
3 changed files with 80 additions and 39 deletions

View File

@ -149,6 +149,8 @@ static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
size_t datlen);
static int evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos,
size_t howfar);
static int evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg);
static struct evbuffer_chain *
evbuffer_chain_new(size_t size)
@ -204,7 +206,7 @@ evbuffer_chain_free(struct evbuffer_chain *chain)
chain);
if (info->segment) {
#ifdef _WIN32
if (info->segment->type == EVBUF_FS_MMAP)
if (info->segment->is_mapping)
UnmapViewOfFile(chain->buffer);
#endif
evbuffer_file_segment_free(info->segment);
@ -2680,6 +2682,7 @@ evbuffer_file_segment_new(
seg->refcnt = 1;
seg->fd = fd;
seg->flags = flags;
seg->file_offset = offset;
#ifdef _WIN32
#define lseek _lseeki64
@ -2696,11 +2699,37 @@ evbuffer_file_segment_new(
#if defined(USE_SENDFILE)
if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) {
seg->offset = offset;
seg->type = EVBUF_FS_SENDFILE;
seg->can_sendfile = 1;
goto done;
}
#endif
if (evbuffer_file_segment_materialize(seg)<0)
goto err;
done:
if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
EVTHREAD_ALLOC_LOCK(seg->lock, 0);
}
return seg;
err:
mm_free(seg);
return NULL;
}
/* DOCDOC */
/* Requires lock */
static int
evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg)
{
const unsigned flags = seg->flags;
const int fd = seg->fd;
const ev_off_t length = seg->length;
const ev_off_t offset = seg->file_offset;
if (seg->contents)
return 0; /* already materialized */
#if defined(_EVENT_HAVE_MMAP)
if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
off_t offset_rounded = 0, offset_leftover = 0;
@ -2736,8 +2765,8 @@ evbuffer_file_segment_new(
} else {
seg->mapping = mapped;
seg->contents = (char*)mapped+offset_leftover;
seg->offset = 0;
seg->type = EVBUF_FS_MMAP;
seg->mmap_offset = 0;
seg->is_mapping = 1;
goto done;
}
}
@ -2754,8 +2783,8 @@ evbuffer_file_segment_new(
NULL);
if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */
seg->mapping_handle = m;
seg->offset = offset;
seg->type = EVBUF_FS_MMAP;
seg->mmap_offset = offset;
seg->is_mapping = 1;
goto done;
}
}
@ -2795,17 +2824,12 @@ evbuffer_file_segment_new(
}
seg->contents = mem;
seg->type = EVBUF_FS_IO;
}
done:
if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
EVTHREAD_ALLOC_LOCK(seg->lock, 0);
}
return seg;
return 0;
err:
mm_free(seg);
return NULL;
return -1;
}
void
@ -2819,17 +2843,14 @@ evbuffer_file_segment_free(struct evbuffer_file_segment *seg)
return;
EVUTIL_ASSERT(refcnt == 0);
if (seg->type == EVBUF_FS_SENDFILE) {
;
} else if (seg->type == EVBUF_FS_MMAP) {
if (seg->is_mapping) {
#ifdef _WIN32
CloseHandle(seg->mapping_handle);
#elif defined (_EVENT_HAVE_MMAP)
if (munmap(seg->mapping, seg->length) == -1)
event_warn("%s: munmap failed", __func__);
#endif
} else {
EVUTIL_ASSERT(seg->type == EVBUF_FS_IO);
} else if (seg->contents) {
mm_free(seg->contents);
}
@ -2847,12 +2868,23 @@ evbuffer_add_file_segment(struct evbuffer *buf,
{
struct evbuffer_chain *chain;
struct evbuffer_chain_file_segment *extra;
EVLOCK_LOCK(seg->lock, 0);
++seg->refcnt;
EVLOCK_UNLOCK(seg->lock, 0);
int can_use_sendfile = 0;
EVBUFFER_LOCK(buf);
EVLOCK_LOCK(seg->lock, 0);
if (buf->flags & EVBUFFER_FLAG_DRAINS_TO_FD) {
can_use_sendfile = 1;
} else {
if (!seg->contents) {
if (evbuffer_file_segment_materialize(seg)<0) {
EVLOCK_UNLOCK(seg->lock, 0);
EVBUFFER_UNLOCK(buf);
return -1;
}
}
}
++seg->refcnt;
EVLOCK_UNLOCK(seg->lock, 0);
if (buf->freeze_end)
goto err;
@ -2873,14 +2905,14 @@ evbuffer_add_file_segment(struct evbuffer *buf,
extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain);
chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT;
if (seg->type == EVBUF_FS_SENDFILE) {
if (can_use_sendfile && seg->can_sendfile) {
chain->flags |= EVBUFFER_SENDFILE;
chain->misalign = seg->offset + offset;
chain->misalign = seg->file_offset + offset;
chain->off = length;
chain->buffer_len = chain->misalign + length;
} else if (seg->type == EVBUF_FS_MMAP) {
} else if (seg->is_mapping) {
#ifdef _WIN32
ev_uint64_t total_offset = seg->offset+offset;
ev_uint64_t total_offset = seg->mmap_offset+offset;
ev_uint64_t offset_rounded=0, offset_remaining=0;
LPVOID data;
if (total_offset) {
@ -2910,7 +2942,6 @@ evbuffer_add_file_segment(struct evbuffer *buf,
chain->off = length;
#endif
} else {
EVUTIL_ASSERT(seg->type == EVBUF_FS_IO);
chain->buffer = (unsigned char*)(seg->contents + offset);
chain->buffer_len = length;
chain->off = length;

View File

@ -220,7 +220,8 @@ struct evbuffer_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;
unsigned can_sendfile : 1;
unsigned is_mapping : 1;
/** The fd that we read the data from. */
int fd;
@ -233,11 +234,11 @@ struct evbuffer_file_segment {
/** If we're using mmap or IO, this is the content of the file
* segment. */
char *contents;
/** Position of this segment within the file. */
ev_off_t file_offset;
/** 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. */
ev_off_t offset;
* this data segment begins. */
ev_off_t mmap_offset;
/** The length of this segment. */
ev_off_t length;
};

View File

@ -652,7 +652,7 @@ test_evbuffer_add_file(void *ptr)
size_t datalen, expect_len;
const char *compare;
int fd = -1;
int want_type = 0;
int want_ismapping = -1, want_cansendfile = -1;
unsigned flags = 0;
int use_segment = 1, use_bigfile = 0, map_from_offset = 0,
view_from_offset = 0;
@ -692,17 +692,20 @@ test_evbuffer_add_file(void *ptr)
/* If sendfile is set, we try to use a sendfile/splice style
* backend. */
flags = EVBUF_FS_DISABLE_MMAP;
want_type = EVBUF_FS_SENDFILE;
want_cansendfile = 1;
want_ismapping = 0;
} else if (strstr(impl, "mmap")) {
/* If sendfile is set, we try to use a mmap/CreateFileMapping
* style backend. */
flags = EVBUF_FS_DISABLE_SENDFILE;
want_type = EVBUF_FS_MMAP;
want_ismapping = 1;
want_cansendfile = 0;
} else if (strstr(impl, "linear")) {
/* If linear is set, we try to use a read-the-whole-thing
* backend. */
flags = EVBUF_FS_DISABLE_SENDFILE|EVBUF_FS_DISABLE_MMAP;
want_type = EVBUF_FS_IO;
want_ismapping = 0;
want_cansendfile = 0;
} else if (strstr(impl, "default")) {
/* The caller doesn't care which backend we use. */
;
@ -747,8 +750,14 @@ test_evbuffer_add_file(void *ptr)
seg = evbuffer_file_segment_new(fd, starting_offset,
mapping_len, flags);
tt_assert(seg);
if ((int)seg->type != (int)want_type)
tt_skip();
if (want_ismapping >= 0) {
if (seg->is_mapping != (unsigned)want_ismapping)
tt_skip();
}
if (want_cansendfile >= 0) {
if (seg->can_sendfile != (unsigned)want_cansendfile)
tt_skip();
}
}
/* Say that it drains to a fd so that we can use sendfile. */