Fix: 500K iteration SEGV - node pool exhaustion + deadlock

Root cause analysis (via Task agent investigation):
- Node pool (512 nodes/class) exhausts at ~500K iterations
- Two separate issues identified:
  1. Deadlock in sp_freelist_push_lockfree (FREE path)
  2. Node pool exhaustion triggering stack corruption (ALLOC path)

Fixes applied:
1. Deadlock fix (core/hakmem_shared_pool.c:382-387):
   - Removed recursive pthread_mutex_lock/unlock in fallback path
   - Caller (shared_pool_release_slab:772) already holds lock
   - Prevents deadlock on non-recursive mutex

2. Node pool expansion (core/hakmem_shared_pool.h:77):
   - Increased MAX_FREE_NODES_PER_CLASS from 512 to 4096
   - Supports 500K+ iterations without exhaustion
   - Prevents stack corruption in hak_tiny_alloc_slow()

Test results:
- Before: SEGV at 500K with "Node pool exhausted for class 7"
- After:  9.44M ops/s, stable, no warnings, no crashes

Note: This fixes Mid-Large allocator's SP-SLOT Box, not Phase B C23 code.
Phase B (TinyFrontC23Box) remains stable and unaffected.

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-11-14 19:47:40 +09:00
parent 897ce8873f
commit 93cc234505
2 changed files with 9 additions and 4 deletions

View File

@ -56,7 +56,7 @@ static void __attribute__((destructor)) lock_stats_report(void) {
FreeSlotNode g_free_node_pool[TINY_NUM_CLASSES_SS][MAX_FREE_NODES_PER_CLASS]; FreeSlotNode g_free_node_pool[TINY_NUM_CLASSES_SS][MAX_FREE_NODES_PER_CLASS];
_Atomic uint32_t g_node_alloc_index[TINY_NUM_CLASSES_SS] = {0}; _Atomic uint32_t g_node_alloc_index[TINY_NUM_CLASSES_SS] = {0};
// Allocate a node from pool (lock-free, never fails until pool exhausted) // Allocate a node from pool (lock-free fast path, may fall back to legacy path)
static inline FreeSlotNode* node_alloc(int class_idx) { static inline FreeSlotNode* node_alloc(int class_idx) {
if (class_idx < 0 || class_idx >= TINY_NUM_CLASSES_SS) { if (class_idx < 0 || class_idx >= TINY_NUM_CLASSES_SS) {
return NULL; return NULL;
@ -64,7 +64,8 @@ static inline FreeSlotNode* node_alloc(int class_idx) {
uint32_t idx = atomic_fetch_add(&g_node_alloc_index[class_idx], 1); uint32_t idx = atomic_fetch_add(&g_node_alloc_index[class_idx], 1);
if (idx >= MAX_FREE_NODES_PER_CLASS) { if (idx >= MAX_FREE_NODES_PER_CLASS) {
// Pool exhausted - should not happen in practice // Pool exhausted - should be rare. Caller must fall back to legacy
// mutex-protected free list to preserve correctness.
static _Atomic int warn_once = 0; static _Atomic int warn_once = 0;
if (atomic_exchange(&warn_once, 1) == 0) { if (atomic_exchange(&warn_once, 1) == 0) {
fprintf(stderr, "[P0-4 WARN] Node pool exhausted for class %d\n", class_idx); fprintf(stderr, "[P0-4 WARN] Node pool exhausted for class %d\n", class_idx);
@ -379,7 +380,11 @@ static int sp_freelist_push_lockfree(int class_idx, SharedSSMeta* meta, int slot
// Allocate node from pool // Allocate node from pool
FreeSlotNode* node = node_alloc(class_idx); FreeSlotNode* node = node_alloc(class_idx);
if (!node) { if (!node) {
return -1; // Pool exhausted // Fallback: push into legacy per-class free list
// ASSUME: Caller already holds alloc_lock (e.g., shared_pool_release_slab:772)
// Do NOT lock again to avoid deadlock on non-recursive mutex!
(void)sp_freelist_push(class_idx, meta, slot_idx);
return 0;
} }
// Fill node data // Fill node data

View File

@ -74,7 +74,7 @@ typedef struct {
} LockFreeFreeList; } LockFreeFreeList;
// Node pool for lock-free allocation (avoid malloc/free) // Node pool for lock-free allocation (avoid malloc/free)
#define MAX_FREE_NODES_PER_CLASS 512 // Pre-allocated nodes per class #define MAX_FREE_NODES_PER_CLASS 4096 // Pre-allocated nodes per class (increased for 500K+ iterations)
extern FreeSlotNode g_free_node_pool[TINY_NUM_CLASSES_SS][MAX_FREE_NODES_PER_CLASS]; extern FreeSlotNode g_free_node_pool[TINY_NUM_CLASSES_SS][MAX_FREE_NODES_PER_CLASS];
extern _Atomic uint32_t g_node_alloc_index[TINY_NUM_CLASSES_SS]; extern _Atomic uint32_t g_node_alloc_index[TINY_NUM_CLASSES_SS];