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

View File

@ -220,7 +220,8 @@ struct evbuffer_file_segment {
unsigned flags; /**< combination of EVBUF_FS_* flags */ unsigned flags; /**< combination of EVBUF_FS_* flags */
/** What kind of file segment is this? */ /** 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. */ /** The fd that we read the data from. */
int fd; int fd;
@ -233,11 +234,11 @@ struct evbuffer_file_segment {
/** If we're using mmap or IO, this is the content of the file /** If we're using mmap or IO, this is the content of the file
* segment. */ * segment. */
char *contents; 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 /** If we're using mmap, this is the offset within 'mapping' where
* this data segment begins. If we're using sendfile, this is the * this data segment begins. */
* offset within the file where this data begins. If we're using IO, ev_off_t mmap_offset;
* this is 0. */
ev_off_t offset;
/** The length of this segment. */ /** The length of this segment. */
ev_off_t length; ev_off_t length;
}; };

View File

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