Files
hakmem/core/smallobject_hotbox_v4.c
Moe Charm (CI) 52c65da783 Phase v4-mid-0: Small-object v4 型・IF 足場(箱化モジュール化)
- SmallHeapCtx/SmallPageMeta/SmallClassHeap typedef alias 追加
- SmallSegment struct (base/num_pages/owner_tid/magic) を smallsegment_v4_box.h に定義
- SmallColdIface_v4 direct function prototypes (refill/retire/remote_push/drain)
- smallobject_hotbox_v4.c の internal/public API 分離(small_segment_v4_internal)
- direct function stubs 実装(SmallColdIfaceV4 delegate 形式)
- ENV OFF デフォルト(ENABLED=0/CLASSES=0)で既存挙動 100% 不変
- ビルド成功・sanity 確認(mixed/C6-heavy、segv/assert なし)
- CURRENT_TASK.md に Phase v4-mid-0 記録

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-10 23:23:07 +09:00

445 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// smallobject_hotbox_v4.c - SmallObject HotHeap v4 (C5/C6/C7 opt-in)
//
// Phase v4-3.1: C7 は v4 独自の freelist/current/partial で完結。C6/C5 は強ゲート付きで同形パスを使う。
#include <stdlib.h>
#include <string.h>
#include "box/smallobject_hotbox_v4_box.h"
#include "box/smallobject_hotbox_v4_env_box.h"
#include "box/smallobject_cold_iface_v4.h"
#include "box/smallobject_hotbox_v3_env_box.h"
#include "box/tiny_heap_box.h"
#include "box/smallsegment_v4_box.h"
#include "box/smallsegment_v4_env_box.h"
#include "box/tiny_cold_iface_v1.h"
#include "box/tiny_geometry_box.h"
#include "tiny_region_id.h"
// TLS context
static __thread small_heap_ctx_v4 g_ctx_v4;
// Internal segment structure (internal use only, not exposed via public box API)
typedef struct small_segment_v4_internal {
int class_idx;
size_t segment_size;
tiny_heap_ctx_t* tiny_ctx;
} small_segment_v4_internal;
static __thread small_segment_v4_internal g_segments_v4[SMALLOBJECT_NUM_CLASSES];
small_heap_ctx_v4* small_heap_ctx_v4_get(void) {
return &g_ctx_v4;
}
static small_page_v4* v4_page_from_lease(tiny_heap_page_t* lease, int class_idx, small_segment_v4* seg);
// -----------------------------------------------------------------------------
// helpers
// -----------------------------------------------------------------------------
static inline int v4_class_supported(int class_idx) {
return class_idx == 7 || class_idx == 6 || class_idx == 5;
}
static size_t smallsegment_v4_default_size(void) {
const char* env = smallsegment_v4_size_env();
if (env && *env) {
size_t v = strtoull(env, NULL, 0);
if (v > (size_t)(64 * 1024)) {
return v;
}
}
return (size_t)(2 * 1024 * 1024); // default 2MiB segment単位将来の実装用
}
small_segment_v4* smallsegment_v4_acquire(int class_idx) {
if (!v4_class_supported(class_idx)) return NULL;
small_segment_v4_internal* seg = &g_segments_v4[class_idx];
seg->class_idx = class_idx;
if (!seg->segment_size) {
seg->segment_size = smallsegment_v4_default_size();
}
if (!seg->tiny_ctx) {
seg->tiny_ctx = tiny_heap_ctx_for_thread();
}
return (small_segment_v4*)seg;
}
void* smallsegment_v4_alloc_page(small_segment_v4* seg, int class_idx) {
if (!seg || !v4_class_supported(class_idx)) return NULL;
// Internal use only: cast to internal type to access tiny_ctx
small_segment_v4_internal* int_seg = (small_segment_v4_internal*)seg;
if (!int_seg->tiny_ctx) {
int_seg->tiny_ctx = tiny_heap_ctx_for_thread();
}
tiny_heap_ctx_t* tctx = int_seg->tiny_ctx ? int_seg->tiny_ctx : tiny_heap_ctx_for_thread();
if (!tctx) return NULL;
tiny_heap_page_t* lease = tiny_heap_prepare_page(tctx, class_idx);
if (!lease) return NULL;
int_seg->tiny_ctx = tctx;
return v4_page_from_lease(lease, class_idx, seg);
}
void smallsegment_v4_release_if_empty(small_segment_v4* seg, void* page_ptr, int class_idx) {
small_page_v4* page = (small_page_v4*)page_ptr;
if (!page || !v4_class_supported(class_idx)) return;
tiny_heap_ctx_t* tctx = tiny_heap_ctx_for_thread();
tiny_heap_page_t* lease = (tiny_heap_page_t*)page->slab_ref;
if (tctx && lease) {
tiny_heap_page_becomes_empty(tctx, class_idx, lease);
}
free(page);
}
static inline void v4_page_push_partial(small_class_heap_v4* h, small_page_v4* page) {
if (!h || !page) return;
page->next = h->partial_head;
h->partial_head = page;
h->partial_count++;
}
static inline small_page_v4* v4_page_pop_partial(small_class_heap_v4* h) {
if (!h) return NULL;
small_page_v4* p = h->partial_head;
if (p) {
h->partial_head = p->next;
p->next = NULL;
if (h->partial_count > 0) {
h->partial_count--;
}
}
return p;
}
static inline void v4_page_push_full(small_class_heap_v4* h, small_page_v4* page) {
if (!h || !page) return;
page->next = h->full_head;
h->full_head = page;
}
static inline uint32_t v4_partial_limit(int class_idx) {
// C7 は refill/retire を抑えるため少し広めに保持
return (class_idx == 7) ? 2u : 1u;
}
static inline int v4_ptr_in_page(const small_page_v4* page, const uint8_t* ptr) {
if (!page || !ptr) return 0;
uint8_t* base = page->base;
size_t span = (size_t)page->block_size * (size_t)page->capacity;
if (ptr < base || ptr >= base + span) return 0;
size_t off = (size_t)(ptr - base);
return (off % page->block_size) == 0;
}
static inline void* v4_build_freelist(uint8_t* base, uint16_t capacity, size_t stride) {
void* head = NULL;
for (int i = capacity - 1; i >= 0; i--) {
uint8_t* blk = base + ((size_t)i * stride);
void* next = head;
head = blk;
memcpy(blk, &next, sizeof(void*));
}
return head;
}
typedef enum {
V4_LOC_NONE = 0,
V4_LOC_CURRENT,
V4_LOC_PARTIAL,
V4_LOC_FULL,
} v4_loc_t;
static small_page_v4* v4_find_page(small_class_heap_v4* h, const uint8_t* ptr, v4_loc_t* loc, small_page_v4** prev_out) {
if (loc) *loc = V4_LOC_NONE;
if (prev_out) *prev_out = NULL;
if (!h || !ptr) return NULL;
if (h->current && v4_ptr_in_page(h->current, ptr)) {
if (loc) *loc = V4_LOC_CURRENT;
return h->current;
}
small_page_v4* prev = NULL;
for (small_page_v4* p = h->partial_head; p; prev = p, p = p->next) {
if (v4_ptr_in_page(p, ptr)) {
if (loc) *loc = V4_LOC_PARTIAL;
if (prev_out) *prev_out = prev;
return p;
}
}
for (small_page_v4* p = h->full_head; p; prev = p, p = p->next) {
if (v4_ptr_in_page(p, ptr)) {
if (loc) *loc = V4_LOC_FULL;
if (prev_out) *prev_out = prev;
return p;
}
}
return NULL;
}
int smallobject_hotbox_v4_can_own(int class_idx, void* ptr) {
if (__builtin_expect(!v4_class_supported(class_idx), 0)) return 0;
if (!small_heap_v4_class_enabled((uint8_t)class_idx)) return 0;
if (!ptr) return 0;
small_heap_ctx_v4* ctx = small_heap_ctx_v4_get();
if (!ctx) return 0;
small_class_heap_v4* h = &ctx->cls[class_idx];
return v4_find_page(h, (const uint8_t*)ptr, NULL, NULL) != NULL;
}
// -----------------------------------------------------------------------------
// Cold iface (C5/C6/C7, Tiny v1 経由)
// -----------------------------------------------------------------------------
static small_page_v4* v4_page_from_lease(tiny_heap_page_t* lease, int class_idx, small_segment_v4* seg) {
if (!lease) return NULL;
small_page_v4* page = (small_page_v4*)malloc(sizeof(small_page_v4));
if (!page) return NULL;
memset(page, 0, sizeof(*page));
page->class_idx = (uint8_t)class_idx;
page->capacity = lease->capacity;
page->used = 0;
page->block_size = (uint32_t)tiny_stride_for_class((int)class_idx);
page->base = lease->base;
page->slab_ref = lease;
page->segment = seg;
page->freelist = v4_build_freelist(lease->base, lease->capacity, page->block_size);
if (!page->freelist) {
free(page);
return NULL;
}
page->next = NULL;
page->flags = 0;
return page;
}
static small_page_v4* cold_refill_page_v4(small_heap_ctx_v4* hot_ctx, uint32_t class_idx) {
if (__builtin_expect(!v4_class_supported((int)class_idx), 0)) return NULL;
(void)hot_ctx;
if (smallsegment_v4_enabled()) {
small_segment_v4* seg = smallsegment_v4_acquire((int)class_idx);
return (small_page_v4*)smallsegment_v4_alloc_page(seg, (int)class_idx);
}
tiny_heap_ctx_t* tctx = tiny_heap_ctx_for_thread();
if (!tctx) return NULL;
tiny_heap_page_t* lease = tiny_heap_prepare_page(tctx, (int)class_idx);
if (!lease) return NULL;
return v4_page_from_lease(lease, (int)class_idx, NULL);
}
static void cold_retire_page_v4(small_heap_ctx_v4* hot_ctx, uint32_t class_idx, small_page_v4* page) {
(void)hot_ctx;
if (!page) return;
if (smallsegment_v4_enabled()) {
small_segment_v4* seg = (small_segment_v4*)page->segment;
smallsegment_v4_release_if_empty(seg, page, (int)class_idx);
return;
}
tiny_heap_ctx_t* tctx = tiny_heap_ctx_for_thread();
tiny_heap_page_t* lease = (tiny_heap_page_t*)page->slab_ref;
if (tctx && lease) {
tiny_heap_page_becomes_empty(tctx, (int)class_idx, lease);
}
free(page);
}
static const SmallColdIfaceV4 g_cold_iface_v4 = {
.refill_page = cold_refill_page_v4,
.retire_page = cold_retire_page_v4,
.remote_push = NULL,
.remote_drain = NULL,
};
const SmallColdIfaceV4* small_cold_iface_v4_get(void) {
return &g_cold_iface_v4;
}
// Direct function stubs (phase v4-mid-0: delegates to iface)
small_page_v4* small_cold_v4_refill_page(small_heap_ctx_v4* ctx, uint32_t class_idx) {
const SmallColdIfaceV4* iface = small_cold_iface_v4_get();
if (!iface || !iface->refill_page) return NULL;
return iface->refill_page(ctx, class_idx);
}
void small_cold_v4_retire_page(small_heap_ctx_v4* ctx, small_page_v4* page) {
const SmallColdIfaceV4* iface = small_cold_iface_v4_get();
if (!iface || !iface->retire_page || !page) return;
iface->retire_page(ctx, (uint32_t)page->class_idx, page);
}
bool small_cold_v4_remote_push(small_page_v4* page, void* ptr, uint32_t tid) {
const SmallColdIfaceV4* iface = small_cold_iface_v4_get();
if (!iface || !iface->remote_push) return false;
return iface->remote_push(page, ptr, tid);
}
void small_cold_v4_remote_drain(small_heap_ctx_v4* ctx) {
const SmallColdIfaceV4* iface = small_cold_iface_v4_get();
if (!iface || !iface->remote_drain) return;
iface->remote_drain(ctx);
}
// Stub accessor for smallsegment_v4_page_meta_of
small_page_v4* smallsegment_v4_page_meta_of(small_segment_v4* seg, void* ptr) {
// Phase v4-mid-0: stub only, will be implemented in later phase
// For now, return NULL (unimplemented)
(void)seg;
(void)ptr;
return NULL;
}
// -----------------------------------------------------------------------------
// alloc/free
// -----------------------------------------------------------------------------
static small_page_v4* small_alloc_slow_v4(small_heap_ctx_v4* ctx, int class_idx) {
small_class_heap_v4* h = &ctx->cls[class_idx];
const uint32_t partial_limit = v4_partial_limit(class_idx);
small_page_v4* cur = h->current;
if (cur && cur->freelist) {
return cur; // usable current
}
if (cur && !cur->freelist) {
// current をいったん partial/full に退避partial を優先)
if (h->partial_count < partial_limit) {
v4_page_push_partial(h, cur);
} else {
v4_page_push_full(h, cur);
}
h->current = NULL;
}
// partial から 1 ページだけ復帰
small_page_v4* from_partial = v4_page_pop_partial(h);
if (from_partial) {
h->current = from_partial;
return from_partial;
}
const SmallColdIfaceV4* cold = small_cold_iface_v4_get();
if (!cold || !cold->refill_page) return NULL;
small_page_v4* page = cold->refill_page(ctx, (uint32_t)class_idx);
if (!page) return NULL;
h->current = page;
return page;
}
void* small_heap_alloc_fast_v4(small_heap_ctx_v4* ctx, int class_idx) {
if (__builtin_expect(!v4_class_supported(class_idx), 0)) {
return NULL; // C5/C6/C7 以外は未対応
}
if (!small_heap_v4_class_enabled((uint8_t)class_idx)) return NULL;
small_class_heap_v4* h = &ctx->cls[class_idx];
small_page_v4* page = h->current;
if (!page || !page->freelist) {
page = small_alloc_slow_v4(ctx, class_idx);
}
if (!page || !page->freelist) {
return NULL;
}
void* blk = page->freelist;
void* next = NULL;
memcpy(&next, blk, sizeof(void*));
page->freelist = next;
page->used++;
return tiny_region_id_write_header(blk, class_idx);
}
static void v4_unlink_from_list(small_class_heap_v4* h, v4_loc_t loc, small_page_v4* prev, small_page_v4* page) {
if (!h || !page) return;
switch (loc) {
case V4_LOC_CURRENT:
h->current = NULL;
break;
case V4_LOC_PARTIAL:
if (prev) prev->next = page->next;
else h->partial_head = page->next;
if (h->partial_count > 0) {
h->partial_count--;
}
break;
case V4_LOC_FULL:
if (prev) prev->next = page->next;
else h->full_head = page->next;
break;
default:
break;
}
page->next = NULL;
}
void small_heap_free_fast_v4(small_heap_ctx_v4* ctx, int class_idx, void* ptr) {
if (__builtin_expect(!v4_class_supported(class_idx), 0)) {
return;
}
if (!small_heap_v4_class_enabled((uint8_t)class_idx)) return;
if (!ptr) return;
small_class_heap_v4* h = &ctx->cls[class_idx];
small_page_v4* prev = NULL;
v4_loc_t loc = V4_LOC_NONE;
small_page_v4* page = v4_find_page(h, (const uint8_t*)ptr, &loc, &prev);
if (!page) return;
const uint32_t partial_limit = v4_partial_limit(class_idx);
// freelist push
void* head = page->freelist;
memcpy(ptr, &head, sizeof(void*));
page->freelist = ptr;
if (page->used > 0) {
page->used--;
}
if (page->used == 0) {
const SmallColdIfaceV4* cold = small_cold_iface_v4_get();
if (loc != V4_LOC_CURRENT) {
v4_unlink_from_list(h, loc, prev, page);
}
if (!h->current) {
h->current = page;
page->next = NULL;
return;
}
if (h->current == page) {
page->next = NULL;
return;
}
if (h->partial_count < partial_limit) {
v4_page_push_partial(h, page);
return;
}
if (cold && cold->retire_page) {
cold->retire_page(ctx, (uint32_t)class_idx, page);
} else {
free(page);
}
return;
}
if (!h->current) {
// このページを current に据える
if (loc != V4_LOC_CURRENT) {
v4_unlink_from_list(h, loc, prev, page);
}
h->current = page;
page->next = NULL;
} else if (loc == V4_LOC_FULL && page->freelist) {
// full → partial に戻すcurrent があっても partial 上限までは復帰)
v4_unlink_from_list(h, loc, prev, page);
if (h->partial_count < partial_limit) {
v4_page_push_partial(h, page);
} else {
v4_page_push_full(h, page); // 上限超なら戻す
}
}
}