diff --git a/core/box/ss_cold_start_box.inc.h b/core/box/ss_cold_start_box.inc.h new file mode 100644 index 00000000..a07b0195 --- /dev/null +++ b/core/box/ss_cold_start_box.inc.h @@ -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 +#include // 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 diff --git a/core/front/tiny_unified_cache.c b/core/front/tiny_unified_cache.c index 1a354e14..818989dc 100644 --- a/core/front/tiny_unified_cache.c +++ b/core/front/tiny_unified_cache.c @@ -303,6 +303,15 @@ void* unified_cache_refill(int 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 int room = (int)cache->capacity - 1; // Leave 1 slot for full detection if (cache->head > cache->tail) { diff --git a/core/hakmem_super_registry.c b/core/hakmem_super_registry.c index 631241f3..a4f20a59 100644 --- a/core/hakmem_super_registry.c +++ b/core/hakmem_super_registry.c @@ -2,6 +2,7 @@ #include "hakmem_tiny_superslab.h" #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_cold_start_box.inc.h" // Phase 11+: Cold Start prewarm defaults #include #include #include // munmap for incompatible SuperSlab eviction @@ -587,11 +588,12 @@ void hak_ss_prewarm_class(int size_class, uint32_t count) { hak_ss_lru_init(); } - // Allocate all SuperSlabs first (store in temp array to avoid LRU pop/push cycle) - SuperSlab** slabs = (SuperSlab**)malloc(count * sizeof(SuperSlab*)); - if (!slabs) { - fprintf(stderr, "[SS_PREWARM] Failed to allocate temp array for class %d\n", size_class); - return; + // Phase 11+: Use static array to avoid malloc() during init (causes recursion) + // Cap at 512 as defined in SS_COLD_START_MAX_COUNT + #define SS_PREWARM_MAX_BATCH 512 + static SuperSlab* slabs[SS_PREWARM_MAX_BATCH]; + if (count > SS_PREWARM_MAX_BATCH) { + count = SS_PREWARM_MAX_BATCH; } // 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 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 +// Phase 11+: Cold Start Box enables prewarm by default (1 SuperSlab/class) void hak_ss_prewarm_init(void) { #if !HAKMEM_BUILD_RELEASE // Debug logging flag (lazy init) @@ -665,19 +668,36 @@ void hak_ss_prewarm_init(void) { static const int dbg = 0; #endif - // Parse environment variable - const char* env = getenv("HAKMEM_PREWARM_SUPERSLABS"); - if (!env || !*env) { - // Prewarm disabled + // Phase 11+: Get default from Cold Start Box (enables prewarm by default) + // Can be disabled via HAKMEM_SS_PREWARM_DISABLE=1 or HAKMEM_SS_PREWARM_COUNT=0 + int cold_start_count = ss_cold_start_get_count(); + ss_cold_start_log_config(); // Log configuration for diagnostics + + if (cold_start_count == 0) { + // Prewarm explicitly disabled return; } - // Parse as single number (uniform across all classes) - char* endptr; - long global = strtol(env, &endptr, 10); - if (*endptr != '\0' || global <= 0) { - fprintf(stderr, "[SS_PREWARM] Invalid HAKMEM_PREWARM_SUPERSLABS='%s' (expected positive integer)\n", env); - return; + // 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; + long parsed = strtol(env, &endptr, 10); + if (*endptr == '\0' && parsed >= 0) { + global = parsed; + if (global == 0) { + // Legacy disable via HAKMEM_PREWARM_SUPERSLABS=0 + 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")