Browse Source

alloc: first cut of new Tridge-inspired allocator

This version has limitations: pools must be at least 1MB, and allocations
are restricted to 1/1024 of the total pool size.
Rusty Russell 16 years ago
parent
commit
ce71acceee
5 changed files with 679 additions and 756 deletions
  1. 2 0
      ccan/alloc/_info
  2. 543 706
      ccan/alloc/alloc.c
  3. 119 0
      ccan/alloc/alloc.h
  4. 11 50
      ccan/alloc/test/run.c
  5. 4 0
      config.h

+ 2 - 0
ccan/alloc/_info

@@ -101,6 +101,8 @@ int main(int argc, char *argv[])
 
 	if (strcmp(argv[1], "depends") == 0) {
 		printf("ccan/build_assert\n");
+		printf("ccan/likely\n");
+		printf("ccan/short_types\n");
 		return 0;
 	}
 

File diff suppressed because it is too large
+ 543 - 706
ccan/alloc/alloc.c


+ 119 - 0
ccan/alloc/alloc.h

@@ -3,12 +3,131 @@
 #include <stdio.h>
 #include <stdbool.h>
 
+/**
+ * alloc_init - initialize a pool of memory for the allocator.
+ * @pool: the contiguous bytes for the allocator to use
+ * @poolsize: the size of the pool
+ *
+ * This stores all the setup state required to perform allocation within the
+ * pool (there is no external state).  Any previous contents of @pool is
+ * discarded.
+ *
+ * The same @pool and @poolsize arguments must be handed to the other alloc
+ * functions after this.
+ *
+ * If the pool is too small for meaningful allocations, alloc_get will fail.
+ *
+ * Example:
+ *	void *pool = malloc(32*1024*1024);
+ *	if (!pool)
+ *		err(1, "Failed to allocate 32MB");
+ *	alloc_init(pool, 32*1024*1024);
+ */
 void alloc_init(void *pool, unsigned long poolsize);
+
+/**
+ * alloc_get - allocate some memory from the pool
+ * @pool: the contiguous bytes for the allocator to use
+ * @poolsize: the size of the pool
+ * @size: the size of the desired allocation
+ * @align: the alignment of the desired allocation (0 or power of 2)
+ *
+ * This is "malloc" within an initialized pool.
+ *
+ * It will return a unique pointer within the pool (ie. between @pool
+ * and @pool+@poolsize) which meets the alignment requirements of
+ * @align.  Note that the alignment is relative to the start of the pool,
+ * so of @pool is not aligned, the pointer won't be either.
+ *
+ * Returns NULL if there is no contiguous room.
+ *
+ * Example:
+ *	#include <ccan/alignof/alignof.h>
+ *	...
+ *		double *d = alloc_get(pool, 32*1024*1024,
+ *				      sizeof(*d), ALIGNOF(*d));
+ *		if (!d)
+ *			err(1, "Failed to allocate a double");
+ */
 void *alloc_get(void *pool, unsigned long poolsize,
 		unsigned long size, unsigned long align);
+
+/**
+ * alloc_free - free some allocated memory from the pool
+ * @pool: the contiguous bytes for the allocator to use
+ * @poolsize: the size of the pool
+ * @p: the non-NULL pointer returned from alloc_get.
+ *
+ * This is "free" within an initialized pool.  A pointer should only be
+ * freed once, and must be a pointer returned from a successful alloc_get()
+ * call.
+ *
+ * Example:
+ *	alloc_free(pool, 32*1024*1024, d);
+ */
 void alloc_free(void *pool, unsigned long poolsize, void *free);
+
+/**
+ * alloc_size - get the actual size allocated by alloc_get
+ * @pool: the contiguous bytes for the allocator to use
+ * @poolsize: the size of the pool
+ * @p: the non-NULL pointer returned from alloc_get.
+ *
+ * alloc_get() may overallocate, in which case you may use the extra
+ * space exactly as if you had asked for it.
+ *
+ * The return value will always be at least the @size passed to alloc_get().
+ *
+ * Example:
+ *	printf("Allocating a double actually got me %lu bytes\n",
+ *		alloc_size(pool, 32*1024*1024, d));
+ */
 unsigned long alloc_size(void *pool, unsigned long poolsize, void *p);
+
+/**
+ * alloc_check - check the integrity of the allocation pool
+ * @pool: the contiguous bytes for the allocator to use
+ * @poolsize: the size of the pool
+ *
+ * alloc_check() can be used for debugging suspected pool corruption.  It may
+ * be quite slow, but provides some assistance for hard-to-find overruns or
+ * double-frees.  Unlike the rest of the code, it will not crash on corrupted
+ * pools.
+ *
+ * There is an internal function check_fail() which this calls on failure which
+ * is useful for placing breakpoints and gaining more insight into the type
+ * of the corruption detected.
+ *
+ * Example:
+ *	#include <assert.h>
+ *
+ *	...
+ *		assert(alloc_check(pool, 32*1024*1024));
+ */
 bool alloc_check(void *pool, unsigned long poolsize);
 
+/**
+ * alloc_visualize - dump information about the allocation pool
+ * @pool: the contiguous bytes for the allocator to use
+ * @poolsize: the size of the pool
+ *
+ * When debugging the allocator itself, it's often useful to see how
+ * the pool is being used.  alloc_visualize() does that, but makes
+ * assumptions about correctness (like the rest of the code) so if you
+ * suspect corruption call alloc_check() first.
+ *
+ * Example:
+ *	#include <stdio.h>
+ *
+ *	double *d = alloc_get(pool, 32*1024*1024,
+ *			      sizeof(*d), ALIGNOF(*d));
+ *	if (!d) {
+ *		fprintf(stderr, "Allocation failed!\n");
+ *		if (!alloc_check(pool, 32*1024*1024))
+ *			errx(1, "Allocation pool is corrupt");
+ *		alloc_visualize(stderr, pool, 32*1024*1024));
+ *		exit(1);
+ *	}
+ */
 void alloc_visualize(FILE *out, void *pool, unsigned long poolsize);
 #endif /* ALLOC_H */

+ 11 - 50
ccan/alloc/test/run.c

@@ -4,7 +4,7 @@
 #include <stdlib.h>
 #include <err.h>
 
-#define POOL_ORD 16
+#define POOL_ORD 20
 #define POOL_SIZE (1 << POOL_ORD)
 
 #define sort(p, num, cmp) \
@@ -32,14 +32,14 @@ static bool free_every_second_one(void *mem, unsigned int num, void *p[])
 	/* Free every second one. */
 	for (i = 0; i < num; i += 2) {
 		alloc_free(mem, POOL_SIZE, p[i]);
-		if (!alloc_check(mem, POOL_SIZE))
-			return false;
 	}
+	if (!alloc_check(mem, POOL_SIZE))
+		return false;
 	for (i = 1; i < num; i += 2) {
 		alloc_free(mem, POOL_SIZE, p[i]);
-		if (!alloc_check(mem, POOL_SIZE))
-			return false;
 	}
+	if (!alloc_check(mem, POOL_SIZE))
+		return false;
 	return true;
 }
 
@@ -48,9 +48,9 @@ int main(int argc, char *argv[])
 {
 	void *mem;
 	unsigned int i, num, max_size;
-	void *p[POOL_SIZE];
+	void **p = calloc(POOL_SIZE, sizeof(*p));
 
-	plan_tests(178);
+	plan_tests(120);
 
 	/* FIXME: Needs to be page aligned for now. */
 	if (posix_memalign(&mem, 1 << POOL_ORD, POOL_SIZE) != 0)
@@ -134,7 +134,7 @@ int main(int argc, char *argv[])
 	ok1(alloc_check(mem, POOL_SIZE));
 
 	/* Alignment constraints should be met, as long as powers of two */
-	for (i = 0; i < POOL_ORD-1; i++) {
+	for (i = 0; i < /*FIXME: POOL_ORD-1*/ 10; i++) {
 		p[i] = alloc_get(mem, POOL_SIZE, i, 1 << i);
 		ok1(p[i]);
 		ok1(((unsigned long)p[i] % (1 << i)) == 0);
@@ -142,13 +142,13 @@ int main(int argc, char *argv[])
 		ok1(alloc_size(mem, POOL_SIZE, p[i]) >= i);
 	}
 
-	for (i = 0; i < POOL_ORD-1; i++) {
+	for (i = 0; i < /*FIXME: POOL_ORD-1*/ 10; i++) {
 		alloc_free(mem, POOL_SIZE, p[i]);
 		ok1(alloc_check(mem, POOL_SIZE));
 	}
 
 	/* Alignment constraints for a single-byte allocation. */
-	for (i = 0; i < POOL_ORD; i++) {
+	for (i = 0; i < /*FIXME: POOL_ORD*/ 10; i++) {
 		p[0] = alloc_get(mem, POOL_SIZE, 1, 1 << i);
 		ok1(p[0]);
 		ok1(alloc_check(mem, POOL_SIZE));
@@ -158,50 +158,11 @@ int main(int argc, char *argv[])
 	}
 
 	/* Alignment check for a 0-byte allocation.  Corner case. */
-	p[0] = alloc_get(mem, POOL_SIZE, 0, 1 << (POOL_ORD - 1));
+	p[0] = alloc_get(mem, POOL_SIZE, 0, 1 << (/*FIXME: POOL_ORD - 1*/ 10));
 	ok1(alloc_check(mem, POOL_SIZE));
 	ok1(alloc_size(mem, POOL_SIZE, p[0]) < POOL_SIZE);
 	alloc_free(mem, POOL_SIZE, p[0]);
 	ok1(alloc_check(mem, POOL_SIZE));
 
-	/* Force the testing of split metadata. */
-	alloc_init(mem, POOL_SIZE);
-	for (i = 0; i < POOL_SIZE; i++) {
-		p[i] = alloc_get(mem, POOL_SIZE, getpagesize(), getpagesize());
-		if (!p[i])
-			break;
-	}
-	ok1(alloc_check(mem, POOL_SIZE));
-	ok1(alloc_size(mem, POOL_SIZE, p[i-1]) >= getpagesize());
-
-	/* Sort them. */
-	sort(p, i-1, addr_cmp);
-
-	/* Free all but the one next to the metadata. */
-	for (i = 1; p[i]; i++)
-		alloc_free(mem, POOL_SIZE, p[i]);
-	ok1(alloc_check(mem, POOL_SIZE));
-	ok1(alloc_size(mem, POOL_SIZE, p[0]) >= getpagesize());
-
-	/* Now do a whole heap of subpage allocs. */
-	for (i = 1; i < POOL_SIZE; i++) {
-		p[i] = alloc_get(mem, POOL_SIZE, 1, 1);
-		if (!p[i])
-			break;
-	}
-	ok1(alloc_check(mem, POOL_SIZE));
-
-	/* Free up our page next to metadata, and should be able to alloc */
-	alloc_free(mem, POOL_SIZE, p[0]);
-	ok1(alloc_check(mem, POOL_SIZE));
-	p[0] = alloc_get(mem, POOL_SIZE, 1, 1);
-	ok1(p[0]);
-	ok1(alloc_size(mem, POOL_SIZE, p[0]) >= 1);
-
-	/* Clean up. */
-	for (i = 0; p[i]; i++)
-		alloc_free(mem, POOL_SIZE, p[i]);
-	ok1(alloc_check(mem, POOL_SIZE));
-
 	return exit_status();
 }

+ 4 - 0
config.h

@@ -13,3 +13,7 @@
 #define HAVE_STATEMENT_EXPR 1
 #define HAVE_TYPEOF 1
 #define HAVE_UTIME 1
+#define HAVE_BUILTIN_CLZL 1
+#define HAVE_BUILTIN_FFSL 1
+#define HAVE_BUILTIN_POPCOUNTL 1
+

Some files were not shown because too many files changed in this diff