diff --git a/src/snmalloc/mem/pool.h b/src/snmalloc/mem/pool.h index 537cc5e..7caddd2 100644 --- a/src/snmalloc/mem/pool.h +++ b/src/snmalloc/mem/pool.h @@ -190,5 +190,42 @@ namespace snmalloc return p->list_next.unsafe_ptr(); } + + /** + * Put the stack in a consistent order. This is helpful for systematic + * testing based systems. It is not thread safe, and the caller should + * ensure no other thread can be performing a `sort` concurrently with this + * call. + */ + static void sort() + { + // Marker is used to signify free elements. + auto marker = reinterpret_cast(1); + + // Extract all the elements and mark them as free. + T* curr = extract(); + T* prev = nullptr; + while (curr != nullptr) + { + prev = curr; + curr = extract(curr); + // Assignment must occur after extract, otherwise extract would read the + // marker + prev->next = marker; + } + + // Build a list of the free elements in the correct order. + // This is the opposite order to the list of all elements + // so that iterate works correctly. + curr = iterate(); + while (curr != nullptr) + { + if (curr->next == marker) + { + get_state().stack.push(curr); + } + curr = iterate(curr); + } + } }; } // namespace snmalloc diff --git a/src/test/func/pool/pool.cc b/src/test/func/pool/pool.cc index c94c793..c721ab2 100644 --- a/src/test/func/pool/pool.cc +++ b/src/test/func/pool/pool.cc @@ -43,6 +43,17 @@ struct PoolLargeEntry : Pooled using PoolLarge = Pool; +template +struct PoolSortEntry : Pooled> +{ + int field; + + PoolSortEntry(int f) : field(f){}; +}; + +template +using PoolSort = Pool, Alloc::Config>; + void test_alloc() { auto ptr = PoolA::acquire(); @@ -147,6 +158,56 @@ void test_large() fflush(stdout); } +/** + * This test confirms that the pool is sorted consistently with + * respect to the iterator after a call to sort. + */ +template +void test_sort() +{ + auto position = [](PoolSortEntry* ptr) { + size_t i = 0; + auto curr = PoolSort::iterate(); + while (ptr != curr) + { + curr = PoolSort::iterate(curr); + ++i; + } + return i; + }; + + // This test checks that `sort` puts the elements in the right order, + // so it is the same as if they had been allocated in that order. + auto a1 = PoolSort::acquire(1); + auto a2 = PoolSort::acquire(1); + + auto position1 = position(a1); + auto position2 = position(a2); + + // Release in either order. + if (order) + { + PoolSort::release(a1); + PoolSort::release(a2); + } + else + { + PoolSort::release(a2); + PoolSort::release(a1); + } + + PoolSort::sort(); + + auto b1 = PoolSort::acquire(1); + auto b2 = PoolSort::acquire(1); + + SNMALLOC_CHECK(position1 == position(b1)); + SNMALLOC_CHECK(position2 == position(b2)); + + PoolSort::release(b1); + PoolSort::release(b2); +} + int main(int argc, char** argv) { setup(); @@ -172,5 +233,9 @@ int main(int argc, char** argv) std::cout << "test_iterator passed" << std::endl; test_large(); std::cout << "test_large passed" << std::endl; + test_sort(); + std::cout << "test_sort passed" << std::endl; + test_sort(); + std::cout << "test_sort passed" << std::endl; return 0; }