Allow memory pools to have a fallback.

If it runs out of memory, it will try and allocate from its fallback.
Freed memory is not returned to the fallback until it is finalised.

Change-Id: Ib63ae95321d5d70931dedda3c5f9267caec82b2d
diff --git a/src/mpool_test.cc b/src/mpool_test.cc
index f4267b3..7c30f89 100644
--- a/src/mpool_test.cc
+++ b/src/mpool_test.cc
@@ -24,6 +24,7 @@
 
 namespace
 {
+using ::testing::Eq;
 using ::testing::IsNull;
 using ::testing::NotNull;
 
@@ -123,7 +124,7 @@
 {
 	struct mpool p;
 	constexpr size_t entry_size = 16;
-	constexpr size_t entries_per_chunk = 10;
+	constexpr size_t entries_per_chunk = 12;
 	constexpr size_t chunk_count = 10;
 	std::vector<std::unique_ptr<char[]>> chunks;
 	std::vector<uintptr_t> allocs;
@@ -255,7 +256,7 @@
 		ret = mpool_alloc(&p);
 		ASSERT_THAT(ret, NotNull());
 		allocs.push_back((uintptr_t)ret);
-		next = (uintptr_t)ret / entry_size + 1;
+		next = ((uintptr_t)ret / entry_size) + 1;
 	} while ((next % 4) != 2);
 
 	/* Allocate 5 entries with an alignment of 4. So two must be skipped. */
@@ -289,4 +290,71 @@
 		    true);
 }
 
+TEST(mpool, allocation_with_fallback)
+{
+	struct mpool fallback;
+	struct mpool p;
+	constexpr size_t entry_size = 16;
+	constexpr size_t entries_per_chunk = 10;
+	constexpr size_t chunk_count = 10;
+	std::vector<std::unique_ptr<char[]>> chunks;
+	std::vector<uintptr_t> allocs;
+	void* ret;
+
+	mpool_init(&fallback, entry_size);
+	mpool_init_with_fallback(&p, &fallback);
+
+	/* Allocate from an empty pool. */
+	EXPECT_THAT(mpool_alloc(&p), IsNull());
+
+	/* Allocate a number of chunks and add them to the fallback pool. */
+	add_chunks(chunks, &fallback, chunk_count,
+		   entries_per_chunk * entry_size);
+
+	/* Allocate from the pool until we run out of memory. */
+	while ((ret = mpool_alloc(&p))) {
+		allocs.push_back((uintptr_t)ret);
+	}
+
+	/* Check that returned entries are within chunks that were added. */
+	ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
+		    true);
+}
+
+TEST(mpool, free_with_fallback)
+{
+	struct mpool fallback;
+	struct mpool p;
+	constexpr size_t entry_size = 16;
+	constexpr size_t entries_per_chunk = 1;
+	constexpr size_t chunk_count = 1;
+	std::vector<std::unique_ptr<char[]>> chunks;
+	std::vector<uintptr_t> allocs;
+	void* ret;
+
+	mpool_init(&fallback, entry_size);
+	mpool_init_with_fallback(&p, &fallback);
+
+	/* Allocate a number of chunks and add them to the fallback pool. */
+	add_chunks(chunks, &fallback, chunk_count,
+		   entries_per_chunk * entry_size);
+
+	/* Allocate, making use of the fallback and free again. */
+	ret = mpool_alloc(&p);
+	mpool_free(&p, ret);
+
+	/* The entry is not available in the fallback. */
+	EXPECT_THAT(mpool_alloc(&fallback), IsNull());
+
+	/* The entry will be allocated by the local pool. */
+	EXPECT_THAT(mpool_alloc(&p), Eq(ret));
+
+	/* Return the memory to the local pool and then to the fallback. */
+	mpool_free(&p, ret);
+	mpool_fini(&p);
+
+	/* The fallback can now allocate the entry. */
+	EXPECT_THAT(mpool_alloc(&fallback), Eq(ret));
+}
+
 } /* namespace */