1 // Copyright 2020 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gtest/gtest.h>
16
17 #include "common/Math.h"
18 #include "common/SlabAllocator.h"
19
20 namespace {
21
22 struct Foo : public PlacementAllocated {
Foo__anona2923b940111::Foo23 Foo(int value) : value(value) {
24 }
25
26 int value;
27 };
28
29 struct alignas(256) AlignedFoo : public Foo {
30 using Foo::Foo;
31 };
32
33 } // namespace
34
35 // Test that a slab allocator of a single object works.
TEST(SlabAllocatorTests,Single)36 TEST(SlabAllocatorTests, Single) {
37 SlabAllocator<Foo> allocator(1 * sizeof(Foo));
38
39 Foo* obj = allocator.Allocate(4);
40 EXPECT_EQ(obj->value, 4);
41
42 allocator.Deallocate(obj);
43 }
44
45 // Allocate multiple objects and check their data is correct.
TEST(SlabAllocatorTests,AllocateSequential)46 TEST(SlabAllocatorTests, AllocateSequential) {
47 // Check small alignment
48 {
49 SlabAllocator<Foo> allocator(5 * sizeof(Foo));
50
51 std::vector<Foo*> objects;
52 for (int i = 0; i < 10; ++i) {
53 auto* ptr = allocator.Allocate(i);
54 EXPECT_TRUE(std::find(objects.begin(), objects.end(), ptr) == objects.end());
55 objects.push_back(ptr);
56 }
57
58 for (int i = 0; i < 10; ++i) {
59 // Check that the value is correct and hasn't been trampled.
60 EXPECT_EQ(objects[i]->value, i);
61
62 // Check that the alignment is correct.
63 EXPECT_TRUE(IsPtrAligned(objects[i], alignof(Foo)));
64 }
65
66 // Deallocate all of the objects.
67 for (Foo* object : objects) {
68 allocator.Deallocate(object);
69 }
70 }
71
72 // Check large alignment
73 {
74 SlabAllocator<AlignedFoo> allocator(9 * sizeof(AlignedFoo));
75
76 std::vector<AlignedFoo*> objects;
77 for (int i = 0; i < 21; ++i) {
78 auto* ptr = allocator.Allocate(i);
79 EXPECT_TRUE(std::find(objects.begin(), objects.end(), ptr) == objects.end());
80 objects.push_back(ptr);
81 }
82
83 for (int i = 0; i < 21; ++i) {
84 // Check that the value is correct and hasn't been trampled.
85 EXPECT_EQ(objects[i]->value, i);
86
87 // Check that the alignment is correct.
88 EXPECT_TRUE(IsPtrAligned(objects[i], 256));
89 }
90
91 // Deallocate all of the objects.
92 for (AlignedFoo* object : objects) {
93 allocator.Deallocate(object);
94 }
95 }
96 }
97
98 // Test that when reallocating a number of objects <= pool size, all memory is reused.
TEST(SlabAllocatorTests,ReusesFreedMemory)99 TEST(SlabAllocatorTests, ReusesFreedMemory) {
100 SlabAllocator<Foo> allocator(17 * sizeof(Foo));
101
102 // Allocate a number of objects.
103 std::set<Foo*> objects;
104 for (int i = 0; i < 17; ++i) {
105 EXPECT_TRUE(objects.insert(allocator.Allocate(i)).second);
106 }
107
108 // Deallocate all of the objects.
109 for (Foo* object : objects) {
110 allocator.Deallocate(object);
111 }
112
113 std::set<Foo*> reallocatedObjects;
114 // Allocate objects again. All of the pointers should be the same.
115 for (int i = 0; i < 17; ++i) {
116 Foo* ptr = allocator.Allocate(i);
117 EXPECT_TRUE(reallocatedObjects.insert(ptr).second);
118 EXPECT_TRUE(std::find(objects.begin(), objects.end(), ptr) != objects.end());
119 }
120
121 // Deallocate all of the objects.
122 for (Foo* object : objects) {
123 allocator.Deallocate(object);
124 }
125 }
126
127 // Test many allocations and deallocations. Meant to catch corner cases with partially
128 // empty slabs.
TEST(SlabAllocatorTests,AllocateDeallocateMany)129 TEST(SlabAllocatorTests, AllocateDeallocateMany) {
130 SlabAllocator<Foo> allocator(17 * sizeof(Foo));
131
132 std::set<Foo*> objects;
133 std::set<Foo*> set3;
134 std::set<Foo*> set7;
135
136 // Allocate many objects.
137 for (uint32_t i = 0; i < 800; ++i) {
138 Foo* object = allocator.Allocate(i);
139 EXPECT_TRUE(objects.insert(object).second);
140
141 if (i % 3 == 0) {
142 set3.insert(object);
143 } else if (i % 7 == 0) {
144 set7.insert(object);
145 }
146 }
147
148 // Deallocate every 3rd object.
149 for (Foo* object : set3) {
150 allocator.Deallocate(object);
151 objects.erase(object);
152 }
153
154 // Allocate many more objects
155 for (uint32_t i = 0; i < 800; ++i) {
156 Foo* object = allocator.Allocate(i);
157 EXPECT_TRUE(objects.insert(object).second);
158
159 if (i % 7 == 0) {
160 set7.insert(object);
161 }
162 }
163
164 // Deallocate every 7th object from the first and second rounds of allocation.
165 for (Foo* object : set7) {
166 allocator.Deallocate(object);
167 objects.erase(object);
168 }
169
170 // Allocate objects again
171 for (uint32_t i = 0; i < 800; ++i) {
172 Foo* object = allocator.Allocate(i);
173 EXPECT_TRUE(objects.insert(object).second);
174 }
175
176 // Deallocate the rest of the objects
177 for (Foo* object : objects) {
178 allocator.Deallocate(object);
179 }
180 }
181