Commit ce71acce authored by Rusty Russell's avatar Rusty Russell

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.
parent a18fb319
......@@ -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;
}
......
This diff is collapsed.
......@@ -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 */
......@@ -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;
}
for (i = 1; i < num; i += 2) {
alloc_free(mem, POOL_SIZE, p[i]);
}
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();
}
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment