Summary of Changes: MOVED TO ARCHIVE: - core/hakmem_tiny_legacy_slow_box.inc → archive/ * Slow path legacy code preserved for reference * Superseded by Gatekeeper Box architecture - core/superslab_allocate.c → archive/superslab_allocate_legacy.c * Legacy SuperSlab allocation implementation * Functionality integrated into new Box system - core/superslab_head.c → archive/superslab_head_legacy.c * Legacy slab head management * Refactored through Box architecture REMOVED DEAD CODE: - Eliminated unused allocation policy variants from ss_allocation_box.c * Reduced from 127+ lines of conditional logic to focused implementation * Removed: old policy branches, unused allocation strategies * Kept: current Box-based allocation path ADDED NEW INFRASTRUCTURE: - core/superslab_head_stub.c (41 lines) * Minimal stub for backward compatibility * Delegates to new architecture - Enhanced core/superslab_cache.c (75 lines added) * Added missing API functions for cache management * Proper interface for SuperSlab cache integration REFACTORED CORE SYSTEMS: - core/hakmem_super_registry.c * Moved registration logic from scattered locations * Centralized SuperSlab registry management - core/hakmem_tiny.c * Removed 27 lines of redundant initialization * Simplified through Box architecture - core/hakmem_tiny_alloc.inc * Streamlined allocation path to use Gatekeeper * Removed legacy decision logic - core/box/ss_allocation_box.c/h * Dramatically simplified allocation policy * Removed conditional branches for unused strategies * Focused on current Box-based approach BUILD SYSTEM: - Updated Makefile for archive structure - Removed obsolete object file references - Maintained build compatibility SAFETY & TESTING: - All deletions verified: no broken references - Build verification: RELEASE=0 and RELEASE=1 pass - Smoke tests: 100% pass rate - Functional verification: allocation/free intact Architecture Consolidation: Before: Multiple overlapping allocation paths with legacy code branches After: Single unified path through Gatekeeper Boxes with clear architecture Benefits: - Reduced code size and complexity - Improved maintainability - Single source of truth for allocation logic - Better diagnostic/observability hooks - Foundation for future optimizations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
268 lines
9.8 KiB
C
268 lines
9.8 KiB
C
/**
|
|
* hak_lane_classify.inc.h - Phase 2: Lane Classification Box
|
|
*
|
|
* Box: Allocation Lane Classification (Single Source of Truth)
|
|
* Responsibility: Centralized size-to-lane mapping with unified boundary definitions
|
|
* Contract: All allocator boundaries defined here; no hardcoded values elsewhere
|
|
*
|
|
* Design Principles (Box Pattern):
|
|
* 1. Single Source of Truth: All lane boundaries defined in ONE place
|
|
* 2. Normalize-then-Classify: Always use normalized size for classification
|
|
* 3. Clear Invariants: POOL_MIN = TINY_MAX + 1 (no gaps)
|
|
* 4. Observable: Debug helpers for lane inspection
|
|
* 5. Safe: LANE_FALLBACK catches design bugs
|
|
*
|
|
* Problem Solved:
|
|
* - Before: TINY_MAX_SIZE=1024 vs tiny_get_max_size()=2047 (inconsistent!)
|
|
* - Before: Hardcoded 8192 in Pool TLS, 1024 in Tiny, etc.
|
|
* - Result: 1025-2047B "unmanaged zone" causing libc fragmentation
|
|
*
|
|
* Solution:
|
|
* - Define all boundaries as LANE_* constants
|
|
* - hak_classify_size() is THE authority for routing
|
|
* - Existing code uses compatibility wrappers
|
|
*
|
|
* Lane Architecture:
|
|
* LANE_TINY: [0, LANE_TINY_MAX] = 0-1024B SuperSlab
|
|
* LANE_POOL: (LANE_TINY_MAX, LANE_POOL_MAX] = 1025-52KB Pool per-thread
|
|
* LANE_ACE: (LANE_POOL_MAX, LANE_ACE_MAX] = 52KB-2MB ACE learning
|
|
* LANE_HUGE: (LANE_ACE_MAX, ∞) = 2MB+ mmap direct
|
|
*
|
|
* Created: 2025-12-02 (Phase 2-1)
|
|
* License: MIT
|
|
*/
|
|
|
|
#ifndef HAK_LANE_CLASSIFY_INC_H
|
|
#define HAK_LANE_CLASSIFY_INC_H
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
// ============================================================================
|
|
// Lane Boundary Definitions (Single Source of Truth)
|
|
// ============================================================================
|
|
//
|
|
// CRITICAL: These are the ONLY authoritative boundary values.
|
|
// All other code MUST reference these constants (not hardcode numbers).
|
|
//
|
|
// Invariant: Each lane's MIN = previous lane's MAX + 1 (no gaps!)
|
|
|
|
#define LANE_TINY_MAX 1024 // Tiny handles [0, 1024]
|
|
#define LANE_POOL_MIN (LANE_TINY_MAX + 1) // Pool handles [1025, ...] (invariant!)
|
|
#define LANE_POOL_MAX (52 * 1024) // Pool handles [..., 52KB]
|
|
#define LANE_ACE_MIN (LANE_POOL_MAX + 1) // ACE handles [52KB+1, ...]
|
|
#define LANE_ACE_MAX (2 * 1024 * 1024) // ACE handles [..., 2MB]
|
|
#define LANE_HUGE_MIN (LANE_ACE_MAX + 1) // Huge handles [2MB+1, ...]
|
|
|
|
// ============================================================================
|
|
// Pool Internal: Request Size vs Block Size (separate concepts!)
|
|
// ============================================================================
|
|
//
|
|
// POOL_MIN_REQUEST_SIZE: Smallest user request Pool will accept (= LANE_POOL_MIN)
|
|
// POOL_MIN_CLASS_SIZE: Smallest block class Pool actually allocates
|
|
//
|
|
// Example: request=1056B -> class=2048B (internal fragmentation ~48%, acceptable)
|
|
|
|
#define POOL_MIN_REQUEST_SIZE LANE_POOL_MIN // 1025 (boundary)
|
|
#define POOL_MIN_CLASS_SIZE (2 * 1024) // 2048 (block size)
|
|
|
|
// ============================================================================
|
|
// Lane Enumeration
|
|
// ============================================================================
|
|
|
|
typedef enum {
|
|
LANE_TINY, // SuperSlab-based, 0-1024B, TLS cache
|
|
LANE_POOL, // Pool per-thread, 1025-52KB, site-sharded
|
|
LANE_ACE, // ACE learning layer, 52KB-2MB
|
|
LANE_HUGE, // Direct mmap, 2MB+
|
|
LANE_FALLBACK // Bug detection only (should never happen)
|
|
} hak_lane_t;
|
|
|
|
// ============================================================================
|
|
// Size Normalization
|
|
// ============================================================================
|
|
//
|
|
// Purpose: Convert user-requested size to internal allocation size
|
|
// Rule: All lane classification uses normalized size for consistency
|
|
//
|
|
// Note: HEADER_SIZE and alignment are allocator-specific.
|
|
// This function provides a generic template; actual allocators may have
|
|
// their own normalization based on their header requirements.
|
|
|
|
#ifndef HAK_LANE_HEADER_SIZE
|
|
#define HAK_LANE_HEADER_SIZE 16 // Default header size (override if needed)
|
|
#endif
|
|
|
|
#ifndef HAK_LANE_ALIGN
|
|
#define HAK_LANE_ALIGN 16 // Default alignment (override if needed)
|
|
#endif
|
|
|
|
/**
|
|
* hak_normalize_size - Convert user size to internal allocation size
|
|
*
|
|
* @param user_size Size requested by user (malloc argument)
|
|
* @return Internal size (header + aligned user data)
|
|
*
|
|
* This ensures consistent boundary checking across all allocators.
|
|
* Example: user_size=1000, header=16, align=16 -> norm_size=1024
|
|
*/
|
|
__attribute__((always_inline))
|
|
static inline size_t hak_normalize_size(size_t user_size) {
|
|
size_t n = user_size;
|
|
// For lane classification, we use user_size directly since each
|
|
// allocator (Tiny/Pool/ACE) handles its own header internally.
|
|
// The boundaries are defined in terms of user-visible sizes.
|
|
return n;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Lane Classification (THE Authority)
|
|
// ============================================================================
|
|
|
|
/**
|
|
* hak_classify_size - Determine which lane handles this allocation
|
|
*
|
|
* @param size User-requested size (not normalized)
|
|
* @return Lane enumeration value
|
|
*
|
|
* CRITICAL: This is THE single point of truth for allocation routing.
|
|
* All allocation paths MUST use this function (or the switch macro).
|
|
*
|
|
* Boundaries are INCLUSIVE on the lower side, EXCLUSIVE on the upper:
|
|
* LANE_TINY: size <= LANE_TINY_MAX
|
|
* LANE_POOL: LANE_TINY_MAX < size <= LANE_POOL_MAX
|
|
* LANE_ACE: LANE_POOL_MAX < size <= LANE_ACE_MAX
|
|
* LANE_HUGE: size > LANE_ACE_MAX
|
|
*/
|
|
__attribute__((always_inline, pure))
|
|
static inline hak_lane_t hak_classify_size(size_t size) {
|
|
if (__builtin_expect(size <= LANE_TINY_MAX, 1)) {
|
|
return LANE_TINY; // Hot path: most allocations are small
|
|
}
|
|
if (size <= LANE_POOL_MAX) {
|
|
return LANE_POOL; // 1025-52KB
|
|
}
|
|
if (size <= LANE_ACE_MAX) {
|
|
return LANE_ACE; // 52KB-2MB
|
|
}
|
|
return LANE_HUGE; // 2MB+ (direct mmap)
|
|
// Note: LANE_FALLBACK is never returned here; it's for error detection
|
|
}
|
|
|
|
// ============================================================================
|
|
// Convenience Macros for Routing
|
|
// ============================================================================
|
|
|
|
/**
|
|
* HAK_LANE_IS_TINY - Check if size belongs to Tiny lane
|
|
*/
|
|
#define HAK_LANE_IS_TINY(size) ((size) <= LANE_TINY_MAX)
|
|
|
|
/**
|
|
* HAK_LANE_IS_POOL - Check if size belongs to Pool lane
|
|
*/
|
|
#define HAK_LANE_IS_POOL(size) ((size) > LANE_TINY_MAX && (size) <= LANE_POOL_MAX)
|
|
|
|
/**
|
|
* HAK_LANE_IS_ACE - Check if size belongs to ACE lane
|
|
*/
|
|
#define HAK_LANE_IS_ACE(size) ((size) > LANE_POOL_MAX && (size) <= LANE_ACE_MAX)
|
|
|
|
/**
|
|
* HAK_LANE_IS_HUGE - Check if size belongs to Huge lane
|
|
*/
|
|
#define HAK_LANE_IS_HUGE(size) ((size) > LANE_ACE_MAX)
|
|
|
|
// ============================================================================
|
|
// Compatibility Wrappers (for existing code migration)
|
|
// ============================================================================
|
|
//
|
|
// These allow gradual migration from old constants to new LANE_* values.
|
|
// TODO: Remove these after all code is migrated to use LANE_* directly.
|
|
|
|
// Tiny compatibility
|
|
#ifndef TINY_MAX_SIZE
|
|
#define TINY_MAX_SIZE LANE_TINY_MAX
|
|
#endif
|
|
|
|
// Pool compatibility (request boundary, not class size)
|
|
// Note: POOL_MIN_SIZE historically meant "minimum request size Pool accepts"
|
|
#ifndef POOL_MIN_SIZE_COMPAT
|
|
#define POOL_MIN_SIZE_COMPAT POOL_MIN_REQUEST_SIZE
|
|
#endif
|
|
|
|
// ============================================================================
|
|
// Debug / Observability
|
|
// ============================================================================
|
|
|
|
#if !defined(HAKMEM_BUILD_RELEASE) || !HAKMEM_BUILD_RELEASE
|
|
|
|
#include <stdio.h>
|
|
|
|
/**
|
|
* hak_lane_name - Get human-readable lane name
|
|
*/
|
|
static inline const char* hak_lane_name(hak_lane_t lane) {
|
|
switch (lane) {
|
|
case LANE_TINY: return "TINY";
|
|
case LANE_POOL: return "POOL";
|
|
case LANE_ACE: return "ACE";
|
|
case LANE_HUGE: return "HUGE";
|
|
case LANE_FALLBACK: return "FALLBACK";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* hak_lane_debug - Print lane classification for debugging
|
|
*/
|
|
static inline void hak_lane_debug(size_t size) {
|
|
hak_lane_t lane = hak_classify_size(size);
|
|
fprintf(stderr, "[LANE] size=%zu -> %s\n", size, hak_lane_name(lane));
|
|
}
|
|
|
|
/**
|
|
* hak_lane_config_report - Print lane configuration
|
|
*/
|
|
static inline void hak_lane_config_report(void) {
|
|
fprintf(stderr, "[LANE_CONFIG] Boundaries:\n");
|
|
fprintf(stderr, " TINY: [0, %d]\n", LANE_TINY_MAX);
|
|
fprintf(stderr, " POOL: [%d, %d] (class_min=%d)\n",
|
|
LANE_POOL_MIN, LANE_POOL_MAX, POOL_MIN_CLASS_SIZE);
|
|
fprintf(stderr, " ACE: [%d, %d]\n", LANE_ACE_MIN, LANE_ACE_MAX);
|
|
fprintf(stderr, " HUGE: [%d, ...]\n", LANE_HUGE_MIN);
|
|
}
|
|
|
|
#endif // !HAKMEM_BUILD_RELEASE
|
|
|
|
// ============================================================================
|
|
// Fallback Detection Guard
|
|
// ============================================================================
|
|
|
|
/**
|
|
* HAK_LANE_ASSERT_NO_FALLBACK - Assert that FALLBACK lane is never reached
|
|
*
|
|
* Usage: Place in allocation paths where LANE_FALLBACK indicates a bug.
|
|
* In release builds, this compiles to nothing.
|
|
*/
|
|
#if !defined(HAKMEM_BUILD_RELEASE) || !HAKMEM_BUILD_RELEASE
|
|
#define HAK_LANE_ASSERT_NO_FALLBACK(lane, size) do { \
|
|
if (__builtin_expect((lane) == LANE_FALLBACK, 0)) { \
|
|
fprintf(stderr, "[HAKMEM] BUG: LANE_FALLBACK reached for size=%zu\n", (size_t)(size)); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define HAK_LANE_ASSERT_NO_FALLBACK(lane, size) ((void)0)
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // HAK_LANE_CLASSIFY_INC_H
|