Phase 6-2.8: SuperSlab modular refactoring (665 lines → 104 lines)

目的: hakmem_tiny_superslab.h の肥大化を解消 (500+ 行)

実装内容:
1. superslab_types.h を作成
   - SuperSlab 構造体定義 (TinySlabMeta, SuperSlab)
   - 設定定数 (SUPERSLAB_SIZE_MAX, TINY_NUM_CLASSES_SS)
   - コンパイル時アサーション

2. superslab_inline.h を作成
   - ホットパス用インライン関数を集約
   - ss_slabs_capacity(), slab_index_for()
   - tiny_slab_base_for(), ss_remote_push()
   - _ss_remote_drain_to_freelist_unsafe()
   - Fail-fast validation helpers
   - ACE helpers (hak_now_ns, hak_tiny_superslab_next_lg)

3. hakmem_tiny_superslab.h をリファクタリング
   - 665 行 → 104 行 (-84%)
   - include のみに書き換え
   - 関数宣言と extern 宣言のみ残す

効果:
 ビルド成功 (libhakmem.so, larson_hakmem)
 Mid-Large allocator テスト通過 (3.98M ops/s)
⚠️ Tiny allocator の freelist corruption バグは未解決 (リファクタリングのスコープ外)

注意:
- Phase 6-2.6/6-2.7 の freelist バグは依然として存在
- リファクタリングは保守性向上のみが目的
- バグ修正は次のフェーズで対応

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-11-07 23:05:33 +09:00
parent 3523e02e51
commit a430545820
3 changed files with 611 additions and 582 deletions

View File

@ -2,6 +2,7 @@
// Purpose: mimalloc-inspired 2MB aligned slab allocation for fast pointer→slab lookup
// License: MIT
// Date: 2025-10-24
// Phase 6-2.8: Refactored into modular headers (types, inline)
#ifndef HAKMEM_TINY_SUPERSLAB_H
#define HAKMEM_TINY_SUPERSLAB_H
@ -15,6 +16,12 @@
#include <signal.h>
#include <stdio.h> // For fprintf() debugging
#include <pthread.h>
// Phase 6-2.8: Modular headers (types, inline functions)
#include "superslab/superslab_types.h"
#include "superslab/superslab_inline.h"
// Legacy includes (for backward compatibility)
#include "tiny_debug_ring.h"
#include "tiny_remote.h"
#include "hakmem_tiny_superslab_constants.h" // Phase 6-2.5: Centralized layout constants
@ -22,264 +29,9 @@
// Debug instrumentation flags (defined in hakmem_tiny.c)
extern int g_debug_remote_guard;
extern int g_tiny_safe_free_strict;
uint32_t tiny_remote_drain_threshold(void);
// ============================================================================
// SuperSlab Configuration
// ============================================================================
// Phase 8.3: ACE - Variable SuperSlab size (1MB ↔ 2MB)
#define SUPERSLAB_SIZE_MAX (2 * 1024 * 1024) // 2MB max size
#define SUPERSLAB_SIZE_MIN (1 * 1024 * 1024) // 1MB min size
#define SUPERSLAB_LG_MAX 21 // lg(2MB)
#define SUPERSLAB_LG_MIN 20 // lg(1MB)
#define SUPERSLAB_LG_DEFAULT 21 // Default: 2MB (syscall reduction, ACE will adapt)
// Phase 6-2.5: SLAB_SIZE now defined in hakmem_tiny_superslab_constants.h
// #define SLAB_SIZE (64 * 1024) // 64KB per slab (fixed)
// Legacy defines (kept for backward compatibility, use lg_size instead)
#define SUPERSLAB_SIZE SUPERSLAB_SIZE_MAX // Default to 2MB (syscall reduction)
#define SUPERSLAB_MASK (SUPERSLAB_SIZE - 1)
// IMPORTANT: Support variable-size SuperSlab (1MB=16 slabs, 2MB=32 slabs)
// Arrays below must be sized for the MAX to avoid OOB when lg_size=21 (2MB)
#define SLABS_PER_SUPERSLAB_MIN (SUPERSLAB_SIZE_MIN / SLAB_SIZE) // 16 for 1MB
#define SLABS_PER_SUPERSLAB_MAX (SUPERSLAB_SIZE_MAX / SLAB_SIZE) // 32 for 2MB
// Magic number for validation
#define SUPERSLAB_MAGIC 0x48414B4D454D5353ULL // "HAKMEMSS"
// ============================================================================
// SuperSlab Metadata Structure
// ============================================================================
// Per-slab metadata (16 bytes)
typedef struct TinySlabMeta {
void* freelist; // Freelist head (NULL = linear mode, Phase 6.24)
uint16_t used; // Blocks currently used
uint16_t capacity; // Total blocks in slab
uint32_t owner_tid; // Owner thread ID (for same-thread fast path)
// Phase 6.24: freelist == NULL → linear allocation mode (lazy init)
// Linear mode: allocate sequentially without building freelist
// Freelist mode: use freelist after first free() call
} TinySlabMeta;
// SuperSlab header (cache-line aligned, 64B)
typedef struct SuperSlab {
// Header fields (64B total)
uint64_t magic; // Magic number (0xHAKMEM_SUPERSLAB)
uint8_t size_class; // Size class (0-7 for 8-64B)
uint8_t active_slabs; // Number of active slabs (0-32 for 2MB, 0-16 for 1MB)
uint8_t lg_size; // Phase 8.3: ACE - SuperSlab size (20=1MB, 21=2MB)
uint8_t _pad0; // Padding
uint32_t slab_bitmap; // 32-bit bitmap (1=active, 0=free)
_Atomic uint32_t freelist_mask; // Bit i=1 when slab i freelist is non-empty (opt-in)
// Phase 6-2.1: ChatGPT Pro P0 optimization - O(1) non-empty slab lookup
uint32_t nonempty_mask; // Bit i = 1 if slabs[i].freelist != NULL (O(1) lookup via ctz)
// Phase 7.6: Deallocation support
atomic_uint total_active_blocks; // Total blocks in use (all slabs combined)
atomic_uint refcount; // MT-safe refcount for empty detection/free将来利用
atomic_uint listed; // 0/1: published to partial adopt ringpublish gating
uint32_t partial_epoch; // Last partial madvise epoch (optional)
uint8_t publish_hint; // Best slab index hint for adopt (0..31), 0xFF=none
uint8_t _pad1[3]; // Padding
// Per-slab metadata (16B each)
// Sized for MAX; use ss->lg_size to bound loops at runtime
TinySlabMeta slabs[SLABS_PER_SUPERSLAB_MAX];
// Remote free queues (per slab): MPSC stack heads + counts
_Atomic(uintptr_t) remote_heads[SLABS_PER_SUPERSLAB_MAX];
_Atomic(uint32_t) remote_counts[SLABS_PER_SUPERSLAB_MAX];
// Per-slab publish state: 0/1 = not listed/listed (for slab-granular republish hints)
atomic_uint slab_listed[SLABS_PER_SUPERSLAB_MAX];
// Partial adopt overflow linkage (single-linked, best-effort)
struct SuperSlab* partial_next;
// Padding to fill remaining space (2MB - 64B - 512B)
// Note: Actual slab data starts at offset SLAB_SIZE (64KB)
} __attribute__((aligned(64))) SuperSlab;
static inline int ss_slabs_capacity(const SuperSlab* ss);
static inline int tiny_refill_failfast_level(void) {
static int g_failfast_level = -1;
if (__builtin_expect(g_failfast_level == -1, 0)) {
const char* env = getenv("HAKMEM_TINY_REFILL_FAILFAST");
if (env && *env) {
g_failfast_level = atoi(env);
} else {
g_failfast_level = 1;
}
}
return g_failfast_level;
}
static inline void tiny_failfast_log(const char* stage,
int class_idx,
SuperSlab* ss,
TinySlabMeta* meta,
const void* node,
const void* next) {
if (__builtin_expect(tiny_refill_failfast_level() < 2, 1)) return;
uintptr_t base = ss ? (uintptr_t)ss : 0;
size_t size = ss ? ((size_t)1ULL << ss->lg_size) : 0;
uintptr_t limit = base + size;
fprintf(stderr,
"[TRC_FREELIST_LOG] stage=%s cls=%d node=%p next=%p head=%p base=%p limit=%p\n",
stage ? stage : "(null)",
class_idx,
node,
next,
meta ? meta->freelist : NULL,
(void*)base,
(void*)limit);
fflush(stderr);
}
static inline void tiny_failfast_abort_ptr(const char* stage,
SuperSlab* ss,
int slab_idx,
const void* ptr,
const char* reason) {
if (__builtin_expect(tiny_refill_failfast_level() < 2, 1)) return;
uintptr_t base = ss ? (uintptr_t)ss : 0;
size_t size = ss ? ((size_t)1ULL << ss->lg_size) : 0;
uintptr_t limit = base + size;
size_t cap = 0;
uint32_t used = 0;
if (ss && slab_idx >= 0 && slab_idx < ss_slabs_capacity(ss)) {
cap = ss->slabs[slab_idx].capacity;
used = ss->slabs[slab_idx].used;
}
size_t offset = 0;
if (ptr && base && ptr >= (void*)base) {
offset = (size_t)((uintptr_t)ptr - base);
}
fprintf(stderr,
"[TRC_FAILFAST_PTR] stage=%s cls=%d slab_idx=%d ptr=%p reason=%s base=%p limit=%p cap=%zu used=%u offset=%zu\n",
stage ? stage : "(null)",
ss ? (int)ss->size_class : -1,
slab_idx,
ptr,
reason ? reason : "(null)",
(void*)base,
(void*)limit,
cap,
used,
offset);
fflush(stderr);
abort();
}
// Compile-time assertions
_Static_assert(sizeof(TinySlabMeta) == 16, "TinySlabMeta must be 16 bytes");
// Phase 8.3: Variable-size SuperSlab assertions (1MB=16 slabs, 2MB=32 slabs)
_Static_assert((SUPERSLAB_SIZE_MIN / SLAB_SIZE) == 16, "1MB SuperSlab must have 16 slabs");
_Static_assert((SUPERSLAB_SIZE_MAX / SLAB_SIZE) == 32, "2MB SuperSlab must have 32 slabs");
_Static_assert((SUPERSLAB_SIZE & SUPERSLAB_MASK) == 0, "SUPERSLAB_SIZE must be power of 2");
// ============================================================================
// Fast Inline Functions (mimalloc-style)
// ============================================================================
// DEPRECATED (Phase 1): This function causes false positives! Use hak_super_lookup() instead.
// Problem: L2.5 allocations at 1MB boundary are misidentified as SuperSlabs
// Solution: Use registry-based hak_super_lookup() from hakmem_super_registry.h
#if 0 // DISABLED - unsafe function removed in Phase 1
static inline SuperSlab* ptr_to_superslab(void* p) {
return (SuperSlab*)((uintptr_t)p & ~(uintptr_t)SUPERSLAB_MASK);
}
#endif
// Get slab index within SuperSlab (shift operation, 0-31)
// Deprecated: Do not use for 2MB SuperSlabs (mask is 1MB). Use slab_index_for().
static inline int ptr_to_slab_index(void* p) {
uintptr_t offset = (uintptr_t)p & SUPERSLAB_MASK;
return (int)(offset >> 16); // Divide by 64KB (2^16)
}
// Runtime-safe slab count for a given SuperSlab
static inline int ss_slabs_capacity(const SuperSlab* ss) {
size_t ss_size = (size_t)1 << ss->lg_size;
return (int)(ss_size / SLAB_SIZE); // 16 or 32
}
// Safe slab index computation using SuperSlab base (supports 1MB/2MB)
static inline int slab_index_for(const SuperSlab* ss, const void* p) {
uintptr_t base = (uintptr_t)ss;
uintptr_t addr = (uintptr_t)p;
uintptr_t off = addr - base;
int idx = (int)(off >> 16); // 64KB
int cap = ss_slabs_capacity(ss);
return (idx >= 0 && idx < cap) ? idx : -1;
}
// DEPRECATED (Phase 1): Uses unsafe ptr_to_superslab() internally
// Use hak_super_lookup() + ptr_to_slab_index() instead
#if 0 // DISABLED - uses unsafe ptr_to_superslab()
static inline TinySlabMeta* ptr_to_slab_meta(void* p) {
SuperSlab* ss = ptr_to_superslab(p);
int idx = ptr_to_slab_index(p);
return &ss->slabs[idx];
}
#endif
// Get slab data start address
static inline void* slab_data_start(SuperSlab* ss, int slab_idx) {
return (char*)ss + (slab_idx * SLAB_SIZE);
}
static inline uint8_t* tiny_slab_base_for(SuperSlab* ss, int slab_idx) {
uint8_t* base = (uint8_t*)slab_data_start(ss, slab_idx);
// Phase 6-2.5 FIX: Use SUPERSLAB_SLAB0_DATA_OFFSET constant
// sizeof(SuperSlab)=1088, aligned to next 1024-boundary=2048
// This ensures proper alignment for class 7 (1024-byte blocks)
if (slab_idx == 0) base += SUPERSLAB_SLAB0_DATA_OFFSET;
return base;
}
// DEPRECATED (Phase 1): Uses unsafe ptr_to_superslab() internally (false positives!)
// Use: SuperSlab* ss = hak_super_lookup(p); if (ss && ss->magic == SUPERSLAB_MAGIC) { ... }
#if 0 // DISABLED - uses unsafe ptr_to_superslab(), causes crashes on L2.5 boundaries
static inline int is_superslab_pointer(void* p) {
SuperSlab* ss = ptr_to_superslab(p);
return ss->magic == SUPERSLAB_MAGIC;
}
#endif
// Refcount helpers将来のMT安全な空回収に使用
static inline void superslab_ref_inc(SuperSlab* ss) {
atomic_fetch_add_explicit(&ss->refcount, 1u, memory_order_relaxed);
}
static inline unsigned superslab_ref_dec(SuperSlab* ss) {
return atomic_fetch_sub_explicit(&ss->refcount, 1u, memory_order_acq_rel) - 1u;
}
static inline unsigned superslab_ref_get(SuperSlab* ss) {
return atomic_load_explicit(&ss->refcount, memory_order_acquire);
}
// Debug counter extern declaration
extern _Atomic uint64_t g_ss_active_dec_calls;
// Active block counter helpers (saturating decrement for free operations)
static inline void ss_active_dec_one(SuperSlab* ss) {
atomic_fetch_add_explicit(&g_ss_active_dec_calls, 1, memory_order_relaxed);
uint32_t old = atomic_load_explicit(&ss->total_active_blocks, memory_order_relaxed);
while (old != 0) {
if (atomic_compare_exchange_weak_explicit(&ss->total_active_blocks, &old, old - 1u,
memory_order_relaxed, memory_order_relaxed)) {
break;
}
// CAS failed: old is reloaded by CAS intrinsic
}
}
uint32_t tiny_remote_drain_threshold(void);
// ============================================================================
// SuperSlab Management Functions
@ -313,23 +65,6 @@ void superslab_ace_print_stats(void);
// Phase 8.3: ACE (Adaptive Cache Engine) - SuperSlab adaptive sizing
// ============================================================================
#define TINY_NUM_CLASSES_SS 8 // Same as TINY_NUM_CLASSES (avoid circular include)
// Per-class ACE state (lightweight observation + decision)
typedef struct {
uint8_t current_lg; // Current lg_size in use (20=1MB, 21=2MB)
uint8_t target_lg; // Target lg_size for next allocation (20/21)
uint16_t hot_score; // Hotness score (0-1000) for visualization
uint32_t alloc_count; // Allocs since last tick
uint32_t refill_count; // Refills since last tick
uint32_t spill_count; // Spills since last tick
uint32_t live_blocks; // Estimated live blocks (alloc-free EMA)
uint64_t last_tick_ns; // Last tick timestamp (ns)
} SuperSlabACEState;
// Global ACE state (one per tiny class)
extern SuperSlabACEState g_ss_ace[TINY_NUM_CLASSES_SS];
// ACE tick function (called periodically, ~150ms interval)
// Observes metrics and decides promotion (1MB→2MB) or demotion (2MB→1MB)
void hak_tiny_superslab_ace_tick(int class_idx, uint64_t now_ns);
@ -337,31 +72,20 @@ void hak_tiny_superslab_ace_tick(int class_idx, uint64_t now_ns);
// Phase 8.4: ACE Observer (called from Learner thread - zero hot-path overhead)
void hak_tiny_superslab_ace_observe_all(void);
// Low-cost timestamp (nanoseconds, monotonic) - inline for hot path
static inline uint64_t hak_now_ns(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}
// Get next lg_size for new SuperSlab allocation (uses target_lg)
static inline uint8_t hak_tiny_superslab_next_lg(int class_idx) {
uint8_t lg = g_ss_ace[class_idx].target_lg ? g_ss_ace[class_idx].target_lg
: g_ss_ace[class_idx].current_lg;
return lg ? lg : SUPERSLAB_LG_DEFAULT; // Use default if uninitialized
}
// ----------------------------------------------------------------------------
// ============================================================================
// Partial SuperSlab adopt/publish (per-class single-slot)
// ----------------------------------------------------------------------------
// ============================================================================
// Publish a SuperSlab with available freelist for other threads to adopt.
void ss_partial_publish(int class_idx, SuperSlab* ss);
// Adopt published SuperSlab for the class (returns NULL if none).
SuperSlab* ss_partial_adopt(int class_idx);
// ----------------------------------------------------------------------------
// ============================================================================
// SuperSlab adopt gate (publish/adopt wiring helper)
// ----------------------------------------------------------------------------
// ============================================================================
// Environment-aware switch that keeps free/alloc sides in sync. Default:
// - Disabled until cross-thread free is observed.
// - `HAKMEM_TINY_SS_ADOPT=1` forces ON, `=0` forces OFF.
@ -369,297 +93,11 @@ int tiny_adopt_gate_should_publish(void);
int tiny_adopt_gate_should_adopt(void);
void tiny_adopt_gate_on_remote_seen(int class_idx);
// Remote free push (MPSC stack) - returns 1 if transitioned from empty
// ============================================================================
// External variable declarations
// ============================================================================
extern _Atomic int g_ss_remote_seen; // set to 1 on first remote free observed
extern int g_debug_remote_guard;
static inline int ss_remote_push(SuperSlab* ss, int slab_idx, void* ptr) {
extern _Atomic uint64_t g_ss_remote_push_calls;
atomic_fetch_add_explicit(&g_ss_remote_push_calls, 1, memory_order_relaxed);
static _Atomic int g_remote_push_count = 0;
int count = atomic_fetch_add_explicit(&g_remote_push_count, 1, memory_order_relaxed);
if (count < 5) {
fprintf(stderr, "[DEBUG ss_remote_push] Call #%d ss=%p slab_idx=%d\n", count+1, (void*)ss, slab_idx);
fflush(stderr);
}
if (g_debug_remote_guard && count < 5) {
fprintf(stderr, "[REMOTE_PUSH] ss=%p slab_idx=%d ptr=%p count=%d\n",
(void*)ss, slab_idx, ptr, count);
}
// Unconditional sanity checks (Fail-Fast without crashing)
{
uintptr_t ptr_val = (uintptr_t)ptr;
uintptr_t base = (uintptr_t)ss;
size_t ss_size = (size_t)1ULL << ss->lg_size;
int cap = ss_slabs_capacity(ss);
int in_range = (ptr_val >= base) && (ptr_val < base + ss_size);
int aligned = ((ptr_val & (sizeof(void*) - 1)) == 0);
if (!in_range || slab_idx < 0 || slab_idx >= cap || !aligned) {
uintptr_t code = 0xB001u;
if (!in_range) code |= 0x01u;
if (!aligned) code |= 0x02u;
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
(uint16_t)ss->size_class,
ptr,
((uintptr_t)slab_idx << 32) | code);
return 0;
}
}
// A/B: global disable for remote MPSC — fallback to legacy freelist push
do {
static int g_disable_remote_glob = -1;
if (__builtin_expect(g_disable_remote_glob == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_DISABLE_REMOTE");
g_disable_remote_glob = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_disable_remote_glob, 0)) {
TinySlabMeta* meta = &ss->slabs[slab_idx];
void* prev = meta->freelist;
*(void**)ptr = prev;
meta->freelist = ptr;
// Reflect accounting (callers also decrement used; keep idempotent here)
ss_active_dec_one(ss);
if (prev == NULL) {
// first item: mark this slab visible to adopters
uint32_t bit = (1u << slab_idx);
atomic_fetch_or_explicit(&ss->freelist_mask, bit, memory_order_release);
return 1;
}
return 0;
}
} while (0);
_Atomic(uintptr_t)* head = &ss->remote_heads[slab_idx];
uintptr_t old;
do {
old = atomic_load_explicit(head, memory_order_acquire);
if (!g_remote_side_enable) {
*(void**)ptr = (void*)old; // legacy embedding
}
} while (!atomic_compare_exchange_weak_explicit(head, &old, (uintptr_t)ptr,
memory_order_release, memory_order_relaxed));
tiny_remote_side_set(ss, slab_idx, ptr, old);
tiny_remote_track_on_remote_push(ss, slab_idx, ptr, "remote_push", 0);
if (__builtin_expect(g_debug_remote_guard, 0)) {
// One-shot verify just-written next/ptr alignment and range
uintptr_t base = (uintptr_t)ss;
size_t ss_size = (size_t)1ULL << ss->lg_size;
uintptr_t pv = (uintptr_t)ptr;
int ptr_in = (pv >= base && pv < base + ss_size);
int ptr_al = ((pv & (sizeof(void*) - 1)) == 0);
int old_in = (old == 0) || ((old >= base) && (old < base + ss_size));
int old_al = (old == 0) || ((old & (sizeof(void*) - 1)) == 0);
if (!ptr_in || !ptr_al || !old_in || !old_al) {
uintptr_t flags = ((uintptr_t)ptr_al << 3) | ((uintptr_t)ptr_in << 2) | ((uintptr_t)old_al << 1) | (uintptr_t)old_in;
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
(uint16_t)ss->size_class,
ptr,
0xB100u | (flags & 0xFu));
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
}
fprintf(stderr, "[REMOTE_PUSH] cls=%u slab=%d ptr=%p old=%p transitioned=%d\n",
ss->size_class, slab_idx, ptr, (void*)old, old == 0);
// Pack: [slab_idx<<32 | bit0:old==0 | bit1:old_al | bit2:ptr_al]
uintptr_t aux = ((uintptr_t)slab_idx << 32) | ((old == 0) ? 1u : 0u) | ((old_al ? 1u : 0u) << 1) | ((ptr_al ? 1u : 0u) << 2);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_PUSH,
(uint16_t)ss->size_class,
ptr,
aux);
} else {
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_PUSH,
(uint16_t)ss->size_class,
ptr,
((uintptr_t)slab_idx << 32) | (uint32_t)(old == 0));
}
atomic_fetch_add_explicit(&ss->remote_counts[slab_idx], 1u, memory_order_relaxed);
ss_active_dec_one(ss); // Fix: Decrement active blocks on cross-thread free
atomic_store_explicit(&g_ss_remote_seen, 1, memory_order_relaxed);
int transitioned = (old == 0);
// (optional hint to Ready ring moved to mailbox/aggregator to avoid header coupling)
if (transitioned) {
// First remote observed for this slab: mark slab_listed and notify publisher paths
unsigned prev = atomic_exchange_explicit(&ss->slab_listed[slab_idx], 1u, memory_order_acq_rel);
(void)prev; // best-effort
extern void tiny_publish_notify(int class_idx, struct SuperSlab* ss, int slab_idx);
tiny_publish_notify((int)ss->size_class, ss, slab_idx);
} else {
// Optional: best-effort notify if already non-empty but not listed
extern int g_remote_force_notify;
if (__builtin_expect(g_remote_force_notify, 0)) {
unsigned listed = atomic_load_explicit(&ss->slab_listed[slab_idx], memory_order_acquire);
if (listed == 0) {
unsigned prev = atomic_exchange_explicit(&ss->slab_listed[slab_idx], 1u, memory_order_acq_rel);
(void)prev;
extern void tiny_publish_notify(int class_idx, struct SuperSlab* ss, int slab_idx);
tiny_publish_notify((int)ss->size_class, ss, slab_idx);
}
}
}
return transitioned;
}
// Drain remote queue into freelist (no change to used/active; already adjusted at free)
// INTERNAL UNSAFE VERSION - Only called by slab_handle.h after ownership verified!
// DO NOT call directly - use slab_drain_remote() via SlabHandle instead.
static inline void _ss_remote_drain_to_freelist_unsafe(SuperSlab* ss, int slab_idx, TinySlabMeta* meta) {
do { // one-shot debug print when enabled
static int en = -1; static _Atomic int printed;
if (__builtin_expect(en == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_REFILL_OPT_DEBUG");
en = (e && *e && *e != '0') ? 1 : 0;
}
if (en) {
int exp = 0; if (atomic_compare_exchange_strong(&printed, &exp, 1)) {
fprintf(stderr, "[DRAIN_OPT] chain splice active (cls=%u slab=%d)\n", ss ? ss->size_class : 0u, slab_idx);
}
}
} while (0);
_Atomic(uintptr_t)* head = &ss->remote_heads[slab_idx];
uintptr_t p = atomic_exchange_explicit(head, (uintptr_t)NULL, memory_order_acq_rel);
if (p == 0) return;
uint32_t drained = 0;
uintptr_t base = (uintptr_t)ss;
size_t ss_size = (size_t)1ULL << ss->lg_size;
uint32_t drain_tid = (uint32_t)(uintptr_t)pthread_self();
// Build a local chain then splice once into freelist to reduce writes
void* chain_head = NULL;
void* chain_tail = NULL;
while (p != 0) {
// Guard: range/alignment before deref
if (__builtin_expect(g_debug_remote_guard, 0)) {
if (p < base || p >= base + ss_size) {
uintptr_t aux = tiny_remote_pack_diag(0xA210u, base, ss_size, p);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)p, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
if ((p & (uintptr_t)(sizeof(void*) - 1)) != 0) {
uintptr_t aux = tiny_remote_pack_diag(0xA211u, base, ss_size, p);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)p, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
}
void* node = (void*)p;
uintptr_t next = tiny_remote_side_get(ss, slab_idx, node);
tiny_remote_watch_note("drain_pull", ss, slab_idx, node, 0xA238u, drain_tid, 0);
if (__builtin_expect(g_remote_side_enable, 0)) {
if (!tiny_remote_sentinel_ok(node)) {
uintptr_t aux = tiny_remote_pack_diag(0xA202u, base, ss_size, (uintptr_t)node);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, node, aux);
uintptr_t observed = atomic_load_explicit((_Atomic uintptr_t*)node, memory_order_relaxed);
tiny_remote_report_corruption("drain", node, observed);
TinySlabMeta* meta = &ss->slabs[slab_idx];
fprintf(stderr,
"[REMOTE_SENTINEL-DRAIN] cls=%u slab=%d node=%p drained=%u observed=0x%016" PRIxPTR " owner=%u used=%u freelist=%p\n",
ss->size_class,
slab_idx,
node,
drained,
observed,
meta->owner_tid,
(unsigned)meta->used,
meta->freelist);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
}
tiny_remote_side_clear(ss, slab_idx, node);
}
tiny_remote_watch_note("drain_link", ss, slab_idx, node, 0xA239u, drain_tid, 0);
tiny_remote_track_on_remote_drain(ss, slab_idx, node, "remote_drain", drain_tid);
if (__builtin_expect(g_debug_remote_guard && drained < 3, 0)) {
// First few nodes: record low info for triage
uintptr_t aux = ((uintptr_t)slab_idx << 32) | (uintptr_t)(drained & 0xFFFF);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_DRAIN, (uint16_t)ss->size_class, node, aux);
}
// Link into local chain (avoid touching meta->freelist per node)
if (chain_head == NULL) {
chain_head = node;
chain_tail = node;
*(void**)node = NULL;
} else {
*(void**)node = chain_head;
chain_head = node;
}
p = next;
drained++;
}
// Splice the drained chain into freelist (single meta write)
if (chain_head != NULL) {
if (chain_tail != NULL) {
*(void**)chain_tail = meta->freelist;
}
void* prev = meta->freelist;
meta->freelist = chain_head;
tiny_failfast_log("remote_drain", ss->size_class, ss, meta, chain_head, prev);
// Optional: set freelist bit when transitioning from empty
do {
static int g_mask_en = -1;
if (__builtin_expect(g_mask_en == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_FREELIST_MASK");
g_mask_en = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_mask_en, 0)) {
uint32_t bit = (1u << slab_idx);
atomic_fetch_or_explicit(&ss->freelist_mask, bit, memory_order_release);
}
} while (0);
}
// Reset remote count after full drain
atomic_store_explicit(&ss->remote_counts[slab_idx], 0u, memory_order_relaxed);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_DRAIN,
(uint16_t)ss->size_class,
ss,
((uintptr_t)slab_idx << 32) | drained);
}
// Legacy wrapper for compatibility (UNSAFE - ownership NOT checked!)
// DEPRECATED: Use slab_drain_remote() via SlabHandle instead
static inline void ss_remote_drain_to_freelist(SuperSlab* ss, int slab_idx) {
TinySlabMeta* meta = &ss->slabs[slab_idx];
_ss_remote_drain_to_freelist_unsafe(ss, slab_idx, meta);
}
// Try to acquire exclusive ownership of slab (REQUIRED before draining remote queue!)
// Returns 1 on success (now own slab), 0 on failure (another thread owns it)
// CRITICAL: Only succeeds if slab is unowned (owner_tid==0) or already owned by us.
static inline int ss_owner_try_acquire(TinySlabMeta* m, uint32_t self_tid) {
uint32_t cur = __atomic_load_n(&m->owner_tid, __ATOMIC_RELAXED);
if (cur == self_tid) return 1; // Already owner - success
if (cur != 0) return 0; // Another thread owns it - FAIL immediately
// Slab is unowned (cur==0) - try to claim it
uint32_t expected = 0;
return __atomic_compare_exchange_n(&m->owner_tid, &expected, self_tid, false,
__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
// Drain remote queues where activity was observed (lightweight sweep).
// CRITICAL: Must acquire ownership before draining each slab!
static inline void ss_remote_drain_light(SuperSlab* ss) {
if (!ss) return;
uint32_t threshold = tiny_remote_drain_threshold();
uint32_t self_tid = (uint32_t)(uintptr_t)pthread_self();
int cap = ss_slabs_capacity(ss);
for (int s = 0; s < cap; s++) {
uint32_t rc = atomic_load_explicit(&ss->remote_counts[s], memory_order_relaxed);
if (rc <= threshold) continue;
if (atomic_load_explicit(&ss->remote_heads[s], memory_order_acquire) != 0) {
// BUGFIX: Must acquire ownership BEFORE draining!
// Without this, we can drain a slab owned by another thread → freelist corruption
TinySlabMeta* m = &ss->slabs[s];
if (!ss_owner_try_acquire(m, self_tid)) {
continue; // Failed to acquire - skip this slab
}
ss_remote_drain_to_freelist(ss, s);
}
}
}
// Best-effort CAS to transfer slab ownership (DEPRECATED - use ss_owner_try_acquire!)
static inline void ss_owner_cas(TinySlabMeta* m, uint32_t self_tid) {
(void)ss_owner_try_acquire(m, self_tid); // Ignore result (unsafe)
}
extern int g_remote_force_notify;
#endif // HAKMEM_TINY_SUPERSLAB_H

View File

@ -0,0 +1,487 @@
// superslab_inline.h - SuperSlab Hot Path Inline Functions
// Purpose: Performance-critical inline helpers for SuperSlab allocator
// Extracted from hakmem_tiny_superslab.h (Phase 6-2.8 Refactoring)
#ifndef SUPERSLAB_INLINE_H
#define SUPERSLAB_INLINE_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <inttypes.h>
#include "superslab_types.h"
#include "hakmem_tiny_superslab_constants.h"
#include "tiny_debug_ring.h"
#include "tiny_remote.h"
// External declarations
extern int g_debug_remote_guard;
extern int g_tiny_safe_free_strict;
extern _Atomic uint64_t g_ss_active_dec_calls;
extern _Atomic uint64_t g_ss_remote_push_calls;
extern _Atomic int g_ss_remote_seen;
extern int g_remote_side_enable;
extern int g_remote_force_notify;
// Function declarations
uint32_t tiny_remote_drain_threshold(void);
void tiny_publish_notify(int class_idx, struct SuperSlab* ss, int slab_idx);
// ============================================================================
// Fast Path Inline Functions
// ============================================================================
// Runtime-safe slab count for a given SuperSlab (MUST BE FIRST - used by other functions)
static inline int ss_slabs_capacity(const SuperSlab* ss) {
size_t ss_size = (size_t)1 << ss->lg_size;
return (int)(ss_size / SLAB_SIZE); // 16 or 32
}
// Fail-fast validation level (0=off, 1=basic, 2=paranoid)
static inline int tiny_refill_failfast_level(void) {
static int g_failfast_level = -1;
if (__builtin_expect(g_failfast_level == -1, 0)) {
const char* env = getenv("HAKMEM_TINY_REFILL_FAILFAST");
if (env && *env) {
g_failfast_level = atoi(env);
} else {
g_failfast_level = 1;
}
}
return g_failfast_level;
}
// Fail-fast logging (level 2 only)
static inline void tiny_failfast_log(const char* stage,
int class_idx,
SuperSlab* ss,
TinySlabMeta* meta,
const void* node,
const void* next) {
if (__builtin_expect(tiny_refill_failfast_level() < 2, 1)) return;
uintptr_t base = ss ? (uintptr_t)ss : 0;
size_t size = ss ? ((size_t)1ULL << ss->lg_size) : 0;
uintptr_t limit = base + size;
fprintf(stderr,
"[TRC_FREELIST_LOG] stage=%s cls=%d node=%p next=%p head=%p base=%p limit=%p\n",
stage ? stage : "(null)",
class_idx,
node,
next,
meta ? meta->freelist : NULL,
(void*)base,
(void*)limit);
fflush(stderr);
}
// Fail-fast abort with detailed diagnostics
static inline void tiny_failfast_abort_ptr(const char* stage,
SuperSlab* ss,
int slab_idx,
const void* ptr,
const char* reason) {
if (__builtin_expect(tiny_refill_failfast_level() < 2, 1)) return;
uintptr_t base = ss ? (uintptr_t)ss : 0;
size_t size = ss ? ((size_t)1ULL << ss->lg_size) : 0;
uintptr_t limit = base + size;
size_t cap = 0;
uint32_t used = 0;
if (ss && slab_idx >= 0 && slab_idx < ss_slabs_capacity(ss)) {
cap = ss->slabs[slab_idx].capacity;
used = ss->slabs[slab_idx].used;
}
size_t offset = 0;
if (ptr && base && ptr >= (void*)base) {
offset = (size_t)((uintptr_t)ptr - base);
}
fprintf(stderr,
"[TRC_FAILFAST_PTR] stage=%s cls=%d slab_idx=%d ptr=%p reason=%s base=%p limit=%p cap=%zu used=%u offset=%zu\n",
stage ? stage : "(null)",
ss ? (int)ss->size_class : -1,
slab_idx,
ptr,
reason ? reason : "(null)",
(void*)base,
(void*)limit,
cap,
used,
offset);
fflush(stderr);
abort();
}
// Get slab index within SuperSlab (DEPRECATED - use slab_index_for)
static inline int ptr_to_slab_index(void* p) {
uintptr_t offset = (uintptr_t)p & SUPERSLAB_MASK;
return (int)(offset >> 16); // Divide by 64KB (2^16)
}
// Safe slab index computation using SuperSlab base (supports 1MB/2MB)
static inline int slab_index_for(const SuperSlab* ss, const void* p) {
uintptr_t base = (uintptr_t)ss;
uintptr_t addr = (uintptr_t)p;
uintptr_t off = addr - base;
int idx = (int)(off >> 16); // 64KB
int cap = ss_slabs_capacity(ss);
return (idx >= 0 && idx < cap) ? idx : -1;
}
// Get slab data start address
static inline void* slab_data_start(SuperSlab* ss, int slab_idx) {
return (char*)ss + (slab_idx * SLAB_SIZE);
}
// Get slab base address (accounts for SUPERSLAB_SLAB0_DATA_OFFSET)
static inline uint8_t* tiny_slab_base_for(SuperSlab* ss, int slab_idx) {
uint8_t* base = (uint8_t*)slab_data_start(ss, slab_idx);
// Phase 6-2.5 FIX: Use SUPERSLAB_SLAB0_DATA_OFFSET constant
// sizeof(SuperSlab)=1088, aligned to next 1024-boundary=2048
// This ensures proper alignment for class 7 (1024-byte blocks)
if (slab_idx == 0) base += SUPERSLAB_SLAB0_DATA_OFFSET;
return base;
}
// Refcount helpers (for future MT-safe empty reclamation)
static inline void superslab_ref_inc(SuperSlab* ss) {
atomic_fetch_add_explicit(&ss->refcount, 1u, memory_order_relaxed);
}
static inline unsigned superslab_ref_dec(SuperSlab* ss) {
return atomic_fetch_sub_explicit(&ss->refcount, 1u, memory_order_acq_rel) - 1u;
}
static inline unsigned superslab_ref_get(SuperSlab* ss) {
return atomic_load_explicit(&ss->refcount, memory_order_acquire);
}
// Active block counter helper (saturating decrement for free operations)
static inline void ss_active_dec_one(SuperSlab* ss) {
atomic_fetch_add_explicit(&g_ss_active_dec_calls, 1, memory_order_relaxed);
uint32_t old = atomic_load_explicit(&ss->total_active_blocks, memory_order_relaxed);
while (old != 0) {
if (atomic_compare_exchange_weak_explicit(&ss->total_active_blocks, &old, old - 1u,
memory_order_relaxed, memory_order_relaxed)) {
break;
}
// CAS failed: old is reloaded by CAS intrinsic
}
}
// Low-cost timestamp (nanoseconds, monotonic) - inline for hot path
static inline uint64_t hak_now_ns(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}
// Get next lg_size for new SuperSlab allocation (uses target_lg)
// Forward declaration of ACE state (defined in main header)
typedef struct {
uint8_t current_lg;
uint8_t target_lg;
uint16_t hot_score;
uint32_t alloc_count;
uint32_t refill_count;
uint32_t spill_count;
uint32_t live_blocks;
uint64_t last_tick_ns;
} SuperSlabACEState;
extern SuperSlabACEState g_ss_ace[8]; // TINY_NUM_CLASSES_SS
static inline uint8_t hak_tiny_superslab_next_lg(int class_idx) {
uint8_t lg = g_ss_ace[class_idx].target_lg ? g_ss_ace[class_idx].target_lg
: g_ss_ace[class_idx].current_lg;
return lg ? lg : SUPERSLAB_LG_DEFAULT; // Use default if uninitialized
}
// Remote free push (MPSC stack) - returns 1 if transitioned from empty
static inline int ss_remote_push(SuperSlab* ss, int slab_idx, void* ptr) {
atomic_fetch_add_explicit(&g_ss_remote_push_calls, 1, memory_order_relaxed);
static _Atomic int g_remote_push_count = 0;
int count = atomic_fetch_add_explicit(&g_remote_push_count, 1, memory_order_relaxed);
if (count < 5) {
fprintf(stderr, "[DEBUG ss_remote_push] Call #%d ss=%p slab_idx=%d\n", count+1, (void*)ss, slab_idx);
fflush(stderr);
}
if (g_debug_remote_guard && count < 5) {
fprintf(stderr, "[REMOTE_PUSH] ss=%p slab_idx=%d ptr=%p count=%d\n",
(void*)ss, slab_idx, ptr, count);
}
// Unconditional sanity checks (Fail-Fast without crashing)
{
uintptr_t ptr_val = (uintptr_t)ptr;
uintptr_t base = (uintptr_t)ss;
size_t ss_size = (size_t)1ULL << ss->lg_size;
int cap = ss_slabs_capacity(ss);
int in_range = (ptr_val >= base) && (ptr_val < base + ss_size);
int aligned = ((ptr_val & (sizeof(void*) - 1)) == 0);
if (!in_range || slab_idx < 0 || slab_idx >= cap || !aligned) {
uintptr_t code = 0xB001u;
if (!in_range) code |= 0x01u;
if (!aligned) code |= 0x02u;
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
(uint16_t)ss->size_class,
ptr,
((uintptr_t)slab_idx << 32) | code);
return 0;
}
}
// A/B: global disable for remote MPSC — fallback to legacy freelist push
do {
static int g_disable_remote_glob = -1;
if (__builtin_expect(g_disable_remote_glob == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_DISABLE_REMOTE");
g_disable_remote_glob = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_disable_remote_glob, 0)) {
TinySlabMeta* meta = &ss->slabs[slab_idx];
void* prev = meta->freelist;
*(void**)ptr = prev;
meta->freelist = ptr;
// Reflect accounting (callers also decrement used; keep idempotent here)
ss_active_dec_one(ss);
if (prev == NULL) {
// first item: mark this slab visible to adopters
uint32_t bit = (1u << slab_idx);
atomic_fetch_or_explicit(&ss->freelist_mask, bit, memory_order_release);
return 1;
}
return 0;
}
} while (0);
_Atomic(uintptr_t)* head = &ss->remote_heads[slab_idx];
uintptr_t old;
do {
old = atomic_load_explicit(head, memory_order_acquire);
if (!g_remote_side_enable) {
*(void**)ptr = (void*)old; // legacy embedding
}
} while (!atomic_compare_exchange_weak_explicit(head, &old, (uintptr_t)ptr,
memory_order_release, memory_order_relaxed));
tiny_remote_side_set(ss, slab_idx, ptr, old);
tiny_remote_track_on_remote_push(ss, slab_idx, ptr, "remote_push", 0);
if (__builtin_expect(g_debug_remote_guard, 0)) {
// One-shot verify just-written next/ptr alignment and range
uintptr_t base = (uintptr_t)ss;
size_t ss_size = (size_t)1ULL << ss->lg_size;
uintptr_t pv = (uintptr_t)ptr;
int ptr_in = (pv >= base && pv < base + ss_size);
int ptr_al = ((pv & (sizeof(void*) - 1)) == 0);
int old_in = (old == 0) || ((old >= base) && (old < base + ss_size));
int old_al = (old == 0) || ((old & (sizeof(void*) - 1)) == 0);
if (!ptr_in || !ptr_al || !old_in || !old_al) {
uintptr_t flags = ((uintptr_t)ptr_al << 3) | ((uintptr_t)ptr_in << 2) | ((uintptr_t)old_al << 1) | (uintptr_t)old_in;
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
(uint16_t)ss->size_class,
ptr,
0xB100u | (flags & 0xFu));
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
}
fprintf(stderr, "[REMOTE_PUSH] cls=%u slab=%d ptr=%p old=%p transitioned=%d\n",
ss->size_class, slab_idx, ptr, (void*)old, old == 0);
// Pack: [slab_idx<<32 | bit0:old==0 | bit1:old_al | bit2:ptr_al]
uintptr_t aux = ((uintptr_t)slab_idx << 32) | ((old == 0) ? 1u : 0u) | ((old_al ? 1u : 0u) << 1) | ((ptr_al ? 1u : 0u) << 2);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_PUSH,
(uint16_t)ss->size_class,
ptr,
aux);
} else {
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_PUSH,
(uint16_t)ss->size_class,
ptr,
((uintptr_t)slab_idx << 32) | (uint32_t)(old == 0));
}
atomic_fetch_add_explicit(&ss->remote_counts[slab_idx], 1u, memory_order_relaxed);
ss_active_dec_one(ss); // Fix: Decrement active blocks on cross-thread free
atomic_store_explicit(&g_ss_remote_seen, 1, memory_order_relaxed);
int transitioned = (old == 0);
// (optional hint to Ready ring moved to mailbox/aggregator to avoid header coupling)
if (transitioned) {
// First remote observed for this slab: mark slab_listed and notify publisher paths
unsigned prev = atomic_exchange_explicit(&ss->slab_listed[slab_idx], 1u, memory_order_acq_rel);
(void)prev; // best-effort
tiny_publish_notify((int)ss->size_class, ss, slab_idx);
} else {
// Optional: best-effort notify if already non-empty but not listed
if (__builtin_expect(g_remote_force_notify, 0)) {
unsigned listed = atomic_load_explicit(&ss->slab_listed[slab_idx], memory_order_acquire);
if (listed == 0) {
unsigned prev = atomic_exchange_explicit(&ss->slab_listed[slab_idx], 1u, memory_order_acq_rel);
(void)prev;
tiny_publish_notify((int)ss->size_class, ss, slab_idx);
}
}
}
return transitioned;
}
// Drain remote queue into freelist (no change to used/active; already adjusted at free)
// INTERNAL UNSAFE VERSION - Only called by slab_handle.h after ownership verified!
// DO NOT call directly - use slab_drain_remote() via SlabHandle instead.
static inline void _ss_remote_drain_to_freelist_unsafe(SuperSlab* ss, int slab_idx, TinySlabMeta* meta) {
do { // one-shot debug print when enabled
static int en = -1; static _Atomic int printed;
if (__builtin_expect(en == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_REFILL_OPT_DEBUG");
en = (e && *e && *e != '0') ? 1 : 0;
}
if (en) {
int exp = 0; if (atomic_compare_exchange_strong(&printed, &exp, 1)) {
fprintf(stderr, "[DRAIN_OPT] chain splice active (cls=%u slab=%d)\n", ss ? ss->size_class : 0u, slab_idx);
}
}
} while (0);
_Atomic(uintptr_t)* head = &ss->remote_heads[slab_idx];
uintptr_t p = atomic_exchange_explicit(head, (uintptr_t)NULL, memory_order_acq_rel);
if (p == 0) return;
uint32_t drained = 0;
uintptr_t base = (uintptr_t)ss;
size_t ss_size = (size_t)1ULL << ss->lg_size;
uint32_t drain_tid = (uint32_t)(uintptr_t)pthread_self();
// Build a local chain then splice once into freelist to reduce writes
void* chain_head = NULL;
void* chain_tail = NULL;
while (p != 0) {
// Guard: range/alignment before deref
if (__builtin_expect(g_debug_remote_guard, 0)) {
if (p < base || p >= base + ss_size) {
uintptr_t aux = tiny_remote_pack_diag(0xA210u, base, ss_size, p);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)p, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
if ((p & (uintptr_t)(sizeof(void*) - 1)) != 0) {
uintptr_t aux = tiny_remote_pack_diag(0xA211u, base, ss_size, p);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)p, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
}
void* node = (void*)p;
uintptr_t next = tiny_remote_side_get(ss, slab_idx, node);
tiny_remote_watch_note("drain_pull", ss, slab_idx, node, 0xA238u, drain_tid, 0);
if (__builtin_expect(g_remote_side_enable, 0)) {
if (!tiny_remote_sentinel_ok(node)) {
uintptr_t aux = tiny_remote_pack_diag(0xA202u, base, ss_size, (uintptr_t)node);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, node, aux);
uintptr_t observed = atomic_load_explicit((_Atomic uintptr_t*)node, memory_order_relaxed);
tiny_remote_report_corruption("drain", node, observed);
TinySlabMeta* meta = &ss->slabs[slab_idx];
fprintf(stderr,
"[REMOTE_SENTINEL-DRAIN] cls=%u slab=%d node=%p drained=%u observed=0x%016" PRIxPTR " owner=%u used=%u freelist=%p\n",
ss->size_class,
slab_idx,
node,
drained,
observed,
meta->owner_tid,
(unsigned)meta->used,
meta->freelist);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
}
tiny_remote_side_clear(ss, slab_idx, node);
}
tiny_remote_watch_note("drain_link", ss, slab_idx, node, 0xA239u, drain_tid, 0);
tiny_remote_track_on_remote_drain(ss, slab_idx, node, "remote_drain", drain_tid);
if (__builtin_expect(g_debug_remote_guard && drained < 3, 0)) {
// First few nodes: record low info for triage
uintptr_t aux = ((uintptr_t)slab_idx << 32) | (uintptr_t)(drained & 0xFFFF);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_DRAIN, (uint16_t)ss->size_class, node, aux);
}
// Link into local chain (avoid touching meta->freelist per node)
if (chain_head == NULL) {
chain_head = node;
chain_tail = node;
*(void**)node = NULL;
} else {
*(void**)node = chain_head;
chain_head = node;
}
p = next;
drained++;
}
// Splice the drained chain into freelist (single meta write)
if (chain_head != NULL) {
if (chain_tail != NULL) {
*(void**)chain_tail = meta->freelist;
}
void* prev = meta->freelist;
meta->freelist = chain_head;
tiny_failfast_log("remote_drain", ss->size_class, ss, meta, chain_head, prev);
// Optional: set freelist bit when transitioning from empty
do {
static int g_mask_en = -1;
if (__builtin_expect(g_mask_en == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_FREELIST_MASK");
g_mask_en = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_mask_en, 0)) {
uint32_t bit = (1u << slab_idx);
atomic_fetch_or_explicit(&ss->freelist_mask, bit, memory_order_release);
}
} while (0);
}
// Reset remote count after full drain
atomic_store_explicit(&ss->remote_counts[slab_idx], 0u, memory_order_relaxed);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_DRAIN,
(uint16_t)ss->size_class,
ss,
((uintptr_t)slab_idx << 32) | drained);
}
// Legacy wrapper for compatibility (UNSAFE - ownership NOT checked!)
// DEPRECATED: Use slab_drain_remote() via SlabHandle instead
static inline void ss_remote_drain_to_freelist(SuperSlab* ss, int slab_idx) {
TinySlabMeta* meta = &ss->slabs[slab_idx];
_ss_remote_drain_to_freelist_unsafe(ss, slab_idx, meta);
}
// Try to acquire exclusive ownership of slab (REQUIRED before draining remote queue!)
// Returns 1 on success (now own slab), 0 on failure (another thread owns it)
// CRITICAL: Only succeeds if slab is unowned (owner_tid==0) or already owned by us.
static inline int ss_owner_try_acquire(TinySlabMeta* m, uint32_t self_tid) {
uint32_t cur = __atomic_load_n(&m->owner_tid, __ATOMIC_RELAXED);
if (cur == self_tid) return 1; // Already owner - success
if (cur != 0) return 0; // Another thread owns it - FAIL immediately
// Slab is unowned (cur==0) - try to claim it
uint32_t expected = 0;
return __atomic_compare_exchange_n(&m->owner_tid, &expected, self_tid, false,
__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
// Drain remote queues where activity was observed (lightweight sweep).
// CRITICAL: Must acquire ownership before draining each slab!
static inline void ss_remote_drain_light(SuperSlab* ss) {
if (!ss) return;
uint32_t threshold = tiny_remote_drain_threshold();
uint32_t self_tid = (uint32_t)(uintptr_t)pthread_self();
int cap = ss_slabs_capacity(ss);
for (int s = 0; s < cap; s++) {
uint32_t rc = atomic_load_explicit(&ss->remote_counts[s], memory_order_relaxed);
if (rc <= threshold) continue;
if (atomic_load_explicit(&ss->remote_heads[s], memory_order_acquire) != 0) {
// BUGFIX: Must acquire ownership BEFORE draining!
// Without this, we can drain a slab owned by another thread → freelist corruption
TinySlabMeta* m = &ss->slabs[s];
if (!ss_owner_try_acquire(m, self_tid)) {
continue; // Failed to acquire - skip this slab
}
ss_remote_drain_to_freelist(ss, s);
}
}
}
// Best-effort CAS to transfer slab ownership (DEPRECATED - use ss_owner_try_acquire!)
static inline void ss_owner_cas(TinySlabMeta* m, uint32_t self_tid) {
(void)ss_owner_try_acquire(m, self_tid); // Ignore result (unsafe)
}
#endif // SUPERSLAB_INLINE_H

View File

@ -0,0 +1,104 @@
// superslab_types.h - SuperSlab Configuration & Data Structures
// Purpose: Core types and configuration for SuperSlab allocator
// Extracted from hakmem_tiny_superslab.h (Phase 6-2.8 Refactoring)
#ifndef SUPERSLAB_TYPES_H
#define SUPERSLAB_TYPES_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <stdio.h>
#include "hakmem_tiny_superslab_constants.h" // SLAB_SIZE, SUPERSLAB_SLAB0_DATA_OFFSET
// ============================================================================
// SuperSlab Configuration
// ============================================================================
// Phase 8.3: ACE - Variable SuperSlab size (1MB ↔ 2MB)
#define SUPERSLAB_SIZE_MAX (2 * 1024 * 1024) // 2MB max size
#define SUPERSLAB_SIZE_MIN (1 * 1024 * 1024) // 1MB min size
#define SUPERSLAB_LG_MAX 21 // lg(2MB)
#define SUPERSLAB_LG_MIN 20 // lg(1MB)
#define SUPERSLAB_LG_DEFAULT 21 // Default: 2MB (syscall reduction, ACE will adapt)
// Number of tiny size classes (same as TINY_NUM_CLASSES to avoid circular include)
#define TINY_NUM_CLASSES_SS 8 // 8-64 bytes (8, 16, 24, 32, 40, 48, 56, 64)
// Legacy defines (kept for backward compatibility, use lg_size instead)
#define SUPERSLAB_SIZE SUPERSLAB_SIZE_MAX // Default to 2MB (syscall reduction)
#define SUPERSLAB_MASK (SUPERSLAB_SIZE - 1)
// IMPORTANT: Support variable-size SuperSlab (1MB=16 slabs, 2MB=32 slabs)
// Arrays below must be sized for the MAX to avoid OOB when lg_size=21 (2MB)
#define SLABS_PER_SUPERSLAB_MIN (SUPERSLAB_SIZE_MIN / SLAB_SIZE) // 16 for 1MB
#define SLABS_PER_SUPERSLAB_MAX (SUPERSLAB_SIZE_MAX / SLAB_SIZE) // 32 for 2MB
// Magic number for validation
#define SUPERSLAB_MAGIC 0x48414B4D454D5353ULL // "HAKMEMSS"
// ============================================================================
// SuperSlab Metadata Structure
// ============================================================================
// Per-slab metadata (16 bytes)
typedef struct TinySlabMeta {
void* freelist; // Freelist head (NULL = linear mode, Phase 6.24)
uint16_t used; // Blocks currently used
uint16_t capacity; // Total blocks in slab
uint32_t owner_tid; // Owner thread ID (for same-thread fast path)
// Phase 6.24: freelist == NULL → linear allocation mode (lazy init)
// Linear mode: allocate sequentially without building freelist
// Freelist mode: use freelist after first free() call
} TinySlabMeta;
// SuperSlab header (cache-line aligned, 64B)
typedef struct SuperSlab {
// Header fields (64B total)
uint64_t magic; // Magic number (0xHAKMEM_SUPERSLAB)
uint8_t size_class; // Size class (0-7 for 8-64B)
uint8_t active_slabs; // Number of active slabs (0-32 for 2MB, 0-16 for 1MB)
uint8_t lg_size; // Phase 8.3: ACE - SuperSlab size (20=1MB, 21=2MB)
uint8_t _pad0; // Padding
uint32_t slab_bitmap; // 32-bit bitmap (1=active, 0=free)
_Atomic uint32_t freelist_mask; // Bit i=1 when slab i freelist is non-empty (opt-in)
// Phase 6-2.1: ChatGPT Pro P0 optimization - O(1) non-empty slab lookup
uint32_t nonempty_mask; // Bit i = 1 if slabs[i].freelist != NULL (O(1) lookup via ctz)
// Phase 7.6: Deallocation support
atomic_uint total_active_blocks; // Total blocks in use (all slabs combined)
atomic_uint refcount; // MT-safe refcount for empty detection/free将来利用
atomic_uint listed; // 0/1: published to partial adopt ringpublish gating
uint32_t partial_epoch; // Last partial madvise epoch (optional)
uint8_t publish_hint; // Best slab index hint for adopt (0..31), 0xFF=none
uint8_t _pad1[3]; // Padding
// Per-slab metadata (16B each)
// Sized for MAX; use ss->lg_size to bound loops at runtime
TinySlabMeta slabs[SLABS_PER_SUPERSLAB_MAX];
// Remote free queues (per slab): MPSC stack heads + counts
_Atomic(uintptr_t) remote_heads[SLABS_PER_SUPERSLAB_MAX];
_Atomic(uint32_t) remote_counts[SLABS_PER_SUPERSLAB_MAX];
// Per-slab publish state: 0/1 = not listed/listed (for slab-granular republish hints)
atomic_uint slab_listed[SLABS_PER_SUPERSLAB_MAX];
// Partial adopt overflow linkage (single-linked, best-effort)
struct SuperSlab* partial_next;
// Padding to fill remaining space (2MB - 64B - 512B)
// Note: Actual slab data starts at offset SLAB_SIZE (64KB)
} __attribute__((aligned(64))) SuperSlab;
// Compile-time assertions
_Static_assert(sizeof(TinySlabMeta) == 16, "TinySlabMeta must be 16 bytes");
// Phase 8.3: Variable-size SuperSlab assertions (1MB=16 slabs, 2MB=32 slabs)
_Static_assert((SUPERSLAB_SIZE_MIN / SLAB_SIZE) == 16, "1MB SuperSlab must have 16 slabs");
_Static_assert((SUPERSLAB_SIZE_MAX / SLAB_SIZE) == 32, "2MB SuperSlab must have 32 slabs");
_Static_assert((SUPERSLAB_SIZE & SUPERSLAB_MASK) == 0, "SUPERSLAB_SIZE must be power of 2");
#endif // SUPERSLAB_TYPES_H