fix(Phase 11+): Cold Start lazy init for unified_cache_refill
Root cause: unified_cache_refill() accessed cache->slots before initialization when a size class was first used via the refill path (not pop path). Fix: Add lazy initialization check at start of unified_cache_refill() - Check if cache->slots is NULL before accessing - Call unified_cache_init() if needed - Return NULL if init fails (graceful degradation) Also includes: - ss_cold_start_box.inc.h: Box Pattern for default prewarm settings - hakmem_super_registry.c: Use static array in prewarm (avoid recursion) - Default prewarm enabled (1 SuperSlab/class, configurable via ENV) Test: 8B→16B→Mixed allocation pattern now works correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
118
core/box/ss_cold_start_box.inc.h
Normal file
118
core/box/ss_cold_start_box.inc.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// ss_cold_start_box.inc.h - SuperSlab Cold Start Protection
|
||||||
|
// ==========================================================================
|
||||||
|
//
|
||||||
|
// Purpose: Prevent OOM on cold start by enabling minimal prewarm by default
|
||||||
|
//
|
||||||
|
// Problem:
|
||||||
|
// - SuperSlab LRU cache starts empty
|
||||||
|
// - First allocation triggers mmap() which may fail
|
||||||
|
// - No prewarm by default = cold start OOM
|
||||||
|
//
|
||||||
|
// Solution (Box Pattern):
|
||||||
|
// - Default: Prewarm 1 SuperSlab per class (minimal footprint: 8MB)
|
||||||
|
// - ENV configurable: HAKMEM_SS_PREWARM_COUNT=N (0 to disable)
|
||||||
|
// - Observable: Debug logging
|
||||||
|
// - Inline: Zero runtime overhead for config access
|
||||||
|
//
|
||||||
|
// ENV Variables:
|
||||||
|
// HAKMEM_SS_PREWARM_COUNT=N - SuperSlabs per class (default: 1)
|
||||||
|
// HAKMEM_SS_PREWARM_DISABLE=1 - Disable prewarm entirely (legacy compat)
|
||||||
|
//
|
||||||
|
// Memory Impact:
|
||||||
|
// Default (count=1): 8 classes * 1MB = 8MB
|
||||||
|
// Aggressive (count=4): 8 classes * 4MB = 32MB
|
||||||
|
//
|
||||||
|
// ==========================================================================
|
||||||
|
|
||||||
|
#ifndef HAK_SS_COLD_START_BOX_INC_H
|
||||||
|
#define HAK_SS_COLD_START_BOX_INC_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h> // For ss_cold_start_log_config()
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Configuration Constants
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Default prewarm count per class (minimal cold start protection)
|
||||||
|
// Phase 11+: Fixed malloc() recursion by using static array in hak_ss_prewarm_class()
|
||||||
|
#define SS_COLD_START_DEFAULT_COUNT 1
|
||||||
|
|
||||||
|
// Maximum prewarm count per class (safety cap)
|
||||||
|
#define SS_COLD_START_MAX_COUNT 512
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Inline Configuration Helper
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Get cold start prewarm count (cached, ENV-configurable)
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - 0 if disabled (HAKMEM_SS_PREWARM_DISABLE=1)
|
||||||
|
// - N from HAKMEM_SS_PREWARM_COUNT=N if set
|
||||||
|
// - SS_COLD_START_DEFAULT_COUNT (1) otherwise
|
||||||
|
//
|
||||||
|
// Thread-safe: uses static cache with simple atomic semantics
|
||||||
|
//
|
||||||
|
static inline int ss_cold_start_get_count(void) {
|
||||||
|
static int g_cached = -1; // -1 = not initialized
|
||||||
|
|
||||||
|
if (__builtin_expect(g_cached >= 0, 1)) {
|
||||||
|
return g_cached; // Fast path: already cached
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: parse ENV
|
||||||
|
|
||||||
|
// Check disable flag first
|
||||||
|
const char* disable_env = getenv("HAKMEM_SS_PREWARM_DISABLE");
|
||||||
|
if (disable_env && *disable_env && *disable_env != '0') {
|
||||||
|
g_cached = 0; // Disabled
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check custom count
|
||||||
|
const char* count_env = getenv("HAKMEM_SS_PREWARM_COUNT");
|
||||||
|
if (count_env && *count_env) {
|
||||||
|
char* endptr;
|
||||||
|
long count = strtol(count_env, &endptr, 10);
|
||||||
|
if (*endptr == '\0' && count >= 0) {
|
||||||
|
// Cap at max
|
||||||
|
if (count > SS_COLD_START_MAX_COUNT) {
|
||||||
|
count = SS_COLD_START_MAX_COUNT;
|
||||||
|
}
|
||||||
|
g_cached = (int)count;
|
||||||
|
return g_cached;
|
||||||
|
}
|
||||||
|
// Invalid format - fall through to default
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: minimal prewarm enabled
|
||||||
|
g_cached = SS_COLD_START_DEFAULT_COUNT;
|
||||||
|
return g_cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if cold start prewarm is enabled
|
||||||
|
static inline int ss_cold_start_is_enabled(void) {
|
||||||
|
return ss_cold_start_get_count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Debug Helper
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Log cold start configuration (call from init for diagnostics)
|
||||||
|
static inline void ss_cold_start_log_config(void) {
|
||||||
|
#if !HAKMEM_BUILD_RELEASE
|
||||||
|
int count = ss_cold_start_get_count();
|
||||||
|
if (count > 0) {
|
||||||
|
fprintf(stderr, "[SS_COLD_START] Enabled: %d SuperSlabs per class (total: %d MB)\n",
|
||||||
|
count, count * 8); // 8 classes * 1MB
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[SS_COLD_START] Disabled\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)0; // No-op in release mode
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAK_SS_COLD_START_BOX_INC_H
|
||||||
@ -303,6 +303,15 @@ void* unified_cache_refill(int class_idx) {
|
|||||||
|
|
||||||
TinyUnifiedCache* cache = &g_unified_cache[class_idx];
|
TinyUnifiedCache* cache = &g_unified_cache[class_idx];
|
||||||
|
|
||||||
|
// ✅ Phase 11+: Ensure cache is initialized (lazy init for cold path)
|
||||||
|
if (!cache->slots) {
|
||||||
|
unified_cache_init();
|
||||||
|
// Re-check after init (may fail due to alloc failure)
|
||||||
|
if (!cache->slots) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 2: Calculate available room in unified cache
|
// Step 2: Calculate available room in unified cache
|
||||||
int room = (int)cache->capacity - 1; // Leave 1 slot for full detection
|
int room = (int)cache->capacity - 1; // Leave 1 slot for full detection
|
||||||
if (cache->head > cache->tail) {
|
if (cache->head > cache->tail) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include "hakmem_tiny_superslab.h"
|
#include "hakmem_tiny_superslab.h"
|
||||||
#include "box/ss_allocation_box.h" // For superslab_allocate() declaration
|
#include "box/ss_allocation_box.h" // For superslab_allocate() declaration
|
||||||
#include "box/ss_addr_map_box.h" // Phase 9-1: SuperSlab address map
|
#include "box/ss_addr_map_box.h" // Phase 9-1: SuperSlab address map
|
||||||
|
#include "box/ss_cold_start_box.inc.h" // Phase 11+: Cold Start prewarm defaults
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/mman.h> // munmap for incompatible SuperSlab eviction
|
#include <sys/mman.h> // munmap for incompatible SuperSlab eviction
|
||||||
@ -587,11 +588,12 @@ void hak_ss_prewarm_class(int size_class, uint32_t count) {
|
|||||||
hak_ss_lru_init();
|
hak_ss_lru_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate all SuperSlabs first (store in temp array to avoid LRU pop/push cycle)
|
// Phase 11+: Use static array to avoid malloc() during init (causes recursion)
|
||||||
SuperSlab** slabs = (SuperSlab**)malloc(count * sizeof(SuperSlab*));
|
// Cap at 512 as defined in SS_COLD_START_MAX_COUNT
|
||||||
if (!slabs) {
|
#define SS_PREWARM_MAX_BATCH 512
|
||||||
fprintf(stderr, "[SS_PREWARM] Failed to allocate temp array for class %d\n", size_class);
|
static SuperSlab* slabs[SS_PREWARM_MAX_BATCH];
|
||||||
return;
|
if (count > SS_PREWARM_MAX_BATCH) {
|
||||||
|
count = SS_PREWARM_MAX_BATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable prewarm bypass to prevent LRU cache from being used during allocation
|
// Enable prewarm bypass to prevent LRU cache from being used during allocation
|
||||||
@ -625,7 +627,7 @@ void hak_ss_prewarm_class(int size_class, uint32_t count) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(slabs);
|
// Note: slabs is static array, no free() needed
|
||||||
|
|
||||||
// Debug logging for PREWARM
|
// Debug logging for PREWARM
|
||||||
if (dbg == 1) {
|
if (dbg == 1) {
|
||||||
@ -653,6 +655,7 @@ void hak_ss_prewarm_all(const uint32_t counts[TINY_NUM_CLASSES]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prewarm: Allocate SuperSlabs at startup and add to LRU cache
|
// Prewarm: Allocate SuperSlabs at startup and add to LRU cache
|
||||||
|
// Phase 11+: Cold Start Box enables prewarm by default (1 SuperSlab/class)
|
||||||
void hak_ss_prewarm_init(void) {
|
void hak_ss_prewarm_init(void) {
|
||||||
#if !HAKMEM_BUILD_RELEASE
|
#if !HAKMEM_BUILD_RELEASE
|
||||||
// Debug logging flag (lazy init)
|
// Debug logging flag (lazy init)
|
||||||
@ -665,20 +668,37 @@ void hak_ss_prewarm_init(void) {
|
|||||||
static const int dbg = 0;
|
static const int dbg = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Parse environment variable
|
// Phase 11+: Get default from Cold Start Box (enables prewarm by default)
|
||||||
const char* env = getenv("HAKMEM_PREWARM_SUPERSLABS");
|
// Can be disabled via HAKMEM_SS_PREWARM_DISABLE=1 or HAKMEM_SS_PREWARM_COUNT=0
|
||||||
if (!env || !*env) {
|
int cold_start_count = ss_cold_start_get_count();
|
||||||
// Prewarm disabled
|
ss_cold_start_log_config(); // Log configuration for diagnostics
|
||||||
|
|
||||||
|
if (cold_start_count == 0) {
|
||||||
|
// Prewarm explicitly disabled
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse as single number (uniform across all classes)
|
// Check for legacy ENV override (HAKMEM_PREWARM_SUPERSLABS)
|
||||||
|
// This takes precedence over Cold Start Box default
|
||||||
|
const char* env = getenv("HAKMEM_PREWARM_SUPERSLABS");
|
||||||
|
long global = cold_start_count; // Default from Cold Start Box
|
||||||
|
|
||||||
|
if (env && *env) {
|
||||||
|
// Legacy ENV override
|
||||||
char* endptr;
|
char* endptr;
|
||||||
long global = strtol(env, &endptr, 10);
|
long parsed = strtol(env, &endptr, 10);
|
||||||
if (*endptr != '\0' || global <= 0) {
|
if (*endptr == '\0' && parsed >= 0) {
|
||||||
fprintf(stderr, "[SS_PREWARM] Invalid HAKMEM_PREWARM_SUPERSLABS='%s' (expected positive integer)\n", env);
|
global = parsed;
|
||||||
|
if (global == 0) {
|
||||||
|
// Legacy disable via HAKMEM_PREWARM_SUPERSLABS=0
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[SS_PREWARM] Invalid HAKMEM_PREWARM_SUPERSLABS='%s' (expected integer)\n", env);
|
||||||
|
// Continue with cold_start_count default
|
||||||
|
global = cold_start_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cap at reasonable limit (avoid OOM on typo like "10000")
|
// Cap at reasonable limit (avoid OOM on typo like "10000")
|
||||||
if (global > 512) {
|
if (global > 512) {
|
||||||
|
|||||||
Reference in New Issue
Block a user