fix(memhub): 非 2 的幂 arena 二进制分解 + reset 重建 + OOM 诊断增强

主池 1.2GB(非 2 幂)不再被 _largest_pow2_le 砍成 1GB:整个区域按二进制分解拆成多个 2 的幂空闲块压入对应 free list,buddy 合并跨分解边界因 order 不匹配自然停止;reset/_rebuild_free_lists 重建同样分解格局。新增 big_alloc_count 计数与 alloc 失败 self_check/free_bytes 诊断、mark 异常 fail-fast。BuddyTest 断言改为初始分解块数。
This commit is contained in:
TermiNexus committed 2026-08-15 06:24:13 +08:00
1 parent 31d7602368
commit 46b7cbc4ba
2 files changed
+106 -44

No files matched your search

+29 -28
View File
@@ -57,9 +57,10 @@ def mbuddy_main() -> CInt:
bd.free(p3)
testcheck.ok("3 frees OK")
# 释放后验证合并: free_count 应回到 1 (单个大块)
testcheck.check(bd.free_count() == 1,
"coalesce: free_count=1 OK", "coalesce: free_count != 1")
# 释放后验证合并: free_count 应回到初始分解块数 (10)
# 4096-264 内非 2 的幂: 65536-264=65272 二进制分解为 10 个 2 的幂块
testcheck.check(bd.free_count() == 10,
"coalesce: free_count=10 OK", "coalesce: free_count != 10")
# 合并后应能分配大块
big: bytes = bd.alloc(16000)
@@ -152,7 +153,7 @@ def mbuddy_main() -> CInt:
testcheck.check(sp_c[0] == CUInt8T(0x42), "shrink data OK", "shrink data FAILED")
bds.free(sp2)
testcheck.check(bds.free_count() == 1, "shrink+free coalesce OK", "shrink+free coalesce FAILED")
testcheck.check(bds.free_count() == 6, "shrink+free coalesce OK", "shrink+free coalesce FAILED")
stdlib.free(arena_shrink)
@@ -263,9 +264,9 @@ def mbuddy_main() -> CInt:
bd6.free(cur_p)
idx += 1
# 全部释放后应能合并回单个大块
testcheck.check(bd6.free_count() == 1,
"OOM: full coalesce OK", "OOM: free_count != 1 after full free")
# 全部释放后应能合并回初始分解块数 (6)
testcheck.check(bd6.free_count() == 6,
"OOM: full coalesce OK", "OOM: free_count != 6 after full free")
stdlib.free(ptrs_arr)
stdlib.free(arena6)
@@ -275,8 +276,8 @@ def mbuddy_main() -> CInt:
arena7: bytes = stdlib.malloc(4096)
bd7: memhub.MemBuddy | CPtr = memhub.MemBuddy(arena7, 4096)
# 初始: 1 个大块
testcheck.check(bd7.free_count() == 1, "init: 1 free block OK", "init: free_count != 1")
# 初始: 非 2 的幂 arena (4096-264=3832) 二进制分解为 6 个块
testcheck.check(bd7.free_count() == 6, "init: 6 free blocks OK", "init: free_count != 6")
# 分配后: 空闲块数应增加 (分裂产生多个小块)
a1: bytes = bd7.alloc(32)
@@ -295,10 +296,10 @@ def mbuddy_main() -> CInt:
fc3: CSizeT = bd7.free_count()
testcheck.check(fc3 > fc2, "after free a1: free increased OK", "after free a1: free not increased")
# 释放 a2: 应合并, 空闲块数回到 1
# 释放 a2: 应合并, 空闲块数回到初始 6 块
bd7.free(a2)
testcheck.check(bd7.free_count() == 1,
"after free all: coalesced to 1 OK", "after free all: free_count != 1")
testcheck.check(bd7.free_count() == 6,
"after free all: coalesced to 6 OK", "after free all: free_count != 6")
stdlib.free(arena7)
@@ -398,7 +399,7 @@ def mbuddy_main() -> CInt:
# 验证分配器状态: 自检通过,所有块已合并
testcheck.check(bd_mt.self_check() == 0, "mt: self_check OK", "mt: self_check FAILED")
testcheck.check(bd_mt.free_count() == 1, "mt: free_count=1 OK", "mt: free_count != 1 (leaked)")
testcheck.check(bd_mt.free_count() == 10, "mt: free_count=10 OK", "mt: free_count != 10 (leaked)")
viperlib.snprintf(buf, 256, "mt: free_count=%lu", bd_mt.free_count())
testcheck.info(buf)
@@ -423,22 +424,22 @@ def mbuddy_main() -> CInt:
bd_ex.free(exact_fit)
bd_ex.free(over_fit)
testcheck.check(bd_ex.free_count() == 1, "boundary: coalesce OK", "boundary: coalesce FAILED")
testcheck.check(bd_ex.free_count() == 6, "boundary: coalesce OK", "boundary: coalesce FAILED")
# 2. 单次分配接近 arena 上限
# 可用数据区 = mem_size - 8(头) - 8(哨兵),alloc(max_usable) 的 needed
# 恰好 = mem_size,正好填满最大块(order 6 = 2048)
max_usable: CSizeT = bd_ex.mem_size - 16
viperlib.snprintf(buf, 256, "mem_size=%lu, max_usable=%lu", bd_ex.mem_size, max_usable)
# 2. 单次分配接近最大块上限(order6=2048,非 2 的幂 arena 的最大分解块)
# 可用数据区 = 2048 - 8(头) - 8(哨兵),alloc(max_usable) 的 needed 恰好
# = 2048,正好填满 order6 最大块
max_usable: CSizeT = 2048 - 16
viperlib.snprintf(buf, 256, "mem_size=%lu, max_block=2048, max_usable=%lu", bd_ex.mem_size, max_usable)
testcheck.info(buf)
# 恰好用完整个 arena
# 恰好用完 order6 块(其余 5 个分解块空闲)
exact_max: bytes = bd_ex.alloc(max_usable)
testcheck.check(exact_max != None, "exact max alloc OK", "exact max alloc FAILED")
testcheck.check(bd_ex.free_count() == 0, "max alloc: arena full OK", "max alloc: arena not full")
testcheck.check(bd_ex.free_count() == 5, "max alloc: 5 free blocks left OK", "max alloc: free_count != 5")
bd_ex.free(exact_max)
testcheck.check(bd_ex.free_count() == 1, "max free: coalesce OK", "max free: coalesce FAILED")
testcheck.check(bd_ex.free_count() == 6, "max free: coalesce OK", "max free: coalesce FAILED")
# 再次分配同样大小 — 验证合并后可再次使用
exact_max2: bytes = bd_ex.alloc(max_usable)
@@ -481,14 +482,14 @@ def mbuddy_main() -> CInt:
# 6. 2 的幂 arena vs 非 2 的幂 arena
arena_pow2: bytes = stdlib.malloc(512)
bd_pow2: memhub.MemBuddy | CPtr = memhub.MemBuddy(arena_pow2, 512)
# 512 - 264 = 248, largest_pow2(248) = 128
testcheck.check(bd_pow2.stats() == 128, "pow2 arena: stats=128 OK", "pow2 arena: stats != 128")
# 512 - 264 = 248, 二进制分解 = 128+64+32 = 224 (尾 24 字节碎片不入池)
testcheck.check(bd_pow2.stats() == 224, "pow2 arena: stats=224 OK", "pow2 arena: stats != 224")
stdlib.free(arena_pow2)
arena_non2: bytes = stdlib.malloc(500)
bd_non2: memhub.MemBuddy | CPtr = memhub.MemBuddy(arena_non2, 500)
# 500 - 264 = 236, largest_pow2(236) = 128
testcheck.check(bd_non2.stats() == 128, "non-pow2 arena: stats=128 OK", "non-pow2 arena: stats != 128")
# 500 - 264 = 236, 二进制分解 = 128+64+32 = 224 (尾 12 字节碎片不入池)
testcheck.check(bd_non2.stats() == 224, "non-pow2 arena: stats=224 OK", "non-pow2 arena: stats != 224")
stdlib.free(arena_non2)
# 7. 压力测试: 反复 alloc/free 同一尺寸 1000 次
@@ -504,7 +505,7 @@ def mbuddy_main() -> CInt:
bd_stress.free(sp)
testcheck.check(bd_stress.self_check() == 0, "stress: self_check OK", "stress: self_check FAILED")
testcheck.check(bd_stress.free_count() == 1, "stress: free_count=1 OK", "stress: leaked")
testcheck.check(bd_stress.free_count() == 6, "stress: free_count=6 OK", "stress: leaked")
stdlib.free(arena_stress)
# 8. 分配全部最小块然后全部释放
@@ -534,7 +535,7 @@ def mbuddy_main() -> CInt:
fp2: bytes = t.CVoid(fill_ptrs[fi], t.CPtr)
bd_fill.free(fp2)
testcheck.check(bd_fill.free_count() == 1, "fill: all freed OK", "fill: leaked after free all")
testcheck.check(bd_fill.free_count() == 6, "fill: all freed OK", "fill: leaked after free all")
testcheck.check(bd_fill.self_check() == 0, "fill: self_check after free OK", "fill: self_check FAILED")
stdlib.free(fill_ptrs_arr)