xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/edata_cache.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1 #include "test/jemalloc_test.h"
2 
3 #include "jemalloc/internal/edata_cache.h"
4 
5 static void
6 test_edata_cache_init(edata_cache_t *edata_cache) {
7 	base_t *base = base_new(TSDN_NULL, /* ind */ 1,
8 	    &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
9 	assert_ptr_not_null(base, "");
10 	bool err = edata_cache_init(edata_cache, base);
11 	assert_false(err, "");
12 }
13 
14 static void
15 test_edata_cache_destroy(edata_cache_t *edata_cache) {
16 	base_delete(TSDN_NULL, edata_cache->base);
17 }
18 
19 TEST_BEGIN(test_edata_cache) {
20 	edata_cache_t ec;
21 	test_edata_cache_init(&ec);
22 
23 	/* Get one */
24 	edata_t *ed1 = edata_cache_get(TSDN_NULL, &ec);
25 	assert_ptr_not_null(ed1, "");
26 
27 	/* Cache should be empty */
28 	assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
29 
30 	/* Get another */
31 	edata_t *ed2 = edata_cache_get(TSDN_NULL, &ec);
32 	assert_ptr_not_null(ed2, "");
33 
34 	/* Still empty */
35 	assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
36 
37 	/* Put one back, and the cache should now have one item */
38 	edata_cache_put(TSDN_NULL, &ec, ed1);
39 	assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 1, "");
40 
41 	/* Reallocating should reuse the item, and leave an empty cache. */
42 	edata_t *ed1_again = edata_cache_get(TSDN_NULL, &ec);
43 	assert_ptr_eq(ed1, ed1_again, "");
44 	assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
45 
46 	test_edata_cache_destroy(&ec);
47 }
48 TEST_END
49 
50 static size_t
51 ecf_count(edata_cache_fast_t *ecf) {
52 	size_t count = 0;
53 	edata_t *cur;
54 	ql_foreach(cur, &ecf->list.head, ql_link_inactive) {
55 		count++;
56 	}
57 	return count;
58 }
59 
60 TEST_BEGIN(test_edata_cache_fast_simple) {
61 	edata_cache_t ec;
62 	edata_cache_fast_t ecf;
63 
64 	test_edata_cache_init(&ec);
65 	edata_cache_fast_init(&ecf, &ec);
66 
67 	edata_t *ed1 = edata_cache_fast_get(TSDN_NULL, &ecf);
68 	expect_ptr_not_null(ed1, "");
69 	expect_zu_eq(ecf_count(&ecf), 0, "");
70 	expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
71 
72 	edata_t *ed2 = edata_cache_fast_get(TSDN_NULL, &ecf);
73 	expect_ptr_not_null(ed2, "");
74 	expect_zu_eq(ecf_count(&ecf), 0, "");
75 	expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
76 
77 	edata_cache_fast_put(TSDN_NULL, &ecf, ed1);
78 	expect_zu_eq(ecf_count(&ecf), 1, "");
79 	expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
80 
81 	edata_cache_fast_put(TSDN_NULL, &ecf, ed2);
82 	expect_zu_eq(ecf_count(&ecf), 2, "");
83 	expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
84 
85 	/* LIFO ordering. */
86 	expect_ptr_eq(ed2, edata_cache_fast_get(TSDN_NULL, &ecf), "");
87 	expect_zu_eq(ecf_count(&ecf), 1, "");
88 	expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
89 
90 	expect_ptr_eq(ed1, edata_cache_fast_get(TSDN_NULL, &ecf), "");
91 	expect_zu_eq(ecf_count(&ecf), 0, "");
92 	expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
93 
94 	test_edata_cache_destroy(&ec);
95 }
96 TEST_END
97 
98 TEST_BEGIN(test_edata_cache_fill) {
99 	edata_cache_t ec;
100 	edata_cache_fast_t ecf;
101 
102 	test_edata_cache_init(&ec);
103 	edata_cache_fast_init(&ecf, &ec);
104 
105 	edata_t *allocs[EDATA_CACHE_FAST_FILL * 2];
106 
107 	/*
108 	 * If the fallback cache can't satisfy the request, we shouldn't do
109 	 * extra allocations until compelled to.  Put half the fill goal in the
110 	 * fallback.
111 	 */
112 	for (int i = 0; i < EDATA_CACHE_FAST_FILL / 2; i++) {
113 		allocs[i] = edata_cache_get(TSDN_NULL, &ec);
114 	}
115 	for (int i = 0; i < EDATA_CACHE_FAST_FILL / 2; i++) {
116 		edata_cache_put(TSDN_NULL, &ec, allocs[i]);
117 	}
118 	expect_zu_eq(EDATA_CACHE_FAST_FILL / 2,
119 	    atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
120 
121 	allocs[0] = edata_cache_fast_get(TSDN_NULL, &ecf);
122 	expect_zu_eq(EDATA_CACHE_FAST_FILL / 2 - 1, ecf_count(&ecf),
123 	    "Should have grabbed all edatas available but no more.");
124 
125 	for (int i = 1; i < EDATA_CACHE_FAST_FILL / 2; i++) {
126 		allocs[i] = edata_cache_fast_get(TSDN_NULL, &ecf);
127 		expect_ptr_not_null(allocs[i], "");
128 	}
129 	expect_zu_eq(0, ecf_count(&ecf), "");
130 
131 	/* When forced, we should alloc from the base. */
132 	edata_t *edata = edata_cache_fast_get(TSDN_NULL, &ecf);
133 	expect_ptr_not_null(edata, "");
134 	expect_zu_eq(0, ecf_count(&ecf), "Allocated more than necessary");
135 	expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED),
136 	    "Allocated more than necessary");
137 
138 	/*
139 	 * We should correctly fill in the common case where the fallback isn't
140 	 * exhausted, too.
141 	 */
142 	for (int i = 0; i < EDATA_CACHE_FAST_FILL * 2; i++) {
143 		allocs[i] = edata_cache_get(TSDN_NULL, &ec);
144 		expect_ptr_not_null(allocs[i], "");
145 	}
146 	for (int i = 0; i < EDATA_CACHE_FAST_FILL * 2; i++) {
147 		edata_cache_put(TSDN_NULL, &ec, allocs[i]);
148 	}
149 
150 	allocs[0] = edata_cache_fast_get(TSDN_NULL, &ecf);
151 	expect_zu_eq(EDATA_CACHE_FAST_FILL - 1, ecf_count(&ecf), "");
152 	expect_zu_eq(EDATA_CACHE_FAST_FILL,
153 	    atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
154 	for (int i = 1; i < EDATA_CACHE_FAST_FILL; i++) {
155 		expect_zu_eq(EDATA_CACHE_FAST_FILL - i, ecf_count(&ecf), "");
156 		expect_zu_eq(EDATA_CACHE_FAST_FILL,
157 		    atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
158 		allocs[i] = edata_cache_fast_get(TSDN_NULL, &ecf);
159 		expect_ptr_not_null(allocs[i], "");
160 	}
161 	expect_zu_eq(0, ecf_count(&ecf), "");
162 	expect_zu_eq(EDATA_CACHE_FAST_FILL,
163 	    atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
164 
165 	allocs[0] = edata_cache_fast_get(TSDN_NULL, &ecf);
166 	expect_zu_eq(EDATA_CACHE_FAST_FILL - 1, ecf_count(&ecf), "");
167 	expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
168 	for (int i = 1; i < EDATA_CACHE_FAST_FILL; i++) {
169 		expect_zu_eq(EDATA_CACHE_FAST_FILL - i, ecf_count(&ecf), "");
170 		expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
171 		allocs[i] = edata_cache_fast_get(TSDN_NULL, &ecf);
172 		expect_ptr_not_null(allocs[i], "");
173 	}
174 	expect_zu_eq(0, ecf_count(&ecf), "");
175 	expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
176 
177 	test_edata_cache_destroy(&ec);
178 }
179 TEST_END
180 
181 TEST_BEGIN(test_edata_cache_disable) {
182 	edata_cache_t ec;
183 	edata_cache_fast_t ecf;
184 
185 	test_edata_cache_init(&ec);
186 	edata_cache_fast_init(&ecf, &ec);
187 
188 	for (int i = 0; i < EDATA_CACHE_FAST_FILL; i++) {
189 		edata_t *edata = edata_cache_get(TSDN_NULL, &ec);
190 		expect_ptr_not_null(edata, "");
191 		edata_cache_fast_put(TSDN_NULL, &ecf, edata);
192 	}
193 
194 	expect_zu_eq(EDATA_CACHE_FAST_FILL, ecf_count(&ecf), "");
195 	expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
196 
197 	edata_cache_fast_disable(TSDN_NULL, &ecf);
198 
199 	expect_zu_eq(0, ecf_count(&ecf), "");
200 	expect_zu_eq(EDATA_CACHE_FAST_FILL,
201 	    atomic_load_zu(&ec.count, ATOMIC_RELAXED), "Disabling should flush");
202 
203 	edata_t *edata = edata_cache_fast_get(TSDN_NULL, &ecf);
204 	expect_zu_eq(0, ecf_count(&ecf), "");
205 	expect_zu_eq(EDATA_CACHE_FAST_FILL - 1,
206 	    atomic_load_zu(&ec.count, ATOMIC_RELAXED),
207 	    "Disabled ecf should forward on get");
208 
209 	edata_cache_fast_put(TSDN_NULL, &ecf, edata);
210 	expect_zu_eq(0, ecf_count(&ecf), "");
211 	expect_zu_eq(EDATA_CACHE_FAST_FILL,
212 	    atomic_load_zu(&ec.count, ATOMIC_RELAXED),
213 	    "Disabled ecf should forward on put");
214 
215 	test_edata_cache_destroy(&ec);
216 }
217 TEST_END
218 
219 int
220 main(void) {
221 	return test(
222 	    test_edata_cache,
223 	    test_edata_cache_fast_simple,
224 	    test_edata_cache_fill,
225 	    test_edata_cache_disable);
226 }
227