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