diff --git a/ChangeLog b/ChangeLog
index eb3ccf75d6..e1b04c50e5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -38,9 +38,9 @@ Changes in version 0.2.0.1-alpha - 2007-??-??
internally and transfer the data in-process. This saves two
sockets per anonymous directory connection (at the client and at
the server), and avoids the nasty Windows socketpair() workaround.
- - Keep unused 4k buffers on a free list, rather than wasting 8k for every
- single inactive connection_t.
- - Free items from the 4k-buffer free list when they haven't been used
+ - Keep unused 4k and 16k buffers on free lists, rather than wasting 8k
+ for every single inactive connection_t.
+ - Free items from the 4/16k-buffer free lists when they haven't been used
for a while.
o Minor features (build):
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 6296ffa7c7..8750ba5bd1 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -158,80 +158,122 @@ _split_range(buf_t *buf, char *at, size_t *len,
}
}
-/** DOCDOC */
-static char *free_mem_list = NULL;
-static int free_mem_list_len = 0;
-static int free_mem_list_lowwater = 0;
+/** A freelist of buffer RAM chunks. */
+typedef struct free_mem_list_t {
+ char *list; /**< The first item on the list; begins with pointer to the
+ * next item. */
+ int len; /**< How many entries in list. */
+ int lowwater; /**< The smallest that list has gotten since the last call to
+ * buf_shrink_freelists(). */
+ const size_t chunksize; /**< How big are the items on the list? */
+ const int slack; /**< We always keep at least this many items on the list
+ * when shrinking it. */
+ const int max; /**< How many elements are we willing to throw onto the list?
+ */
+} free_mem_list_t;
-/** DOCDOC */
-static void
+/** Freelists to hold 4k and 16k memory chunks. This seems to be what
+ * we use most. */
+static free_mem_list_t free_mem_list_4k = { NULL, 0, 0, 4096, 16, INT_MAX };
+static free_mem_list_t free_mem_list_16k = { NULL, 0, 0, 16384, 4, 128 };
+
+/** Macro: True iff the size is one for which we keep a freelist. */
+#define IS_FREELIST_SIZE(sz) ((sz) == 4096 || (sz) == 16384)
+
+/** Return the proper freelist for chunks of size sz, or fail
+ * with an assertion. */
+static INLINE free_mem_list_t *
+get_free_mem_list(size_t sz)
+{
+ if (sz == 4096) {
+ return &free_mem_list_4k;
+ } else {
+ tor_assert(sz == 16384);
+ return &free_mem_list_16k;
+ }
+}
+
+/** Throw the memory from buf onto the appropriate freelist.
+ * Return true if we added the memory, 0 if the freelist was full. */
+static int
add_buf_mem_to_freelist(buf_t *buf)
{
char *mem;
+ free_mem_list_t *list;
- tor_assert(buf->len == MIN_LAZY_SHRINK_SIZE);
tor_assert(buf->datalen == 0);
tor_assert(buf->mem);
+ list = get_free_mem_list(buf->len);
+
+ if (list->len >= list->max)
+ return 0;
mem = RAW_MEM(buf->mem);
buf->len = buf->memsize = 0;
buf->mem = buf->cur = NULL;
- *(char**)mem = free_mem_list;
- free_mem_list = mem;
- ++free_mem_list_len;
- log_info(LD_GENERAL, "Add buf mem to freelist. Freelist has %d entries.",
- free_mem_list_len);
+ *(char**)mem = list->list;
+ list->list = mem;
+ ++list->len;
+ log_debug(LD_GENERAL, "Add buf mem to %d-byte freelist. Freelist has "
+ "%d entries.", (int)list->chunksize, list->len);
+
+ return 1;
}
-/** DOCDOC */
+/** Pull memory of size sz from the appropriate freelist for use by
+ * buf, or allocate it as needed. */
static void
-buf_get_initial_mem(buf_t *buf)
+buf_get_initial_mem(buf_t *buf, size_t sz)
{
char *mem;
+ free_mem_list_t *list = get_free_mem_list(sz);
tor_assert(!buf->mem);
- if (free_mem_list) {
- mem = free_mem_list;
- free_mem_list = *(char**)mem;
- if (--free_mem_list_len < free_mem_list_lowwater)
- free_mem_list_lowwater = free_mem_list_len;
- log_info(LD_GENERAL, "Got buf mem from freelist. Freelist has %d entries.",
- free_mem_list_len);
+ if (list->list) {
+ mem = list->list;
+ list->list = *(char**)mem;
+ if (--list->len < list->lowwater)
+ list->lowwater = list->len;
+ log_debug(LD_GENERAL, "Got buf mem from %d-byte freelist. Freelist has "
+ "%d entries.", (int)list->chunksize, list->len);
} else {
- log_info(LD_GENERAL, "Freelist empty; allocating another chunk.");
- tor_assert(free_mem_list_len == 0);
- mem = tor_malloc(ALLOC_LEN(MIN_LAZY_SHRINK_SIZE));
+ log_debug(LD_GENERAL, "%d-byte freelist empty; allocating another chunk.",
+ (int)list->chunksize);
+ tor_assert(list->len == 0);
+ mem = tor_malloc(ALLOC_LEN(sz));
}
buf->mem = GUARDED_MEM(mem);
- SET_GUARDS(buf->mem, MIN_LAZY_SHRINK_SIZE);
- buf->len = MIN_LAZY_SHRINK_SIZE;
- buf->memsize = ALLOC_LEN(MIN_LAZY_SHRINK_SIZE);
+ SET_GUARDS(buf->mem, sz);
+ buf->len = sz;
+ buf->memsize = ALLOC_LEN(sz);
buf->cur = buf->mem;
}
-/** DOCDOC 64k of 4k buffers. */
-#define BUF_FREELIST_SLACK 16
-
-/** DOCDOC */
+/** Remove elements from the freelists that haven't been needed since the
+ * last call to this function. */
void
-buf_shrink_freelist(void)
+buf_shrink_freelists(void)
{
- if (free_mem_list_lowwater > BUF_FREELIST_SLACK) {
- int i;
- log_info(LD_GENERAL, "We haven't used %d/%d allocated buffer memory "
- "chunks since the last call(); freeing all but %d of them",
- free_mem_list_lowwater, free_mem_list_len,
- BUF_FREELIST_SLACK);
- for (i = BUF_FREELIST_SLACK; i < free_mem_list_lowwater; ++i) {
- char *mem = free_mem_list;
- tor_assert(mem);
- free_mem_list = *(char**)mem;
- tor_free(mem);
- --free_mem_list_len;
+ int j;
+ for (j = 0; j < 2; ++j) {
+ free_mem_list_t *list = j ? &free_mem_list_16k : &free_mem_list_4k;
+ if (list->lowwater > list->slack) {
+ int i;
+ log_info(LD_GENERAL, "We haven't used %d/%d allocated %d-byte buffer "
+ "memory chunks since the last call; freeing all but %d of them",
+ list->lowwater, list->len, list->chunksize, list->slack);
+ for (i = list->slack; i < list->lowwater; ++i) {
+ /* XXXX we should really free the last few entries, not the first. */
+ char *mem = list->list;
+ tor_assert(mem);
+ list->list = *(char**)mem;
+ tor_free(mem);
+ --list->len;
+ }
}
+ list->lowwater = list->len;
}
- free_mem_list_lowwater = free_mem_list_len;
}
/** Change a buffer's capacity. new_capacity must be \>=
@@ -281,26 +323,12 @@ buf_resize(buf_t *buf, size_t new_capacity)
}
}
-#if 0
- /* XXX Some play code to throw away old buffers sometimes rather
- * than constantly reallocing them; just in case this is our memory
- * problem. It looks for now like it isn't, so disabled. -RD */
- if (0 && new_capacity == MIN_LAZY_SHRINK_SIZE &&
- !buf->datalen &&
- buf->len >= 1<<16) {
- /* don't realloc; free and malloc */
- char *oldmem, *newmem = GUARDED_MEM(tor_malloc(ALLOC_LEN(new_capacity)));
- SET_GUARDS(newmem, new_capacity);
- oldmem = RAW_MEM(buf->mem);
- tor_free(oldmem);
- buf->mem = buf->cur = newmem;
- } else { /* ... */ }
-#endif
-
- if (buf->len == 0 && new_capacity <= MIN_LAZY_SHRINK_SIZE) {
+ if (new_capacity < MIN_LAZY_SHRINK_SIZE)
new_capacity = MIN_LAZY_SHRINK_SIZE;
+
+ if (buf->len == 0 && IS_FREELIST_SIZE(new_capacity)) {
tor_assert(!buf->mem);
- buf_get_initial_mem(buf);
+ buf_get_initial_mem(buf, new_capacity);
} else {
char *raw;
if (buf->mem)
@@ -385,9 +413,9 @@ buf_shrink(buf_t *buf)
new_len = buf->len;
if (buf->datalen == 0 && buf->highwater == 0 &&
- buf->len == MIN_LAZY_SHRINK_SIZE) {
- add_buf_mem_to_freelist(buf);
- return;
+ IS_FREELIST_SIZE(buf->len)) {
+ if (add_buf_mem_to_freelist(buf))
+ return;
}
while (buf->highwater < (new_len>>2) && new_len > MIN_LAZY_SHRINK_SIZE*2)
new_len >>= 1;
@@ -433,8 +461,8 @@ buf_new_with_capacity(size_t size)
buf_t *buf;
buf = tor_malloc_zero(sizeof(buf_t));
buf->magic = BUFFER_MAGIC;
- if (size == MIN_LAZY_SHRINK_SIZE) {
- buf_get_initial_mem(buf);
+ if (IS_FREELIST_SIZE(size)) {
+ buf_get_initial_mem(buf, size);
} else {
buf->cur = buf->mem = GUARDED_MEM(tor_malloc(ALLOC_LEN(size)));
SET_GUARDS(buf->mem, size);
@@ -492,10 +520,12 @@ buf_free(buf_t *buf)
char *oldmem;
assert_buf_ok(buf);
buf->magic = 0xDEADBEEF;
- if (buf->len == MIN_LAZY_SHRINK_SIZE) {
+ if (IS_FREELIST_SIZE(buf->len)) {
buf->datalen = 0; /* Avoid assert in add_buf_mem_to_freelist. */
add_buf_mem_to_freelist(buf);
- } else if (buf->mem) {
+ }
+ if (buf->mem) {
+ /* The freelist didn't want the RAM. */
oldmem = RAW_MEM(buf->mem);
tor_free(oldmem);
}
diff --git a/src/or/main.c b/src/or/main.c
index 2c2387255a..14ca11d068 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1021,7 +1021,7 @@ run_scheduled_events(time_t now)
buf_shrink(conn->inbuf);
}
clean_cell_pool();
- buf_shrink_freelist();
+ buf_shrink_freelists();
time_to_shrink_memory = now + MEM_SHRINK_INTERVAL;
}
diff --git a/src/or/or.h b/src/or/or.h
index 4d3f193320..e5f9701acf 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2020,7 +2020,7 @@ buf_t *buf_new_with_capacity(size_t size);
void buf_free(buf_t *buf);
void buf_clear(buf_t *buf);
void buf_shrink(buf_t *buf);
-void buf_shrink_freelist(void);
+void buf_shrink_freelists(void);
size_t buf_datalen(const buf_t *buf);
size_t buf_capacity(const buf_t *buf);