Implement the bucket allocator. This implementation uses a three-stage
allocation scheme: (1) allocate from the freelist, (2) if that fails, carve
a chunk off of an 8KB block, or (3) just pass the thing directly to
apr_allocator_alloc(). In early testing, this implementation has been
shown to perform better than the existing alternatives.
Reviewed by: Brian Pane
git-svn-id: https://svn.apache.org/repos/asf/apr/apr-util/trunk@58588 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/buckets/apr_buckets_alloc.c b/buckets/apr_buckets_alloc.c
index a38816d..e77ed09 100644
--- a/buckets/apr_buckets_alloc.c
+++ b/buckets/apr_buckets_alloc.c
@@ -52,33 +52,102 @@
* <http://www.apache.org/>.
*/
-#include <stdlib.h>
#include "apr_buckets.h"
+#include "apr_allocator.h"
-/*
- * XXX: this file will be filled in later
+#define ALLOC_AMT (8192 - APR_MEMNODE_T_SIZE)
+
+typedef struct node_header_t {
+ apr_size_t size;
+ apr_bucket_alloc_t *alloc;
+ apr_memnode_t *memnode;
+ struct node_header_t *next;
+} node_header_t;
+
+#define SIZEOF_NODE_HEADER_T APR_ALIGN_DEFAULT(sizeof(node_header_t))
+#define SMALL_NODE_SIZE (APR_BUCKET_ALLOC_SIZE + SIZEOF_NODE_HEADER_T)
+
+/** A list of free memory from which new buckets or private bucket
+ * structures can be allocated.
*/
-
struct apr_bucket_alloc_t {
- int x; /* temporary... some compilers trigger an error on empty structure defs */
+ apr_allocator_t *allocator;
+ node_header_t *freelist;
+ apr_memnode_t *blocks;
};
APU_DECLARE_NONSTD(apr_bucket_alloc_t *) apr_bucket_alloc_create(apr_pool_t *p)
{
- return (apr_bucket_alloc_t *)0xFECCFECC;
+ apr_allocator_t *allocator;
+ apr_bucket_alloc_t *list;
+ apr_memnode_t *block;
+
+ apr_allocator_create(&allocator);
+ block = apr_allocator_alloc(allocator, ALLOC_AMT);
+ list = (apr_bucket_alloc_t *)block->first_avail;
+ list->allocator = allocator;
+ list->freelist = NULL;
+ list->blocks = block;
+ block->first_avail += APR_ALIGN_DEFAULT(sizeof(*list));
+ return list;
}
APU_DECLARE_NONSTD(void) apr_bucket_alloc_destroy(apr_bucket_alloc_t *list)
{
+ apr_allocator_t *allocator = list->allocator;
+
+ apr_allocator_free(allocator, list->blocks);
+ apr_allocator_destroy(allocator);
}
APU_DECLARE_NONSTD(void *) apr_bucket_alloc(apr_size_t size,
apr_bucket_alloc_t *list)
{
- return malloc(size);
+ node_header_t *node;
+ apr_memnode_t *active = list->blocks;
+ char *endp;
+
+ size += SIZEOF_NODE_HEADER_T;
+ if (size <= SMALL_NODE_SIZE) {
+ if (list->freelist) {
+ node = list->freelist;
+ list->freelist = node->next;
+ }
+ else {
+ endp = active->first_avail + SMALL_NODE_SIZE;
+ if (endp >= active->endp) {
+ list->blocks = apr_allocator_alloc(list->allocator, ALLOC_AMT);
+ list->blocks->next = active;
+ active = list->blocks;
+ endp = active->first_avail + SMALL_NODE_SIZE;
+ }
+ node = (node_header_t *)active->first_avail;
+ node->alloc = list;
+ node->memnode = active;
+ node->size = SMALL_NODE_SIZE;
+ active->first_avail = endp;
+ }
+ }
+ else {
+ apr_memnode_t *memnode = apr_allocator_alloc(list->allocator, size);
+ node = (node_header_t *)memnode->first_avail;
+ node->alloc = list;
+ node->memnode = memnode;
+ node->size = size;
+ }
+ return ((char *)node) + SIZEOF_NODE_HEADER_T;
}
-APU_DECLARE_NONSTD(void) apr_bucket_free(void *block)
+APU_DECLARE_NONSTD(void) apr_bucket_free(void *mem)
{
- free(block);
+ node_header_t *node = (node_header_t *)((char *)mem - SIZEOF_NODE_HEADER_T);
+ apr_bucket_alloc_t *list = node->alloc;
+
+ if (node->size == SMALL_NODE_SIZE) {
+ node->next = list->freelist;
+ list->freelist = node;
+ }
+ else {
+ apr_allocator_free(list->allocator, node->memnode);
+ }
}
diff --git a/include/apr_buckets.h b/include/apr_buckets.h
index b1e5dbd..2a91e43 100644
--- a/include/apr_buckets.h
+++ b/include/apr_buckets.h
@@ -84,8 +84,8 @@
* @{
*/
-/** default bucket buffer size */
-#define APR_BUCKET_BUFF_SIZE 8192
+/** default bucket buffer size - 8KB minus room for memory allocator headers */
+#define APR_BUCKET_BUFF_SIZE 8000
typedef enum {
APR_BLOCK_READ, /* block until data becomes available */
@@ -614,6 +614,28 @@
apr_pool_t *readpool;
};
+typedef union apr_bucket_structs apr_bucket_structs;
+/**
+ * A union of all bucket structures so we know what
+ * the max size is.
+ */
+union apr_bucket_structs {
+ apr_bucket b;
+ apr_bucket_heap heap;
+ apr_bucket_pool pool;
+#if APR_HAS_MMAP
+ apr_bucket_mmap mmap;
+#endif
+ apr_bucket_file file;
+};
+
+/**
+ * The amount that apr_bucket_alloc() should allocate in the common case.
+ * Note: this is twice as big as apr_bucket_structs to allow breathing
+ * room for third-party bucket types.
+ */
+#define APR_BUCKET_ALLOC_SIZE APR_ALIGN_DEFAULT(2*sizeof(apr_bucket_structs))
+
/* ***** Bucket Brigade Functions ***** */
/**
* Create a new bucket brigade. The bucket brigade is originally empty.