|
| 1 | +// The installed wrapper, as a consumer reaches it: each check names what the |
| 2 | +// allocator promises. |
| 3 | +import std; |
| 4 | +import mcpplibs.mwrap.mimalloc; |
| 5 | + |
| 6 | +namespace { |
| 7 | +int failures = 0; |
| 8 | +void check(bool ok, std::string_view what) |
| 9 | +{ |
| 10 | + std::println("{} {}", ok ? "ok " : "FAIL", what); |
| 11 | + if (!ok) ++failures; |
| 12 | +} |
| 13 | +} |
| 14 | + |
| 15 | +int main() |
| 16 | +{ |
| 17 | + namespace m = mcpplibs::mwrap::mimalloc::macros; |
| 18 | + check(mi_version() == m::MI_MALLOC_VERSION && mi_version() == 30601, "the library is mimalloc 3.6.1"); |
| 19 | + |
| 20 | + void* p = mi_malloc(1000); |
| 21 | + std::memset(p, 0x5a, 1000); |
| 22 | + p = mi_realloc(p, 1 << 20); |
| 23 | + check(p && static_cast<unsigned char*>(p)[999] == 0x5a && mi_usable_size(p) >= (1u << 20), |
| 24 | + "a block grows and keeps its bytes"); |
| 25 | + mi_free(p); |
| 26 | + |
| 27 | + for (std::size_t align : {16u, 256u, 4096u, 65536u}) { |
| 28 | + void* a = mi_malloc_aligned(align + 3, align); |
| 29 | + check(a && reinterpret_cast<std::uintptr_t>(a) % align == 0, std::format("a block aligned to {}", align)); |
| 30 | + mi_free(a); |
| 31 | + } |
| 32 | + |
| 33 | + mi_heap_t* heap = mi_heap_new(); |
| 34 | + std::vector<void*> blocks; |
| 35 | + for (int i = 0; i < 2000; ++i) blocks.push_back(mi_heap_malloc(heap, 8 + i % 700)); |
| 36 | + std::size_t seen = 0; |
| 37 | + mi_heap_visit_blocks(heap, true, [](const mi_heap_t*, const mi_heap_area_t*, void* block, std::size_t, void* arg) { |
| 38 | + if (block) ++*static_cast<std::size_t*>(arg); |
| 39 | + return true; |
| 40 | + }, &seen); |
| 41 | + check(seen == blocks.size() && mi_heap_contains(heap, blocks.back()), |
| 42 | + "a private heap holds and visits every block it handed out"); |
| 43 | + mi_heap_destroy(heap); |
| 44 | + |
| 45 | + std::vector<std::string, mi_stl_allocator<std::string>> names; |
| 46 | + for (int i = 0; i < 5000; ++i) names.push_back(std::format("module{}.cppm", i)); |
| 47 | + check(names.size() == 5000 && names[4999] == "module4999.cppm", "a container's elements live in mimalloc"); |
| 48 | + |
| 49 | + const long before = mi_option_get(mi_option_purge_delay); |
| 50 | + mi_option_set(mi_option_purge_delay, before + 5); |
| 51 | + check(mi_option_get(mi_option_purge_delay) == before + 5, "an option is set and read back"); |
| 52 | + mi_option_set(mi_option_purge_delay, before); |
| 53 | + |
| 54 | + // Blocks freed by a thread other than the one that allocated them, and the |
| 55 | + // memory taken again afterwards. |
| 56 | + std::vector<void*> handed(20000); |
| 57 | + std::thread([&] { for (auto& b : handed) b = mi_malloc(48); }).join(); |
| 58 | + const bool all = std::ranges::all_of(handed, [](void* b) { return b != nullptr; }); |
| 59 | + std::thread([&] { for (auto* b : handed) mi_free(b); }).join(); |
| 60 | + mi_collect(true); |
| 61 | + void* again = mi_malloc(48); |
| 62 | + check(all && again != nullptr, "blocks allocated in one thread are freed in another"); |
| 63 | + mi_free(again); |
| 64 | + |
| 65 | + mi_stats_t stats; |
| 66 | + mcpplibs::mwrap::mimalloc::mi_stats_init(&stats); |
| 67 | + check(mi_stats_get(&stats) && stats.size == sizeof stats, "the process's statistics are read"); |
| 68 | + |
| 69 | + std::println("-- failures: {} --", failures); |
| 70 | + return failures == 0 ? 0 : 1; |
| 71 | +} |
0 commit comments