From: Nick Mathewson Date: Wed, 8 Apr 2009 03:03:59 +0000 (+0000) Subject: Add a new facility to "pin" the memory in an evbuffer chain. X-Git-Tag: release-2.0.1-alpha~47 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d9086fc00777ff841209fdc8373a1b3ae784dec6;p=libevent Add a new facility to "pin" the memory in an evbuffer chain. For overlapped IO (and possibly other stuff) we need to be able to label an evbuffer_chain as "pinned", meaning that every byte in it must remain at the same address as it is now until it unpinned. This differs from being "immutable": it is okay to add data to the end of a pinned chain, so long as existing data is not moved. svn:r1142 --- diff --git a/buffer.c b/buffer.c index f5f8a2c0..c1656cfb 100644 --- a/buffer.c +++ b/buffer.c @@ -130,6 +130,11 @@ static int use_mmap = 1; #define CHAIN_SPACE_LEN(ch) ((ch)->flags & EVBUFFER_IMMUTABLE ? \ 0 : (ch)->buffer_len - ((ch)->misalign + (ch)->off)) + +#define CHAIN_PINNED(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_ANY) != 0) + +static void evbuffer_chain_align(struct evbuffer_chain *chain); + static struct evbuffer_chain * evbuffer_chain_new(size_t size) { @@ -162,6 +167,10 @@ evbuffer_chain_new(size_t size) static inline void evbuffer_chain_free(struct evbuffer_chain *chain) { + if (CHAIN_PINNED(chain)) { + chain->flags |= EVBUFFER_DANGLING; + return; + } if (chain->flags & (EVBUFFER_MMAP|EVBUFFER_SENDFILE| EVBUFFER_REFERENCE)) { if (chain->flags & EVBUFFER_REFERENCE) { @@ -207,7 +216,7 @@ evbuffer_chain_insert(struct evbuffer *buf, struct evbuffer_chain *chain) buf->previous_to_last = NULL; } else { /* the last chain is empty so we can just drop it */ - if (buf->last->off == 0) { + if (buf->last->off == 0 && !CHAIN_PINNED(buf->last)) { evbuffer_chain_free(buf->last); buf->previous_to_last->next = chain; buf->last = chain; @@ -221,6 +230,24 @@ evbuffer_chain_insert(struct evbuffer *buf, struct evbuffer_chain *chain) buf->total_len += chain->off; } +#ifdef NOT_USED_YET +static void +evbuffer_chain_pin(struct evbuffer_chain *chain, unsigned flag) +{ + assert((chain->flags & flag) == 0); + chain->flags |= flag; +} + +static void +evbuffer_chain_unpin(struct evbuffer_chain *chain, unsigned flag) +{ + assert((chain->flags & flag) != 0); + chain->flags &= ~flag; + if (chain->flags & EVBUFFER_DANGLING) + evbuffer_chain_free(chain); +} +#endif + struct evbuffer * evbuffer_new(void) { @@ -524,6 +551,9 @@ evbuffer_drain(struct evbuffer *buf, size_t len) if (old_len == 0) goto done; + /* TODO(nickm) when we drain the last byte from a chain, we + * should not unlink or free it if it is pinned. */ + if (len >= old_len) { len = old_len; for (chain = buf->first; chain != NULL; chain = next) { @@ -698,6 +728,7 @@ evbuffer_pullup(struct evbuffer *buf, ssize_t size) { struct evbuffer_chain *chain, *next, *tmp; unsigned char *buffer, *result = NULL; + ssize_t remaining; EVBUFFER_LOCK(buf, EVTHREAD_WRITE); @@ -718,7 +749,28 @@ evbuffer_pullup(struct evbuffer *buf, ssize_t size) goto done; } - if (chain->buffer_len - chain->misalign >= size) { + /* Make sure that none of the chains we need to copy from is pinned. */ + remaining = size - chain->off; + for (tmp=chain->next; tmp; tmp=tmp->next) { + if (CHAIN_PINNED(tmp)) + goto done; + if (tmp->off >= remaining) + break; + remaining -= tmp->off; + } + + if (CHAIN_PINNED(chain)) { + size_t old_off = chain->off; + if (CHAIN_SPACE_LEN(chain) < size - chain->off) { + /* not enough room at end of chunk. */ + goto done; + } + buffer = CHAIN_SPACE_PTR(chain); + tmp = chain; + tmp->off = size; + size -= old_off; + chain = chain->next; + } else if (chain->buffer_len - chain->misalign >= size) { /* already have enough space in the first chain */ size_t old_off = chain->off; buffer = chain->buffer + chain->misalign + chain->off; @@ -988,12 +1040,9 @@ evbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen) buf->total_len += datlen; buf->n_add_for_cb += datlen; goto out; - } else if (chain->misalign >= datlen) { + } else if (chain->misalign >= datlen && !CHAIN_PINNED(chain)) { /* we can fit the data into the misalignment */ - memmove(chain->buffer, - chain->buffer + chain->misalign, - chain->off); - chain->misalign = 0; + evbuffer_chain_align(chain); memcpy(chain->buffer + chain->off, data, datlen); chain->off += datlen; @@ -1106,6 +1155,7 @@ static void evbuffer_chain_align(struct evbuffer_chain *chain) { assert(!(chain->flags & EVBUFFER_IMMUTABLE)); + assert(!(chain->flags & EVBUFFER_MEM_PINNED_ANY)); memmove(chain->buffer, chain->buffer + chain->misalign, chain->off); chain->misalign = 0; } @@ -1125,7 +1175,8 @@ evbuffer_expand(struct evbuffer *buf, size_t datlen) chain = buf->last; - if (chain == NULL || (chain->flags & EVBUFFER_IMMUTABLE)) { + if (chain == NULL || + (chain->flags & (EVBUFFER_IMMUTABLE|EVBUFFER_MEM_PINNED_ANY))) { chain = evbuffer_chain_new(datlen); if (chain == NULL) goto err; diff --git a/evbuffer-internal.h b/evbuffer-internal.h index 41128cfc..026b19a0 100644 --- a/evbuffer-internal.h +++ b/evbuffer-internal.h @@ -76,7 +76,8 @@ struct evbuffer { void *lock; #endif unsigned own_lock : 1; - int lock_count : 31; + + int lock_count; TAILQ_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks; }; @@ -103,6 +104,14 @@ struct evbuffer_chain { #define EVBUFFER_SENDFILE 0x0002 /**< a chain used for sendfile */ #define EVBUFFER_REFERENCE 0x0004 /**< a chain with a mem reference */ #define EVBUFFER_IMMUTABLE 0x0008 /**< read-only chain */ + /** a chain that mustn't be reallocated or freed, or have its contents + * memmoved, until the chain is un-pinned. */ +#define EVBUFFER_MEM_PINNED_R 0x0010 +#define EVBUFFER_MEM_PINNED_W 0x0020 +#define EVBUFFER_MEM_PINNED_ANY (EVBUFFER_MEM_PINNED_R|EVBUFFER_MEM_PINNED_W) + /** a chain that should be freed, but can't be freed until it is + * un-pinned. */ +#define EVBUFFER_DANGLING 0x0040 /** Usually points to the read-write memory belonging to this * buffer allocated as part of the evbuffer_chain allocation.