diff --git a/libs/util/cmem.c b/libs/util/cmem.c index c98eeb389..cc55ef01a 100644 --- a/libs/util/cmem.c +++ b/libs/util/cmem.c @@ -72,6 +72,15 @@ link_free_line (memsuper_t *super, memline_t *line) super->free_lines[ind] = line; } +static void +unlink_free_line (memline_t *line) +{ + if (line->free_next) { + line->free_next->free_prev = line->free_prev; + } + *line->free_prev = line->free_next; +} + static void unlink_line (memline_t *line) { @@ -80,10 +89,7 @@ unlink_line (memline_t *line) } *line->block_prev = line->block_next; - if (line->free_next) { - line->free_next->free_prev = line->free_prev; - } - *line->free_prev = line->free_next; + unlink_free_line (line); } static memblock_t * __attribute__((noinline)) @@ -223,6 +229,9 @@ line_free (memsuper_t *super, memblock_t *block, void *mem) line->size += line->block_next->size; unlink_line (line->block_next); } + // the line changed size so needs to be relinked in the super + unlink_free_line (line); + link_free_line (super, line); return; } if ((size_t) mem >= (size_t) line diff --git a/libs/util/test/test-cmem.c b/libs/util/test/test-cmem.c index 6acd76c33..107c20ad4 100644 --- a/libs/util/test/test-cmem.c +++ b/libs/util/test/test-cmem.c @@ -1,9 +1,10 @@ #include -#include +#include #include #include #include "QF/cmem.h" +#include "QF/set.h" static int test_block (memsuper_t *super) @@ -46,6 +47,133 @@ test_block (memsuper_t *super) return 1; } +static int +check_block (memblock_t *block, int line_count, int allocated) +{ + set_t *visited = set_new (); + size_t free_bytes = 0; + int ret = 1; + int count = 0; + + for (memline_t **l = &block->free_lines; *l; l = &(*l)->block_next) { + memline_t *line = *l; + ptrdiff_t ind = (memline_t *) block - line; + if (ind < 1 || (size_t ) ind > block->pre_size / MEM_LINE_SIZE) { + fprintf (stderr, "line outside of block\n"); + return 0; + } + if (set_is_member (visited, ind)) { + fprintf (stderr, "loop in block free_lines\n"); + return 0; + } + count++; + set_add (visited, ind); + if (!line->size) { + fprintf (stderr, "line with size 0\n"); + ret = 0; + } + if (line->block_next && line->block_next < line) { + fprintf (stderr, "line link in wrong direction\n"); + ret = 0; + } + if ((size_t) line + line->size == (size_t) line->block_next) { + fprintf (stderr, "adjacant free line blocks\n"); + ret = 0; + } + if (line->block_prev != l) { + fprintf (stderr, "line block_prev link incorrect\n"); + ret = 0; + } + if (line->size > block->pre_size) { + fprintf (stderr, "line size too large: %zd / %zd\n", + line->size, block->pre_size); + ret = 0; + } + if (line->size % MEM_LINE_SIZE) { + fprintf (stderr, "bad line size: %zd / %zd\n", + line->size, line->size % MEM_LINE_SIZE); + ret = 0; + } + if (ret) { + free_bytes += line->size; + } + } + if (ret) { + if (free_bytes + block->pre_allocated != block->pre_size) { + fprintf (stderr, "block space mismatch: s: %zd a: %zd f: %zd\n", + block->pre_size, block->pre_allocated, free_bytes); + ret = 0; + } + if (line_count >= 0 && line_count != count) { + fprintf (stderr, "incorrect number of lines: e: %d g: %d\n", + line_count, count); + ret = 0; + } + if (allocated >= 0 && (size_t) allocated != block->pre_allocated) { + fprintf (stderr, "pre_allocated wrong size: %zd != %d\n", + block->pre_allocated, allocated); + } + } + set_delete (visited); + return ret; +} + +static int +check_for_loop (memline_t *line, memline_t **stop) +{ + memline_t *next = line->free_next; + + for (memline_t **l = line->free_prev; l != stop; + l = line->free_prev) { + line = (memline_t *) l; + if (line == next) { + return 1; + } + } + return 0; +} + +static int +check_bins (memsuper_t *super, int mask) +{ + int ret = 1; + for (int i = MAX_CACHE_LINES; i-- > 0; ) { + if (mask >= 0) { + if (mask & (1 << i)) { + if (!super->free_lines[i]) { + fprintf (stderr, "super free_lines[%d] is empty\n", i); + ret = 0; + } + } else { + if (super->free_lines[i]) { + fprintf (stderr, "super free_lines[%d] is occupied\n", i); + ret = 0; + } + } + } + for (memline_t **l = &super->free_lines[i]; *l; l = &(*l)->free_next) { + memline_t *line = *l; + if (line->free_prev != l) { + fprintf (stderr, "super free_lines[%d] has bad prev\n", i); + ret = 0; + break; + } + if (check_for_loop (line, &super->free_lines[i])) { + fprintf (stderr, "super free_lines[%d] loop detected\n", i); + ret = 0; + break; + } + if ((MEM_LINE_SIZE << i) > (int) line->size + || (MEM_LINE_SIZE << (i + 1)) <= (int) line->size) { + fprintf (stderr, "line in wrong i: %d %d %zd\n", + i, MEM_LINE_SIZE << i, line->size); + ret = 0; + } + } + } + return ret; +} + static int test_line (memsuper_t *super) { @@ -90,68 +218,153 @@ test_line (memsuper_t *super) fprintf (stderr, "line3 not contiguous with free lines\n"); return 0; } - if (block->free_lines->block_next) { - fprintf (stderr, "multiple free line blocks\n"); + + if (!check_block (block, 1, 3 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 1 failed\n"); return 0; } - if (block->pre_allocated != 3 * MEM_LINE_SIZE) { - fprintf (stderr, "pre_allocated wrong size: %zd != %d\n", - block->pre_allocated, 3 * MEM_LINE_SIZE); + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 1 failed\n"); return 0; } - if (block->free_lines->size != block->pre_size - block->pre_allocated) { - fprintf (stderr, "free lines wrong size: %zd != %zd\n", - block->free_lines->size, - block->pre_size - block->pre_allocated); - return 0; - } - size_t old_size = block->free_lines->size; - memline_t *old_line = block->free_lines; + cmemfree (super, line2); - if (block->pre_allocated != 2 * MEM_LINE_SIZE) { - fprintf (stderr, "pre_allocated wrong size: %zd != %d\n", - block->pre_allocated, 2 * MEM_LINE_SIZE); + + if (!check_block (block, 2, 2 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 2 failed\n"); return 0; } + if (!check_bins (super, 0x21)) { + fprintf (stderr, "bin check 2 failed\n"); + return 0; + } + if (block->free_lines != line2) { - fprintf (stderr, "free lines not pointing to line2\n"); + fprintf (stderr, "block free_lines not pointing to line2\n"); return 0; } - if (!block->free_lines->block_next - || block->free_lines->block_next->block_next) { - fprintf (stderr, "incorrect number of free blocks\n"); - return 0; - } - if (line2->block_next != old_line || old_line->size != old_size) { - fprintf (stderr, "free line blocks corrupted\n"); - return 0; - } - if (block->free_lines->size != MEM_LINE_SIZE) { - fprintf (stderr, "free line block wrong size: %zd != %d\n", - block->free_lines->size, MEM_LINE_SIZE); + if (super->free_lines[0] != line2) { + fprintf (stderr, "super free_lines[0] not pointing to line2\n"); return 0; } + cmemfree (super, line3); + if (!check_block (block, 1, 1 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 3 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 3 failed\n"); + return 0; + } + if (block->free_lines != line2) { fprintf (stderr, "free lines not pointing to line2 2\n"); return 0; } - if (block->pre_allocated != MEM_LINE_SIZE) { - fprintf (stderr, "pre_allocated wrong size: %zd != %d\n", - block->pre_allocated, MEM_LINE_SIZE); - return 0; - } - if (block->free_lines->size != block->pre_size - block->pre_allocated) { - fprintf (stderr, "free lines wrong size: %zd != %zd\n", - block->free_lines->size, - block->pre_size - block->pre_allocated); - return 0; - } + cmemfree (super, line1); if (super->memblocks) { fprintf (stderr, "line pool not freed\n"); return 0; } + if (!check_bins (super, 0x00)) { + fprintf (stderr, "bins not cleared\n"); + return 0; + } + + line1 = cmemalloc (super, MEM_LINE_SIZE); + line2 = cmemalloc (super, MEM_LINE_SIZE); + line3 = cmemalloc (super, MEM_LINE_SIZE); + block = super->memblocks; + + if (!check_block (block, 1, 3 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 4 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 4 failed\n"); + return 0; + } + + cmemfree (super, line1); + + if (!check_block (block, 2, 2 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 5 failed\n"); + return 0; + } + if (!check_bins (super, 0x21)) { + fprintf (stderr, "bin check 5 failed\n"); + return 0; + } + + cmemfree (super, line2); + + if (!check_block (block, 2, 1 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 6 failed\n"); + return 0; + } + if (!check_bins (super, 0x22)) { + fprintf (stderr, "bin check 6 failed\n"); + return 0; + } + + cmemfree (super, line3); + if (super->memblocks) { + fprintf (stderr, "line pool not freed 2\n"); + return 0; + } + if (!check_bins (super, 0x00)) { + fprintf (stderr, "bins not cleared 2\n"); + return 0; + } + + line1 = cmemalloc (super, MEM_LINE_SIZE); + line2 = cmemalloc (super, MEM_LINE_SIZE); + line3 = cmemalloc (super, MEM_LINE_SIZE); + block = super->memblocks; + + if (!check_block (block, 1, 3 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 7 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 7 failed\n"); + return 0; + } + + cmemfree (super, line3); + + if (!check_block (block, 1, 2 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 8 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 8 failed\n"); + return 0; + } + + cmemfree (super, line2); + + if (!check_block (block, 1, 1 * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 9 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 9 failed\n"); + return 0; + } + + cmemfree (super, line1); + if (super->memblocks) { + fprintf (stderr, "line pool not freed 3\n"); + return 0; + } + if (!check_bins (super, 0x00)) { + fprintf (stderr, "bins not cleared 3\n"); + return 0; + } + return 1; } @@ -252,9 +465,10 @@ int main (void) { memsuper_t *super = new_memsuper (); + int i; - if (sizeof (memsuper_t) != MEM_LINE_SIZE) { - fprintf (stderr, "memsuper_t not cache size: %zd\n", + if (sizeof (memsuper_t) != 2 * MEM_LINE_SIZE) { + fprintf (stderr, "memsuper_t not 2 * cache size: %zd\n", sizeof (memsuper_t)); return 1; } @@ -301,13 +515,50 @@ main (void) fprintf (stderr, "super block list not null\n"); return 1; } + for (i = 4; i-- > 0; ) { + if (super->last_freed[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super last_freed not all null\n"); + return 1; + } + for (i = MAX_CACHE_LINES; i-- > 0; ) { + if (super->free_lines[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super free_lines not all null\n"); + return 1; + } if (!test_block (super)) { fprintf (stderr, "block tests failed\n"); + return 1; } if (super->memblocks) { fprintf (stderr, "super block list not null 2\n"); return 1; } + for (i = 4; i-- > 0; ) { + if (super->last_freed[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super last_freed not all null 2\n"); + return 1; + } + for (i = MAX_CACHE_LINES; i-- > 0; ) { + if (super->free_lines[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super free_lines not all null 2\n"); + return 1; + } if (!test_line (super)) { fprintf (stderr, "line tests failed\n"); return 1;