**問題:**
- Larson 4T で 100% SEGV (1T は 2.09M ops/s で完走)
- System/mimalloc は 4T で 33.52M ops/s 正常動作
- SS OFF + Remote OFF でも 4T で SEGV
**根本原因: (Task agent ultrathink 調査結果)**
```
CRASH: mov (%r15),%r13
R15 = 0x6261 ← ASCII "ba" (ゴミ値、未初期化TLS)
```
Worker スレッドの TLS 変数が未初期化:
- `__thread void* g_tls_sll_head[TINY_NUM_CLASSES];` ← 初期化なし
- pthread_create() で生成されたスレッドでゼロ初期化されない
- NULL チェックが通過 (0x6261 != NULL) → dereference → SEGV
**修正内容:**
全 TLS 配列に明示的初期化子 `= {0}` を追加:
1. **core/hakmem_tiny.c:**
- `g_tls_sll_head[TINY_NUM_CLASSES] = {0}`
- `g_tls_sll_count[TINY_NUM_CLASSES] = {0}`
- `g_tls_live_ss[TINY_NUM_CLASSES] = {0}`
- `g_tls_bcur[TINY_NUM_CLASSES] = {0}`
- `g_tls_bend[TINY_NUM_CLASSES] = {0}`
2. **core/tiny_fastcache.c:**
- `g_tiny_fast_cache[TINY_FAST_CLASS_COUNT] = {0}`
- `g_tiny_fast_count[TINY_FAST_CLASS_COUNT] = {0}`
- `g_tiny_fast_free_head[TINY_FAST_CLASS_COUNT] = {0}`
- `g_tiny_fast_free_count[TINY_FAST_CLASS_COUNT] = {0}`
3. **core/hakmem_tiny_magazine.c:**
- `g_tls_mags[TINY_NUM_CLASSES] = {0}`
4. **core/tiny_sticky.c:**
- `g_tls_sticky_ss[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}`
- `g_tls_sticky_idx[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}`
- `g_tls_sticky_pos[TINY_NUM_CLASSES] = {0}`
**効果:**
```
Before: 1T: 2.09M ✅ | 4T: SEGV 💀
After: 1T: 2.41M ✅ | 4T: 4.19M ✅ (+15% 1T, SEGV解消)
```
**テスト:**
```bash
# 1 thread: 完走
./larson_hakmem 2 8 128 1024 1 12345 1
→ Throughput = 2,407,597 ops/s ✅
# 4 threads: 完走(以前は SEGV)
./larson_hakmem 2 8 128 1024 1 12345 4
→ Throughput = 4,192,155 ops/s ✅
```
**調査協力:** Task agent (ultrathink mode) による完璧な根本原因特定
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
456 lines
19 KiB
C++
456 lines
19 KiB
C++
#include <inttypes.h>
|
||
#include "tiny_remote.h"
|
||
#include "slab_handle.h"
|
||
#include "tiny_refill.h"
|
||
#include "tiny_tls_guard.h"
|
||
#include "box/free_publish_box.h"
|
||
#include "mid_tcache.h"
|
||
extern __thread void* g_tls_sll_head[TINY_NUM_CLASSES];
|
||
extern __thread uint32_t g_tls_sll_count[TINY_NUM_CLASSES];
|
||
#if !HAKMEM_BUILD_RELEASE
|
||
#include "hakmem_tiny_magazine.h"
|
||
#endif
|
||
extern int g_tiny_force_remote;
|
||
|
||
// ENV: HAKMEM_TINY_DRAIN_TO_SLL (0=off) — adopt/bind境界でfreelist→TLS SLLへN個スプライス
|
||
static inline int tiny_drain_to_sll_budget(void) {
|
||
static int v = -1;
|
||
if (__builtin_expect(v == -1, 0)) {
|
||
const char* s = getenv("HAKMEM_TINY_DRAIN_TO_SLL");
|
||
int parsed = (s && *s) ? atoi(s) : 0;
|
||
if (parsed < 0) parsed = 0; if (parsed > 256) parsed = 256;
|
||
v = parsed;
|
||
}
|
||
return v;
|
||
}
|
||
|
||
static inline void tiny_drain_freelist_to_sll_once(SuperSlab* ss, int slab_idx, int class_idx) {
|
||
int budget = tiny_drain_to_sll_budget();
|
||
if (__builtin_expect(budget <= 0, 1)) return;
|
||
if (!(ss && ss->magic == SUPERSLAB_MAGIC)) return;
|
||
if (slab_idx < 0) return;
|
||
TinySlabMeta* m = &ss->slabs[slab_idx];
|
||
int moved = 0;
|
||
while (m->freelist && moved < budget) {
|
||
void* p = m->freelist;
|
||
m->freelist = *(void**)p;
|
||
*(void**)p = g_tls_sll_head[class_idx];
|
||
g_tls_sll_head[class_idx] = p;
|
||
g_tls_sll_count[class_idx]++;
|
||
moved++;
|
||
}
|
||
}
|
||
|
||
static inline int tiny_remote_queue_contains_guard(SuperSlab* ss, int slab_idx, void* target) {
|
||
if (!ss || slab_idx < 0) return 0;
|
||
uintptr_t cur = atomic_load_explicit(&ss->remote_heads[slab_idx], memory_order_acquire);
|
||
int limit = 8192;
|
||
while (cur && limit-- > 0) {
|
||
if ((void*)cur == target) {
|
||
return 1;
|
||
}
|
||
uintptr_t next;
|
||
if (__builtin_expect(g_remote_side_enable, 0)) {
|
||
next = tiny_remote_side_get(ss, slab_idx, (void*)cur);
|
||
} else {
|
||
next = atomic_load_explicit((_Atomic uintptr_t*)cur, memory_order_relaxed);
|
||
}
|
||
cur = next;
|
||
}
|
||
if (limit <= 0) {
|
||
return 1; // fail-safe: treat unbounded traversal as duplicate
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
// Phase 6.12.1: Free with pre-calculated slab (Option C - avoids duplicate lookup)
|
||
void hak_tiny_free_with_slab(void* ptr, TinySlab* slab) {
|
||
// Phase 7.6: slab == NULL means SuperSlab mode (Magazine integration)
|
||
if (!slab) {
|
||
// SuperSlab path: Get class_idx from SuperSlab
|
||
SuperSlab* ss = hak_super_lookup(ptr);
|
||
if (!ss || ss->magic != SUPERSLAB_MAGIC) return;
|
||
int class_idx = ss->size_class;
|
||
size_t ss_size = (size_t)1ULL << ss->lg_size;
|
||
uintptr_t ss_base = (uintptr_t)ss;
|
||
if (__builtin_expect(class_idx < 0 || class_idx >= TINY_NUM_CLASSES, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_SUPERSLAB_ADOPT_FAIL, (uint16_t)0xFFu, ss, (uintptr_t)ss->size_class);
|
||
return;
|
||
}
|
||
// Optional: cross-lookup TinySlab owner and detect class mismatch early
|
||
if (__builtin_expect(g_tiny_safe_free, 0)) {
|
||
TinySlab* ts = hak_tiny_owner_slab(ptr);
|
||
if (ts) {
|
||
int ts_cls = ts->class_idx;
|
||
if (ts_cls >= 0 && ts_cls < TINY_NUM_CLASSES && ts_cls != class_idx) {
|
||
uint32_t code = 0xAA00u | ((uint32_t)ts_cls & 0xFFu);
|
||
uintptr_t aux = tiny_remote_pack_diag(code, ss_base, ss_size, (uintptr_t)ptr);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)class_idx, ptr, aux);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
}
|
||
}
|
||
}
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_ENTER, (uint16_t)class_idx, ptr, 0);
|
||
// Detect cross-thread: cross-thread free MUST go via superslab path
|
||
int slab_idx = slab_index_for(ss, ptr);
|
||
int ss_cap = ss_slabs_capacity(ss);
|
||
if (__builtin_expect(slab_idx < 0 || slab_idx >= ss_cap, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_SUPERSLAB_ADOPT_FAIL, (uint16_t)0xFEu, ss, (uintptr_t)slab_idx);
|
||
return;
|
||
}
|
||
TinySlabMeta* meta = &ss->slabs[slab_idx];
|
||
if (__builtin_expect(g_tiny_safe_free, 0)) {
|
||
size_t blk = g_tiny_class_sizes[class_idx];
|
||
uint8_t* base = tiny_slab_base_for(ss, slab_idx);
|
||
uintptr_t delta = (uintptr_t)ptr - (uintptr_t)base;
|
||
int cap_ok = (meta->capacity > 0) ? 1 : 0;
|
||
int align_ok = (delta % blk) == 0;
|
||
int range_ok = cap_ok && (delta / blk) < meta->capacity;
|
||
if (!align_ok || !range_ok) {
|
||
uint32_t code = 0xA104u;
|
||
if (align_ok) code |= 0x2u;
|
||
if (range_ok) code |= 0x1u;
|
||
uintptr_t aux = tiny_remote_pack_diag(code, ss_base, ss_size, (uintptr_t)ptr);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)class_idx, ptr, aux);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
return;
|
||
}
|
||
}
|
||
uint32_t self_tid = tiny_self_u32();
|
||
if (__builtin_expect(meta->owner_tid != self_tid, 0)) {
|
||
// route directly to superslab (remote queue / freelist)
|
||
uintptr_t ptr_val = (uintptr_t)ptr;
|
||
uintptr_t ss_base = (uintptr_t)ss;
|
||
size_t ss_size = (size_t)1ULL << ss->lg_size;
|
||
if (__builtin_expect(ptr_val < ss_base || ptr_val >= ss_base + ss_size, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_SUPERSLAB_ADOPT_FAIL, (uint16_t)0xFDu, ss, ptr_val);
|
||
return;
|
||
}
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_REMOTE, (uint16_t)class_idx, ss, (uintptr_t)ptr);
|
||
hak_tiny_free_superslab(ptr, ss);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
|
||
// A/B: Force SS freelist path for same-thread frees (publish on first-free)
|
||
do {
|
||
static int g_free_to_ss2 = -1;
|
||
if (__builtin_expect(g_free_to_ss2 == -1, 0)) {
|
||
const char* e = getenv("HAKMEM_TINY_FREE_TO_SS");
|
||
g_free_to_ss2 = (e && *e && *e != '0') ? 1 : 0; // default OFF
|
||
}
|
||
if (g_free_to_ss2) {
|
||
hak_tiny_free_superslab(ptr, ss);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
} while (0);
|
||
|
||
if (__builtin_expect(g_debug_fast0, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FRONT_BYPASS, (uint16_t)class_idx, ptr, (uintptr_t)slab_idx);
|
||
void* prev = meta->freelist;
|
||
*(void**)ptr = prev;
|
||
meta->freelist = ptr;
|
||
meta->used--;
|
||
ss_active_dec_one(ss);
|
||
if (prev == NULL) {
|
||
ss_partial_publish((int)ss->size_class, ss);
|
||
}
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_LOCAL, (uint16_t)class_idx, ptr, (uintptr_t)slab_idx);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
|
||
if (g_fast_enable && g_fast_cap[class_idx] != 0) {
|
||
if (tiny_fast_push(class_idx, ptr)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_FAST, (uint16_t)class_idx, ptr, slab_idx);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (g_tls_list_enable) {
|
||
TinyTLSList* tls = &g_tls_lists[class_idx];
|
||
uint32_t seq = atomic_load_explicit(&g_tls_param_seq[class_idx], memory_order_relaxed);
|
||
if (__builtin_expect(seq != g_tls_param_seen[class_idx], 0)) {
|
||
tiny_tls_refresh_params(class_idx, tls);
|
||
}
|
||
// TinyHotMag front push(8/16/32B, A/B)
|
||
if (__builtin_expect(g_hotmag_enable && class_idx <= 2, 1)) {
|
||
if (hotmag_push(class_idx, ptr)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_RETURN_MAG, (uint16_t)class_idx, ptr, 1);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
}
|
||
if (tls->count < tls->cap) {
|
||
tiny_tls_list_guard_push(class_idx, tls, ptr);
|
||
tls_list_push(tls, ptr);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_LOCAL, (uint16_t)class_idx, ptr, 0);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
seq = atomic_load_explicit(&g_tls_param_seq[class_idx], memory_order_relaxed);
|
||
if (__builtin_expect(seq != g_tls_param_seen[class_idx], 0)) {
|
||
tiny_tls_refresh_params(class_idx, tls);
|
||
}
|
||
tiny_tls_list_guard_push(class_idx, tls, ptr);
|
||
tls_list_push(tls, ptr);
|
||
if (tls_list_should_spill(tls)) {
|
||
tls_list_spill_excess(class_idx, tls);
|
||
}
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_LOCAL, (uint16_t)class_idx, ptr, 2);
|
||
HAK_STAT_FREE(class_idx);
|
||
return;
|
||
}
|
||
|
||
#include "tiny_free_magazine.inc.h"
|
||
// ============================================================================
|
||
// Phase 6.23: SuperSlab Allocation Helpers
|
||
// ============================================================================
|
||
|
||
// Phase 6.24: Allocate from SuperSlab slab (lazy freelist + linear allocation)
|
||
#include "tiny_superslab_alloc.inc.h"
|
||
#include "tiny_superslab_free.inc.h"
|
||
|
||
void hak_tiny_free(void* ptr) {
|
||
// Track total tiny free calls (diagnostics)
|
||
extern _Atomic uint64_t g_hak_tiny_free_calls;
|
||
atomic_fetch_add_explicit(&g_hak_tiny_free_calls, 1, memory_order_relaxed);
|
||
if (!ptr || !g_tiny_initialized) return;
|
||
|
||
hak_tiny_stats_poll();
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_ENTER, 0, ptr, 0);
|
||
|
||
#ifdef HAKMEM_TINY_BENCH_SLL_ONLY
|
||
// Bench-only SLL-only free: push to TLS SLL for ≤64B when possible
|
||
{
|
||
int class_idx = -1;
|
||
if (g_use_superslab) {
|
||
// FIXED: Use hak_super_lookup() instead of hak_super_lookup() to avoid false positives
|
||
SuperSlab* ss = hak_super_lookup(ptr);
|
||
if (ss && ss->magic == SUPERSLAB_MAGIC) class_idx = ss->size_class;
|
||
}
|
||
if (class_idx < 0) {
|
||
TinySlab* slab = hak_tiny_owner_slab(ptr);
|
||
if (slab) class_idx = slab->class_idx;
|
||
}
|
||
if (class_idx >= 0 && class_idx <= 3) {
|
||
uint32_t sll_cap = sll_cap_for_class(class_idx, (uint32_t)TINY_TLS_MAG_CAP);
|
||
if ((int)g_tls_sll_count[class_idx] < (int)sll_cap) {
|
||
*(void**)ptr = g_tls_sll_head[class_idx];
|
||
g_tls_sll_head[class_idx] = ptr;
|
||
g_tls_sll_count[class_idx]++;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (g_tiny_ultra) {
|
||
int class_idx = -1;
|
||
if (g_use_superslab) {
|
||
// FIXED: Use hak_super_lookup() instead of hak_super_lookup() to avoid false positives
|
||
SuperSlab* ss = hak_super_lookup(ptr);
|
||
if (ss && ss->magic == SUPERSLAB_MAGIC) class_idx = ss->size_class;
|
||
}
|
||
if (class_idx < 0) {
|
||
TinySlab* slab = hak_tiny_owner_slab(ptr);
|
||
if (slab) class_idx = slab->class_idx;
|
||
}
|
||
if (class_idx >= 0) {
|
||
// Ultra free: push directly to TLS SLL without magazine init
|
||
int sll_cap = ultra_sll_cap_for_class(class_idx);
|
||
if ((int)g_tls_sll_count[class_idx] < sll_cap) {
|
||
*(void**)ptr = g_tls_sll_head[class_idx];
|
||
g_tls_sll_head[class_idx] = ptr;
|
||
g_tls_sll_count[class_idx]++;
|
||
return;
|
||
}
|
||
}
|
||
// Fallback to existing path if class resolution fails
|
||
}
|
||
|
||
SuperSlab* fast_ss = NULL;
|
||
TinySlab* fast_slab = NULL;
|
||
int fast_class_idx = -1;
|
||
if (g_use_superslab) {
|
||
fast_ss = hak_super_lookup(ptr);
|
||
if (fast_ss && fast_ss->magic == SUPERSLAB_MAGIC) {
|
||
fast_class_idx = fast_ss->size_class;
|
||
// BUGFIX: Validate size_class before using as array index (prevents OOB = 85% of FREE_TO_SS SEGV)
|
||
if (__builtin_expect(fast_class_idx < 0 || fast_class_idx >= TINY_NUM_CLASSES, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, 0xF0, ptr, (uintptr_t)fast_class_idx);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
fast_ss = NULL;
|
||
fast_class_idx = -1;
|
||
}
|
||
} else {
|
||
fast_ss = NULL;
|
||
}
|
||
}
|
||
if (fast_class_idx < 0) {
|
||
fast_slab = hak_tiny_owner_slab(ptr);
|
||
if (fast_slab) fast_class_idx = fast_slab->class_idx;
|
||
}
|
||
// Safety: detect class mismatch (SS vs TinySlab) early
|
||
if (__builtin_expect(g_tiny_safe_free && fast_class_idx >= 0, 0)) {
|
||
int ss_cls = -1, ts_cls = -1;
|
||
SuperSlab* chk_ss = fast_ss ? fast_ss : (g_use_superslab ? hak_super_lookup(ptr) : NULL);
|
||
if (chk_ss && chk_ss->magic == SUPERSLAB_MAGIC) ss_cls = chk_ss->size_class;
|
||
TinySlab* chk_slab = fast_slab ? fast_slab : hak_tiny_owner_slab(ptr);
|
||
if (chk_slab) ts_cls = chk_slab->class_idx;
|
||
if (ss_cls >= 0 && ts_cls >= 0 && ss_cls != ts_cls) {
|
||
uintptr_t packed = ((uintptr_t)(uint16_t)ss_cls << 16) | (uint16_t)ts_cls;
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)fast_class_idx, ptr, packed);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
}
|
||
}
|
||
if (fast_class_idx >= 0) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_ENTER, (uint16_t)fast_class_idx, ptr, 1);
|
||
}
|
||
if (fast_class_idx >= 0 && g_fast_enable && g_fast_cap[fast_class_idx] != 0) {
|
||
if (tiny_fast_push(fast_class_idx, ptr)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_FREE_FAST, (uint16_t)fast_class_idx, ptr, 0);
|
||
HAK_STAT_FREE(fast_class_idx);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// SuperSlab detection: prefer fast mask-based check when available
|
||
SuperSlab* ss = fast_ss;
|
||
if (!ss && g_use_superslab) {
|
||
ss = hak_super_lookup(ptr);
|
||
if (!(ss && ss->magic == SUPERSLAB_MAGIC)) {
|
||
ss = NULL;
|
||
}
|
||
}
|
||
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
||
// BUGFIX: Validate size_class before using as array index (prevents OOB)
|
||
if (__builtin_expect(ss->size_class < 0 || ss->size_class >= TINY_NUM_CLASSES, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, 0xF2, ptr, (uintptr_t)ss->size_class);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
return;
|
||
}
|
||
// Direct SuperSlab free (avoid second lookup TOCTOU)
|
||
hak_tiny_free_superslab(ptr, ss);
|
||
HAK_STAT_FREE(ss->size_class);
|
||
return;
|
||
}
|
||
|
||
// Fallback to TinySlab only when SuperSlab is not in use
|
||
TinySlab* slab = fast_slab;
|
||
if (!slab) slab = hak_tiny_owner_slab(ptr);
|
||
if (!slab) return; // Not managed by Tiny Pool
|
||
if (__builtin_expect(g_use_superslab, 0)) {
|
||
// In SS mode, a pointer that resolves only to TinySlab is suspicious → treat as invalid free
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, 0xEE, ptr, 0xF1u);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
return;
|
||
}
|
||
|
||
hak_tiny_free_with_slab(ptr, slab);
|
||
}
|
||
|
||
// ============================================================================
|
||
// EXTRACTED TO hakmem_tiny_query.c (Phase 2B-1)
|
||
// ============================================================================
|
||
// EXTRACTED: int hak_tiny_is_managed(void* ptr) {
|
||
// EXTRACTED: if (!ptr || !g_tiny_initialized) return 0;
|
||
// EXTRACTED: // Phase 6.12.1: O(1) slab lookup via registry/list
|
||
// EXTRACTED: return hak_tiny_owner_slab(ptr) != NULL || hak_super_lookup(ptr) != NULL;
|
||
// EXTRACTED: }
|
||
|
||
// Phase 7.6: Check if pointer is managed by Tiny Pool (TinySlab OR SuperSlab)
|
||
// EXTRACTED: int hak_tiny_is_managed_superslab(void* ptr) {
|
||
// EXTRACTED: if (!ptr || !g_tiny_initialized) return 0;
|
||
// EXTRACTED:
|
||
// EXTRACTED: // Safety: Only check if g_use_superslab is enabled
|
||
// EXTRACTED: if (g_use_superslab) {
|
||
// EXTRACTED: SuperSlab* ss = hak_super_lookup(ptr);
|
||
// EXTRACTED: // Phase 8.2 optimization: Use alignment check instead of mincore()
|
||
// EXTRACTED: // SuperSlabs are always SUPERSLAB_SIZE-aligned (2MB)
|
||
// EXTRACTED: if (ss && ((uintptr_t)ss & (SUPERSLAB_SIZE - 1)) == 0) {
|
||
// EXTRACTED: if (ss->magic == SUPERSLAB_MAGIC) {
|
||
// EXTRACTED: return 1; // Valid SuperSlab pointer
|
||
// EXTRACTED: }
|
||
// EXTRACTED: }
|
||
// EXTRACTED: }
|
||
// EXTRACTED:
|
||
// EXTRACTED: // Fallback to TinySlab check
|
||
// EXTRACTED: return hak_tiny_owner_slab(ptr) != NULL;
|
||
// EXTRACTED: }
|
||
|
||
// Return the usable size for a Tiny-managed pointer (0 if unknown/not tiny).
|
||
// Prefer SuperSlab metadata when available; otherwise use TinySlab owner class.
|
||
// EXTRACTED: size_t hak_tiny_usable_size(void* ptr) {
|
||
// EXTRACTED: if (!ptr || !g_tiny_initialized) return 0;
|
||
// EXTRACTED:
|
||
// EXTRACTED: // Check SuperSlab first via registry (safe under direct link and LD)
|
||
// EXTRACTED: if (g_use_superslab) {
|
||
// EXTRACTED: SuperSlab* ss = hak_super_lookup(ptr);
|
||
// EXTRACTED: if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
||
// EXTRACTED: int k = (int)ss->size_class;
|
||
// EXTRACTED: if (k >= 0 && k < TINY_NUM_CLASSES) {
|
||
// EXTRACTED: return g_tiny_class_sizes[k];
|
||
// EXTRACTED: }
|
||
// EXTRACTED: }
|
||
// EXTRACTED: }
|
||
// EXTRACTED:
|
||
// EXTRACTED: // Fallback: TinySlab owner lookup
|
||
// EXTRACTED: TinySlab* slab = hak_tiny_owner_slab(ptr);
|
||
// EXTRACTED: if (slab) {
|
||
// EXTRACTED: int k = slab->class_idx;
|
||
// EXTRACTED: if (k >= 0 && k < TINY_NUM_CLASSES) {
|
||
// EXTRACTED: return g_tiny_class_sizes[k];
|
||
// EXTRACTED: }
|
||
// EXTRACTED: }
|
||
// EXTRACTED: return 0;
|
||
// EXTRACTED: }
|
||
|
||
|
||
// ============================================================================
|
||
// Statistics and Debug Functions - Extracted to hakmem_tiny_stats.c
|
||
// ============================================================================
|
||
// (Phase 2B API headers moved to top of file)
|
||
|
||
|
||
// Optional shutdown hook to stop background components (e.g., Intelligence Engine)
|
||
void hak_tiny_shutdown(void) {
|
||
// Release TLS SuperSlab references (dec refcount) before stopping BG/INT
|
||
for (int k = 0; k < TINY_NUM_CLASSES; k++) {
|
||
TinyTLSSlab* tls = &g_tls_slabs[k];
|
||
if (tls->ss) {
|
||
superslab_ref_dec(tls->ss);
|
||
tls->ss = NULL;
|
||
tls->meta = NULL;
|
||
tls->slab_base = NULL;
|
||
}
|
||
}
|
||
if (g_bg_bin_started) {
|
||
g_bg_bin_stop = 1;
|
||
if (!pthread_equal(tiny_self_pt(), g_bg_bin_thread)) {
|
||
pthread_join(g_bg_bin_thread, NULL);
|
||
}
|
||
g_bg_bin_started = 0;
|
||
g_bg_bin_enable = 0;
|
||
}
|
||
tiny_obs_shutdown();
|
||
if (g_int_engine && g_int_started) {
|
||
g_int_stop = 1;
|
||
// Best-effort join; avoid deadlock if called from within the thread
|
||
if (!pthread_equal(tiny_self_pt(), g_int_thread)) {
|
||
pthread_join(g_int_thread, NULL);
|
||
}
|
||
g_int_started = 0;
|
||
g_int_engine = 0;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// Always-available: Trim empty slabs (release fully-free slabs)
|