Files
hakmem/docs/analysis/TLS_SLL_ARCHITECTURE_INVESTIGATION.md
Moe Charm (CI) dc9e650db3 Tiny Pool redesign: P0.1, P0.3, P1.1, P1.2 - Out-of-band class_idx lookup
This commit implements the first phase of Tiny Pool redesign based on
ChatGPT architecture review. The goal is to eliminate Header/Next pointer
conflicts by moving class_idx lookup out-of-band (to SuperSlab metadata).

## P0.1: C0(8B) class upgraded to 16B
- Size table changed: {16,32,64,128,256,512,1024,2048} (8 classes)
- LUT updated: 1..16 → class 0, 17..32 → class 1, etc.
- tiny_next_off: C0 now uses offset 1 (header preserved)
- Eliminates edge cases for 8B allocations

## P0.3: Slab reuse guard Box (tls_slab_reuse_guard_box.h)
- New Box for draining TLS SLL before slab reuse
- ENV gate: HAKMEM_TINY_SLAB_REUSE_GUARD=1
- Prevents stale pointers when slabs are recycled
- Follows Box theory: single responsibility, minimal API

## P1.1: SuperSlab class_map addition
- Added uint8_t class_map[SLABS_PER_SUPERSLAB_MAX] to SuperSlab
- Maps slab_idx → class_idx for out-of-band lookup
- Initialized to 255 (UNASSIGNED) on SuperSlab creation
- Set correctly on slab initialization in all backends

## P1.2: Free fast path uses class_map
- ENV gate: HAKMEM_TINY_USE_CLASS_MAP=1
- Free path can now get class_idx from class_map instead of Header
- Falls back to Header read if class_map returns invalid value
- Fixed Legacy Backend dynamic slab initialization bug

## Documentation added
- HAKMEM_ARCHITECTURE_OVERVIEW.md: 4-layer architecture analysis
- TLS_SLL_ARCHITECTURE_INVESTIGATION.md: Root cause analysis
- PTR_LIFECYCLE_TRACE_AND_ROOT_CAUSE_ANALYSIS.md: Pointer tracking
- TINY_REDESIGN_CHECKLIST.md: Implementation roadmap (P0-P3)

## Test results
- Baseline: 70% success rate (30% crash - pre-existing issue)
- class_map enabled: 70% success rate (same as baseline)
- Performance: ~30.5M ops/s (unchanged)

## Next steps (P1.3, P2, P3)
- P1.3: Add meta->active for accurate TLS/freelist sync
- P2: TLS SLL redesign with Box-based counting
- P3: Complete Header out-of-band migration

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 13:42:39 +09:00

1350 lines
43 KiB
Markdown
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.

# TLS SLL アーキテクチャ改善と今後の開発方針
**調査日**: 2025-11-28
**対象**: Larson ベンチマーク double-free バグの根本原因と包括的なアーキテクチャ改善
---
## エグゼクティブサマリー
### 根本原因の特定
Larson ベンチマークでの double-free バグの根本原因は、**TLS SLL と `meta->used` の同期不整合**です:
1. **TLS SLL 操作時に `meta->used` が更新されない**
- `tls_sll_push()`: ポインタを TLS SLL に追加するが `meta->used` は変更なし
- `tls_sll_pop()`: ポインタを TLS SLL から取り出すが `meta->used` は変更なし
- Drain 時のみ: `tiny_free_local_box()` 経由で `meta->used--` される
2. **Slab 再利用時の TLS SLL クリア漏れ**
- Slab が `meta->used == 0` で empty と判定される
- しかし TLS SLL には古いポインタが残っている
- Slab が再初期化されて別のアドレス範囲で使われる
- 古いポインタが再度 allocate されて重複検出
3. **既知のバグ(既に修正済み)**
- TLS Drain pushback バグ: `tls_sll_drain_box.h:148-162` (commit c2f104618 で修正)
- ポインタが TLS SLL の 2 箇所に存在する状態を作り出していた
### 影響範囲
- **確実に影響**: Larson ベンチマーク(マルチスレッド、高頻度 alloc/free
- **潜在的影響**: 本番環境での長時間稼働時のメモリリーク・重複 free
- **性能への影響**: 現在の periodic drain (2048 frees ごと) では不十分な可能性
---
## Part 1: 現状分析
### 1.1 TLS SLL の全体構造
#### 主要ファイル
| ファイル | 役割 | 行数 |
|---------|------|------|
| `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_box.h` | TLS SLL のコア実装 (push/pop/splice) | 753行 |
| `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_drain_box.h` | Periodic drain (Option B) | 270行 |
| `/mnt/workdisk/public_share/hakmem/core/tiny_free_fast_v2.inc.h` | Fast free path (TLS SLL への push) | 370行 |
| `/mnt/workdisk/public_share/hakmem/core/box/free_local_box.c` | Slow free path (`meta->used--`) | 210行 |
#### TLS SLL の設計
```c
// TLS SLL 構造体 (hakmem_tiny.c で定義)
typedef struct {
void* head; // リストの先頭ポインタ (BASE pointer)
uint32_t count; // リスト内の要素数
} TinyTLSSLL;
extern __thread TinyTLSSLL g_tls_sll[TINY_NUM_CLASSES]; // クラスごとに1つ
```
**重要な設計原則**:
- すべてのポインタは **BASE ポインタ** (user pointer - 1 byte)
- クラス 0 と 7 は `next_offset = 0` (header を上書きしない)
- クラス 1-6 は `next_offset = 1` (header を書き戻す)
### 1.2 `meta->used` の更新箇所
#### Increment 箇所 (alloc 時)
| ファイル | 行番号 | 条件 |
|---------|-------|------|
| `tiny_superslab_alloc.inc.h` | 30, 102, 146, 323, 363, 387 | 線形 carve / freelist pop |
| `hakmem_tiny_refill_p0.inc.h` | 126, 131, 200 | バッチ refill |
| `hakmem_tiny_tls_ops.h` | 93, 136 | TLS slab refill |
| `slab_handle.h` | 313 | SlabHandle 経由の操作 |
#### Decrement 箇所 (free 時)
| ファイル | 行番号 | 条件 |
|---------|-------|------|
| `free_local_box.c` | 180 | **唯一の decrement 箇所** |
| `hakmem_tiny_bg_spill.c` | 72, 81 | バックグラウンド spill |
| `hakmem_tiny_tls_ops.h` | 198 | TLS spill |
| `slab_handle.h` | 276 | SlabHandle 経由の free |
**重要な発見**: `meta->used` の decrement は **`tiny_free_local_box()` を経由した場合のみ** 実行される。
### 1.3 ポインタのライフサイクル
```
[状態遷移図]
CARVED (meta->used++)
|
v
ALLOCATED (ユーザーが使用中)
|
v (free 呼び出し)
|
+---> [FAST PATH] TLS SLL に push (meta->used は変更なし) ----+
| |
| v
| (periodic drain)
| |
+---> [SLOW PATH] tiny_free_local_box() --------------------+
|
v
FREELIST (meta->used--, freelist に追加)
|
v
(再利用時)
|
v
ALLOCATED
```
**問題点**:
- Fast path (99% のケース) では `meta->used` が更新されない
- Periodic drain (2048 frees ごと) まで `meta->used` は高いまま
- この間、Slab は "full" に見える → 再利用されない → メモリリーク
### 1.4 Slab 再利用のコードパス
#### Empty 判定条件
```c
// ss_hot_cold_box.h:37-39
static inline bool ss_is_slab_empty(const TinySlabMeta* meta) {
return (meta->capacity > 0 && meta->used == 0);
}
```
#### Empty マーク
```c
// free_local_box.c:183-202
if (meta->used == 0) {
// Slab became EMPTY → mark for highest-priority reuse
ss_mark_slab_empty(ss, slab_idx);
fprintf(stderr, "[FREE_LOCAL_BOX] EMPTY detected: cls=%u ss=%p slab=%d\n",
cls, (void*)ss, slab_idx);
}
```
#### Slab 再利用フロー
1. `shared_pool_acquire_slab()` (hakmem_shared_pool.c:700-1300)
- **Stage 1**: EMPTY slots を優先的に再利用 (empty_mask をスキャン)
- **Stage 2**: UNUSED slots を探す
- **Stage 3**: 新しい SuperSlab を割り当て
2. `superslab_init_slab()` (初期化時)
- `meta->used = 0`
- `meta->freelist = NULL`
- `meta->class_idx = class_idx` を設定
**重要な問題**: Slab が EMPTY と判定されて再初期化される時、**TLS SLL はクリアされない**。
### 1.5 既存の診断機能
#### 環境変数ゲート
| 環境変数 | 機能 | ファイル |
|---------|------|---------|
| `HAKMEM_TINY_SLL_DIAG` | TLS SLL の詳細診断 | tls_sll_box.h:118 |
| `HAKMEM_TINY_SLL_DRAIN_ENABLE` | Periodic drain の有効/無効 | tls_sll_drain_box.h:40 |
| `HAKMEM_TINY_SLL_DRAIN_INTERVAL` | Drain 間隔 (デフォルト 2048) | tls_sll_drain_box.h:54 |
| `HAKMEM_TINY_SLL_DRAIN_DEBUG` | Drain の詳細ログ | tls_sll_drain_box.h:121 |
| `HAKMEM_TINY_SLL_SAFEHEADER` | Header 上書き防止モード | tls_sll_box.h:336 |
| `HAKMEM_TINY_LARSON_FIX` | クロススレッド free 検出 | tiny_free_fast_v2.inc.h:189 |
#### 重複チェック
```c
// tls_sll_box.h:372-402 (debug ビルドのみ)
#if !HAKMEM_BUILD_RELEASE
void* scan = g_tls_sll[class_idx].head;
uint32_t scanned = 0;
const uint32_t limit = 256; // 最大 256 要素をスキャン
while (scan && scanned < limit) {
if (scan == ptr) {
fprintf(stderr, "[TLS_SLL_PUSH_DUP] cls=%d ptr=%p ...\n");
abort(); // 重複検出時は即座に abort
}
// ...
}
#endif
```
---
## Part 2: 修正案の設計
### 2.1 即座のバグ修正(最小変更)
#### 修正 A: TLS SLL クリア on Slab Reuse
**ファイル**: `/mnt/workdisk/public_share/hakmem/core/tiny_superslab_alloc.inc.h`
**場所**: `superslab_refill()` 関数内 (行 192-265)
**問題**: Slab が再初期化される時、TLS SLL に古いポインタが残る。
**修正案**:
```c
// superslab_refill() 内 (行 243 の後に追加)
// CRITICAL FIX: Clear TLS SLL for this class on slab reuse
// Problem: When slab is recycled (EMPTY → reinitialized), old pointers
// remain in TLS SLL, causing double-free on next allocation
// Solution: Drain TLS SLL to ensure no stale pointers remain
#if !HAKMEM_BUILD_RELEASE
{
extern __thread TinyTLSSLL g_tls_sll[TINY_NUM_CLASSES];
uint32_t old_count = g_tls_sll[class_idx].count;
if (old_count > 0) {
// Drain ALL blocks from TLS SLL to freelist (slow but safe)
extern uint32_t tiny_tls_sll_drain(int class_idx, uint32_t batch_size);
uint32_t drained = tiny_tls_sll_drain(class_idx, 0); // 0 = drain all
fprintf(stderr, "[SUPERSLAB_REFILL_TLS_DRAIN] cls=%d drained=%u from TLS SLL\n",
class_idx, drained);
}
}
#endif
```
**影響**:
- **本番ビルド**: 変更なし (debug only)
- **開発ビルド**: Slab refill 時に TLS SLL を強制的に drain
- **性能**: 無視できる (refill は低頻度、O(count) の drain)
**副作用**:
- なし(防御的コード、性能への影響は最小)
---
#### 修正 B: Drain 間隔の動的調整
**ファイル**: `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_drain_box.h`
**場所**: `tls_sll_drain_get_interval()` (行 54-73)
**問題**: 現在の固定間隔 (2048 frees) では、ワークロードによっては不十分。
**修正案**:
```c
// Dynamic drain interval based on workload
static inline uint32_t tls_sll_drain_get_interval_dynamic(int class_idx) {
static uint32_t g_drain_interval_base = 2048; // デフォルト
static int g_adaptive_drain = -1;
if (__builtin_expect(g_adaptive_drain == -1, 0)) {
const char* env = getenv("HAKMEM_TINY_SLL_DRAIN_ADAPTIVE");
g_adaptive_drain = (env && *env && *env != '0') ? 1 : 0;
}
if (!g_adaptive_drain) {
return g_drain_interval_base; // 固定間隔モード
}
// Adaptive: Hot classes (0-3) drain more frequently
switch (class_idx) {
case 0: return 512; // 8B - 最頻使用
case 1: return 768; // 16B
case 2: return 768; // 32B
case 3: return 1024; // 64B
case 4: return 1536; // 128B
case 5: return 2048; // 256B
case 6: return 2048; // 512B
case 7: return 2048; // 2048B
default: return g_drain_interval_base;
}
}
```
**使い方**:
```bash
# Adaptive drain を有効化
export HAKMEM_TINY_SLL_DRAIN_ADAPTIVE=1
# または特定クラスの間隔を手動調整
export HAKMEM_TINY_SLL_DRAIN_INTERVAL=512
```
**影響**:
- **性能**: Hot classes の drain 頻度 ↑ → `meta->used` の正確性 ↑ → empty detection ↑
- **メモリ**: TLS SLL の最大サイズ ↓ → footprint ↓
---
### 2.2 同期マクロの導入
#### 設計: `TLS_SLL_PUSH_TRACKED` / `TLS_SLL_POP_TRACKED`
**目的**: TLS SLL 操作時に `meta->used` を追跡する(将来的な同期のための基盤)
**ファイル**: 新規作成 `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_tracked_box.h`
```c
#pragma once
#include "tls_sll_box.h"
#include "hakmem_tiny_superslab.h"
#include "superslab/superslab_inline.h"
// TLS_SLL_PUSH_TRACKED: Push to TLS SLL with optional meta->used tracking
//
// Args:
// cls: Size class index
// ptr: BASE pointer to push
// cap: Capacity limit
// ss: SuperSlab pointer (for meta->used tracking, can be NULL)
// slab_idx: Slab index (for meta->used tracking, can be -1)
//
// Returns: true on success, false on failure
//
// Behavior:
// - Always pushes to TLS SLL (normal path)
// - If ss != NULL && slab_idx >= 0: optionally track in meta->used (env-gated)
//
static inline bool TLS_SLL_PUSH_TRACKED(int cls, void* ptr, uint32_t cap,
SuperSlab* ss, int slab_idx) {
// Standard TLS SLL push
if (!tls_sll_push(cls, ptr, cap)) {
return false;
}
#if !HAKMEM_BUILD_RELEASE
// Optional: Track TLS SLL inventory in meta->used_tls_sll (future work)
// ENV: HAKMEM_TINY_TLS_SLL_TRACK_USED=1
static int g_track_used = -1;
if (__builtin_expect(g_track_used == -1, 0)) {
const char* env = getenv("HAKMEM_TINY_TLS_SLL_TRACK_USED");
g_track_used = (env && *env && *env != '0') ? 1 : 0;
}
if (g_track_used && ss && slab_idx >= 0 && slab_idx < ss_slabs_capacity(ss)) {
// Future: Increment ss->slabs[slab_idx].used_tls_sll
// Note: Requires adding new field to TinySlabMeta
// For now, just log (diagnostic mode)
static _Atomic uint64_t g_track_log = 0;
uint64_t n = atomic_fetch_add(&g_track_log, 1);
if (n < 10) {
fprintf(stderr, "[TLS_SLL_TRACK] PUSH cls=%d ptr=%p ss=%p slab=%d\n",
cls, ptr, (void*)ss, slab_idx);
}
}
#else
(void)ss;
(void)slab_idx;
#endif
return true;
}
// TLS_SLL_POP_TRACKED: Pop from TLS SLL with optional meta->used tracking
static inline bool TLS_SLL_POP_TRACKED(int cls, void** out,
SuperSlab** ss_out, int* slab_idx_out) {
// Standard TLS SLL pop
if (!tls_sll_pop(cls, out)) {
return false;
}
// Resolve SuperSlab/slab for caller (optional metadata)
if (ss_out && slab_idx_out) {
SuperSlab* ss = hak_super_lookup(*out);
if (ss && ss->magic == SUPERSLAB_MAGIC) {
int sidx = slab_index_for(ss, *out);
if (sidx >= 0 && sidx < ss_slabs_capacity(ss)) {
*ss_out = ss;
*slab_idx_out = sidx;
#if !HAKMEM_BUILD_RELEASE
static int g_track_used = -1;
if (__builtin_expect(g_track_used == -1, 0)) {
const char* env = getenv("HAKMEM_TINY_TLS_SLL_TRACK_USED");
g_track_used = (env && *env && *env != '0') ? 1 : 0;
}
if (g_track_used) {
// Future: Decrement ss->slabs[sidx].used_tls_sll
static _Atomic uint64_t g_track_log = 0;
uint64_t n = atomic_fetch_add(&g_track_log, 1);
if (n < 10) {
fprintf(stderr, "[TLS_SLL_TRACK] POP cls=%d ptr=%p ss=%p slab=%d\n",
cls, *out, (void*)ss, sidx);
}
}
#endif
return true;
}
}
}
return true;
}
```
**使い方**:
```c
// Before (existing code)
tls_sll_push(class_idx, base, UINT32_MAX);
// After (with tracking)
TLS_SLL_PUSH_TRACKED(class_idx, base, UINT32_MAX, ss, slab_idx);
```
**段階的導入**:
1. Phase 1: マクロ導入、ログのみ(診断用)
2. Phase 2: `TinySlabMeta.used_tls_sll` フィールド追加
3. Phase 3: `meta->used` の代わりに `meta->used + meta->used_tls_sll` で empty 判定
---
### 2.3 Slab 再利用ガードの設計
#### 設計: `tiny_slab_can_reuse()`
**目的**: Slab を再利用する前に、TLS SLL に古いポインタが残っていないことを確認
**ファイル**: 新規作成 `/mnt/workdisk/public_share/hakmem/core/box/slab_reuse_guard_box.h`
```c
#pragma once
#include "hakmem_tiny_superslab.h"
#include "hakmem_tiny_config.h"
#include <stdint.h>
#include <stdbool.h>
// ============================================================================
// Slab Reuse Guard: Prevent reuse when TLS SLL contains stale pointers
// ============================================================================
//
// Problem:
// - Slab marked as EMPTY (meta->used == 0)
// - But TLS SLL still contains pointers from this slab
// - Slab gets reinitialized for different class
// - Old pointers in TLS SLL get allocated → double-free
//
// Solution:
// - Before reusing EMPTY slab, check if TLS SLL contains pointers from it
// - If yes, force drain TLS SLL first (or skip reuse)
//
// Design:
// - Development builds: strict check + force drain
// - Production builds: best-effort check (optional, env-gated)
// Check if TLS SLL contains pointers from given slab
// Returns: number of pointers found in TLS SLL from this slab
static inline uint32_t tiny_slab_count_tls_sll_refs(SuperSlab* ss, int slab_idx, int class_idx) {
if (!ss || slab_idx < 0 || slab_idx >= ss_slabs_capacity(ss)) {
return 0;
}
extern __thread TinyTLSSLL g_tls_sll[TINY_NUM_CLASSES];
// Calculate slab address range
uint8_t* slab_base = tiny_slab_base_for(ss, slab_idx);
uintptr_t slab_start = (uintptr_t)slab_base;
uintptr_t slab_end = slab_start + SLAB_SIZE;
// Scan TLS SLL for this class
uint32_t found = 0;
void* cur = g_tls_sll[class_idx].head;
uint32_t scanned = 0;
const uint32_t limit = 1024; // Scan up to 1024 entries
while (cur && scanned < limit) {
uintptr_t addr = (uintptr_t)cur;
// Check if pointer is in this slab's range
if (addr >= slab_start && addr < slab_end) {
found++;
}
// Get next pointer
void* next = NULL;
PTR_NEXT_READ("reuse_guard_scan", class_idx, cur, 0, next);
cur = next;
scanned++;
}
return found;
}
// Guard: Check if slab can be safely reused
// Returns: true if safe to reuse, false if TLS SLL drain needed
static inline bool tiny_slab_can_reuse(SuperSlab* ss, int slab_idx, int class_idx) {
#if !HAKMEM_BUILD_RELEASE
// Development: Always check for safety
uint32_t refs = tiny_slab_count_tls_sll_refs(ss, slab_idx, class_idx);
if (refs > 0) {
fprintf(stderr,
"[SLAB_REUSE_GUARD] BLOCKED: cls=%d ss=%p slab=%d has %u refs in TLS SLL\n",
class_idx, (void*)ss, slab_idx, refs);
// Force drain TLS SLL to clear stale pointers
extern uint32_t tiny_tls_sll_drain(int class_idx, uint32_t batch_size);
uint32_t drained = tiny_tls_sll_drain(class_idx, 0); // Drain all
fprintf(stderr,
"[SLAB_REUSE_GUARD] Forced drain: cls=%d drained=%u blocks\n",
class_idx, drained);
// Re-check after drain
refs = tiny_slab_count_tls_sll_refs(ss, slab_idx, class_idx);
if (refs > 0) {
fprintf(stderr,
"[SLAB_REUSE_GUARD] CRITICAL: Still %u refs after drain! (memory corruption?)\n",
refs);
return false; // Unsafe to reuse
}
}
return true; // Safe to reuse
#else
// Production: Optional check (env-gated for minimal overhead)
static int g_reuse_guard = -1;
if (__builtin_expect(g_reuse_guard == -1, 0)) {
const char* env = getenv("HAKMEM_TINY_SLAB_REUSE_GUARD");
g_reuse_guard = (env && *env && *env != '0') ? 1 : 0;
}
if (!g_reuse_guard) {
return true; // Guard disabled in production
}
// Same logic as development build
uint32_t refs = tiny_slab_count_tls_sll_refs(ss, slab_idx, class_idx);
if (refs > 0) {
// Production: Just skip reuse (don't force drain to avoid latency spike)
return false;
}
return true;
#endif
}
```
**統合場所**: `hakmem_shared_pool.c``shared_pool_acquire_slab()`
```c
// shared_pool_acquire_slab() 内 (Stage 1: EMPTY slot reuse の前)
// Before reusing EMPTY slot, check TLS SLL safety
if (!tiny_slab_can_reuse(ss, slot_idx, class_idx)) {
// Skip this EMPTY slot (TLS SLL drain needed first)
fprintf(stderr, "[ACQUIRE_SLAB] Skipped EMPTY slot due to TLS SLL refs\n");
continue; // Try next slot
}
```
**影響**:
- **開発ビルド**: 常に有効、TLS SLL の自動 drain 付き
- **本番ビルド**: ENV で有効化可能、drain なし(スキップのみ)
- **性能**: 開発ビルドで O(TLS_SLL_count) のスキャン、本番では無視できる
---
## Part 3: アーキテクチャ改善
### 3.1 ポインタライフサイクル管理
#### 状態遷移図(改善版)
```
┌─────────────────────────────────────────────────────────────┐
│ Pointer Lifecycle States │
└─────────────────────────────────────────────────────────────┘
[UNCARVED] (Slab 初期化直後、meta->used < meta->capacity)
|
| superslab_alloc_from_slab() - Linear carve
v
[CARVED] (meta->used++, user に返す直前)
|
| return block to user
v
[ALLOCATED] (ユーザーコードが使用中)
|
| user calls free(ptr)
v
+---> [FAST FREE PATH 99%] ----+ +---> [SLOW FREE PATH 1%]
| | |
| hak_tiny_free_fast_v2() | | hak_tiny_free()
| - Header から class_idx 読取 | | - SuperSlab lookup
| - tls_sll_push(ptr) | | - Ownership 確認
| (meta->used 不変!) | |
| | |
v | v
[IN_TLS_SLL] | [FREELIST]
| - TLS SLL に格納 | | - tiny_free_local_box()
| - meta->used は高いまま | | - freelist に push
| - periodic drain を待つ | | - meta->used-- ★
| | |
| (2048 frees 後) | |
v | |
[DRAINING] | |
| tiny_tls_sll_drain() | |
| - TLS SLL から pop +<----+
| - tiny_free_local_box() 呼出
| - meta->used-- ★
v
[FREELIST] (meta->freelist に連結、meta->used 正確)
|
| (meta->used == 0 なら)
v
[EMPTY] (ss_mark_slab_empty())
|
| shared_pool_acquire_slab() - Stage 1
| → tiny_slab_can_reuse() ガード ★
v
[REUSABLE] (再初期化可能)
|
| superslab_init_slab()
v
[UNCARVED] (ループ)
```
**重要な Invariants**:
1. **Meta->used の正確性**
```c
// Invariant 1: meta->used reflects allocated + in_freelist
// (TLS SLL 内のポインタは含まれない!)
meta->used == allocated_count + freelist_count // 不正確
// Correct invariant (with TLS SLL):
meta->used == allocated_count + freelist_count + tls_sll_count
```
2. **Empty 判定の条件**
```c
// Current (WRONG):
is_empty = (meta->used == 0); // TLS SLL に残っている可能性
// Fixed (with guard):
is_empty = (meta->used == 0) && !tiny_slab_has_tls_sll_refs(ss, slab_idx, cls);
```
3. **TLS SLL のクリーン状態**
```c
// Invariant 3: TLS SLL must be drained before slab reuse
// Enforced by: tiny_slab_can_reuse() guard
```
### 3.2 コード構造の改善
#### 責任の分離
| モジュール | 責任 | 主要 API |
|-----------|------|---------|
| **TLS SLL Box** | TLS 単方向リスト管理 | `tls_sll_push/pop/splice` |
| **TLS SLL Drain Box** | Periodic drain、meta->used 同期 | `tiny_tls_sll_drain/try_drain` |
| **Slab Meta Box** | Slab メタデータ管理 | `ss_slab_meta_*` |
| **Slab Reuse Guard Box** | 再利用安全性チェック | `tiny_slab_can_reuse` |
| **Free Local Box** | Freelist 管理、meta->used 更新 | `tiny_free_local_box` |
| **Shared Pool** | SuperSlab 割り当て、slot 管理 | `shared_pool_acquire/release_slab` |
#### ファイル構成の提案
```
core/
├── box/
│ ├── tls_sll_box.h (既存) Push/Pop/Splice
│ ├── tls_sll_drain_box.h (既存) Periodic drain
│ ├── tls_sll_tracked_box.h (新規) Tracked push/pop
│ ├── slab_reuse_guard_box.h (新規) Reuse safety guard
│ ├── free_local_box.{h,c} (既存) Freelist + meta->used--
│ ├── ss_slab_meta_box.h (既存) Meta accessors
│ └── ss_hot_cold_box.h (既存) Hot/Cold/Empty marking
├── hakmem_shared_pool.{h,c} (既存) Shared pool
├── tiny_superslab_alloc.inc.h (既存) Alloc paths
└── tiny_free_fast_v2.inc.h (既存) Fast free path
```
### 3.3 デバッグ・診断機能の強化
#### 新規追加すべき診断機能
##### A. TLS SLL 在庫トラッキング
**ENV**: `HAKMEM_TINY_TLS_SLL_INVENTORY=1`
```c
// Per-class TLS SLL inventory statistics (TLS)
static __thread struct {
uint64_t total_push; // 累計 push 回数
uint64_t total_pop; // 累計 pop 回数
uint64_t total_drained; // 累計 drain 回数
uint32_t peak_count; // 最大 count
uint32_t current_count; // 現在の count
} g_tls_sll_stats[TINY_NUM_CLASSES];
// Destructor で出力
static void tls_sll_inventory_report(void) __attribute__((destructor));
```
##### B. Slab 再利用ヒストリー
**ENV**: `HAKMEM_TINY_SLAB_REUSE_HISTORY=1`
```c
// Per-slab reuse history (circular buffer)
#define REUSE_HISTORY_SIZE 16
typedef struct {
uint64_t timestamp_ns; // 再利用時刻
uint8_t prev_class_idx; // 前のクラス
uint8_t new_class_idx; // 新しいクラス
uint16_t tls_sll_refs; // 再利用時の TLS SLL 参照数
} SlabReuseEvent;
// Per SuperSlab
SlabReuseEvent reuse_history[SLABS_PER_SUPERSLAB_MAX][REUSE_HISTORY_SIZE];
uint32_t reuse_index[SLABS_PER_SUPERSLAB_MAX];
```
##### C. Meta->used 整合性チェック
**ENV**: `HAKMEM_TINY_META_USED_VERIFY=1`
```c
// Periodic verification: count actual allocations vs meta->used
// (Expensive, debug only)
static void verify_meta_used_consistency(SuperSlab* ss, int slab_idx) {
TinySlabMeta* meta = &ss->slabs[slab_idx];
// Count freelist entries
uint32_t freelist_count = 0;
void* cur = meta->freelist;
while (cur && freelist_count < meta->capacity) {
freelist_count++;
cur = tiny_next_read(meta->class_idx, cur);
}
// Expected: meta->used + freelist_count <= meta->capacity
if (meta->used + freelist_count > meta->capacity) {
fprintf(stderr, "[META_USED_VERIFY] CORRUPTION: used=%u freelist=%u cap=%u\n",
meta->used, freelist_count, meta->capacity);
abort();
}
}
```
#### 性能への影響を最小化
**戦略**:
1. **本番ビルドでは完全無効化**: `#if !HAKMEM_BUILD_RELEASE` でガード
2. **ENV ゲート**: デフォルト OFF、必要時のみ有効化
3. **サンプリング**: 全操作ではなく N 回に 1 回だけチェック
4. **TLS カウンタ**: Atomic 操作を避ける
### 3.4 ドキュメント化
#### MEMORY_LIFECYCLE.md の内容提案
```markdown
# メモリライフサイクル設計書
## 概要
HAKMEM の Tiny Pool におけるポインタのライフサイクルと状態遷移を定義。
## 状態定義
### UNCARVED
- **定義**: Slab 初期化直後、まだ carve されていない領域
- **条件**: `meta->used < meta->capacity`
- **次状態**: CARVED (alloc 時)
### CARVED
- **定義**: Slab から carve され、ユーザーに返す直前
- **操作**: `meta->used++`
- **次状態**: ALLOCATED
### ALLOCATED
- **定義**: ユーザーコードが使用中
- **Invariant**: ポインタはユーザー空間にあり、内部リストには未登録
- **次状態**: IN_TLS_SLL (fast free) or FREELIST (slow free)
### IN_TLS_SLL
- **定義**: TLS SLL に push された、drain 待ち
- **Invariant**:
- `g_tls_sll[class_idx]` のリスト内に存在
- `meta->used` は変更なし(高いまま)
- Periodic drain まで freelist に戻らない
- **次状態**: DRAINING (periodic drain)
### DRAINING
- **定義**: TLS SLL から freelist に移動中
- **操作**:
1. `tls_sll_pop()`
2. `tiny_free_local_box()` → `meta->used--`
3. Freelist に push
- **次状態**: FREELIST
### FREELIST
- **定義**: Slab の freelist に連結
- **Invariant**:
- `meta->freelist` のリスト内に存在
- `meta->used` は正確decrement 済み)
- **次状態**: ALLOCATED (再利用) or EMPTY (すべて free)
### EMPTY
- **定義**: Slab 内のすべてのブロックが free
- **条件**: `meta->used == 0`
- **マーク**: `ss_mark_slab_empty(ss, slab_idx)`
- **次状態**: REUSABLE (再利用準備)
### REUSABLE
- **定義**: 別のクラスで再初期化可能
- **ガード**: `tiny_slab_can_reuse()` でチェック
- **操作**: `superslab_init_slab()`
- **次状態**: UNCARVED
## 状態遷移の Invariants
### Invariant 1: Meta->used の意味
```c
// WRONG (current):
meta->used == allocated_count + freelist_count
// CORRECT (with TLS SLL):
meta->used == allocated_count + freelist_count + tls_sll_count
```
**問題**: TLS SLL 内のポインタは `meta->used` に反映されない。
### Invariant 2: Empty 判定
```c
// WRONG:
is_empty = (meta->used == 0);
// CORRECT:
is_empty = (meta->used == 0) &&
!tiny_slab_has_tls_sll_refs(ss, slab_idx, class_idx);
```
### Invariant 3: Slab 再利用前の TLS SLL クリーン
```c
// Before reuse:
tiny_slab_can_reuse(ss, slab_idx, class_idx) == true
⇒ TLS SLL に当該 slab のポインタが存在しない
```
## デバッグツール
- `HAKMEM_TINY_TLS_SLL_DIAG=1`: TLS SLL 診断
- `HAKMEM_TINY_SLAB_REUSE_GUARD=1`: 再利用ガード(本番)
- `HAKMEM_TINY_META_USED_VERIFY=1`: Meta->used 検証(開発)
## 参考文献
- `/mnt/workdisk/public_share/hakmem/docs/analysis/LARSON_DOUBLE_FREE_INVESTIGATION.md`
- `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_box.h`
- `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_drain_box.h`
```
### 3.5 テスト戦略
#### 単体テスト
**ファイル**: 新規作成 `tests/test_tls_sll_lifecycle.c`
```c
// Test 1: TLS SLL push/pop/drain cycle
void test_tls_sll_basic_cycle(void) {
int class_idx = 1; // 16B
void* ptr1 = allocate_test_block(class_idx);
void* ptr2 = allocate_test_block(class_idx);
// Push to TLS SLL
assert(tls_sll_push(class_idx, ptr1, 1000));
assert(g_tls_sll[class_idx].count == 1);
// Pop from TLS SLL
void* out;
assert(tls_sll_pop(class_idx, &out));
assert(out == ptr1);
assert(g_tls_sll[class_idx].count == 0);
// Drain empty list (no-op)
uint32_t drained = tiny_tls_sll_drain(class_idx, 0);
assert(drained == 0);
}
// Test 2: Slab reuse guard
void test_slab_reuse_guard(void) {
int class_idx = 2; // 32B
SuperSlab* ss = get_test_superslab();
int slab_idx = 0;
// Allocate and free blocks (push to TLS SLL)
for (int i = 0; i < 10; i++) {
void* ptr = allocate_from_slab(ss, slab_idx);
tls_sll_push(class_idx, ptr, 1000);
}
// Manually mark slab as empty (simulate meta->used == 0)
ss->slabs[slab_idx].used = 0;
ss_mark_slab_empty(ss, slab_idx);
// Reuse guard should detect TLS SLL refs
assert(!tiny_slab_can_reuse(ss, slab_idx, class_idx));
// Drain TLS SLL
tiny_tls_sll_drain(class_idx, 0);
// Now reuse should be safe
assert(tiny_slab_can_reuse(ss, slab_idx, class_idx));
}
// Test 3: Meta->used consistency after drain
void test_meta_used_consistency(void) {
// TODO: Implement
}
```
#### 統合テスト
**ベンチマーク**: Larson + 拡張
```bash
# 現在の Larson (MT stress test)
./mimalloc-bench/out/bench/larson 10 5 200000
# 追加テスト: Slab 再利用を強制
export HAKMEM_TINY_SLL_DRAIN_INTERVAL=100 # 頻繁に drain
export HAKMEM_TINY_SLAB_REUSE_GUARD=1 # ガード有効
./mimalloc-bench/out/bench/larson 10 5 500000
# 診断モード
export HAKMEM_TINY_SLL_DIAG=1
export HAKMEM_TINY_SLAB_REUSE_HISTORY=1
./mimalloc-bench/out/bench/larson 10 5 100000 2>larson_diag.log
```
#### Fuzzing
**ツール**: AFL++ or libFuzzer
```c
// Fuzzing harness for TLS SLL operations
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 4) return 0;
int class_idx = data[0] % TINY_NUM_CLASSES;
uint8_t op = data[1];
switch (op % 4) {
case 0: { // Push
void* ptr = allocate_test_block(class_idx);
tls_sll_push(class_idx, ptr, 1000);
break;
}
case 1: { // Pop
void* out;
tls_sll_pop(class_idx, &out);
break;
}
case 2: { // Drain
tiny_tls_sll_drain(class_idx, data[2]);
break;
}
case 3: { // Slab reuse
// Simulate slab reuse
break;
}
}
return 0;
}
```
---
## Part 4: リファクタリング計画
### 段階的な改善ステップ
#### Phase 1: 即座の修正1-2 日)
**目標**: Larson double-free バグを修正
| ステップ | ファイル | 変更内容 | 工数 |
|---------|---------|---------|------|
| 1.1 | `tiny_superslab_alloc.inc.h` | Slab refill 時の TLS SLL drain | 小 |
| 1.2 | `tls_sll_drain_box.h` | Adaptive drain interval | 小 |
| 1.3 | テスト | Larson 100 runs → 0% crash | 中 |
**期待される結果**:
- ✅ Larson ベンチマークで double-free が発生しない
- ✅ 既存の性能を維持(変更は診断コードのみ)
#### Phase 2: ガード機構の導入3-5 日)
**目標**: 再発防止のための予防的チェック
| ステップ | ファイル | 変更内容 | 工数 |
|---------|---------|---------|------|
| 2.1 | `box/slab_reuse_guard_box.h` | Reuse guard 実装 | 中 |
| 2.2 | `hakmem_shared_pool.c` | Guard 統合 | 小 |
| 2.3 | テスト | 単体テスト作成 | 中 |
**期待される結果**:
- ✅ Slab 再利用時に TLS SLL の残留を検出
- ✅ 開発ビルドで自動 drain、本番ビルドで ENV ゲート
#### Phase 3: 診断機能の強化5-7 日)
**目標**: デバッグ効率の向上
| ステップ | ファイル | 変更内容 | 工数 |
|---------|---------|---------|------|
| 3.1 | `box/tls_sll_tracked_box.h` | Tracked push/pop マクロ | 中 |
| 3.2 | 診断統計 | TLS SLL inventory tracking | 小 |
| 3.3 | 診断統計 | Slab reuse history | 中 |
| 3.4 | ドキュメント | `MEMORY_LIFECYCLE.md` 作成 | 中 |
**期待される結果**:
- ✅ TLS SLL の挙動を可視化
- ✅ Slab 再利用のヒストリーを追跡
- ✅ 将来的な meta->used 同期の準備
#### Phase 4: アーキテクチャ改善14-21 日)
**目標**: 根本的な解決meta->used の正確性)
| ステップ | ファイル | 変更内容 | 工数 |
|---------|---------|---------|------|
| 4.1 | `superslab_types.h` | `TinySlabMeta.used_tls_sll` フィールド追加 | 小 |
| 4.2 | `box/tls_sll_tracked_box.h` | `used_tls_sll` の increment/decrement | 中 |
| 4.3 | `box/ss_hot_cold_box.h` | Empty 判定を `used + used_tls_sll` に変更 | 小 |
| 4.4 | 全体統合 | Tracked マクロを全箇所に適用 | 大 |
| 4.5 | テスト | 統合テスト、Larson 長時間実行 | 中 |
**期待される結果**:
- ✅ `meta->used` が常に正確
- ✅ TLS SLL 内のポインタも追跡
- ✅ Empty 判定が正しく動作
### 優先順位の提案
| フェーズ | 緊急度 | 重要度 | 推奨順序 |
|---------|-------|-------|---------|
| Phase 1 | 🔴 高 | 🔴 高 | 1位即座に実施 |
| Phase 2 | 🟡 中 | 🔴 高 | 2位Phase 1 の後) |
| Phase 3 | 🟢 低 | 🟡 中 | 3位並行可能 |
| Phase 4 | 🟢 低 | 🔴 高 | 4位長期計画 |
---
## Part 5: 設計ドキュメント草案
### ポインタライフサイクル図
```
┌───────────────────────────────────────────────────────────────────┐
│ HAKMEM Tiny Pool - Pointer Lifecycle │
└───────────────────────────────────────────────────────────────────┘
States:
UNCARVED → CARVED → ALLOCATED → IN_TLS_SLL → DRAINING → FREELIST → EMPTY → REUSABLE
Critical Paths:
1. Alloc (Fast): UNCARVED → CARVED → ALLOCATED
2. Free (Fast): ALLOCATED → IN_TLS_SLL
3. Free (Slow): ALLOCATED → FREELIST
4. Drain: IN_TLS_SLL → DRAINING → FREELIST
5. Reuse: EMPTY → REUSABLE → UNCARVED
Invariants:
I1: meta->used = allocated + freelist + tls_sll (total blocks not in UNCARVED)
I2: is_empty ⇔ (meta->used == 0) ∧ (tls_sll_refs == 0)
I3: reusable ⇔ is_empty ∧ tiny_slab_can_reuse() == true
Guards:
G1: tiny_slab_can_reuse() - Prevents reuse when TLS SLL has refs
G2: tls_sll_push() duplicate check - Prevents double-free in TLS SLL
G3: tiny_free_local_box() - Only path that decrements meta->used
Synchronization Points:
S1: Periodic drain (every N frees) - Syncs TLS SLL → freelist
S2: Slab refill - Forces drain if TLS SLL has refs
S3: Slab reuse - Blocks if TLS SLL has refs
```
### 不変条件リスト
#### I1: Meta->used の正確性
```c
// Definition:
// meta->used counts all blocks that have been carved but not returned to freelist
// INCLUDING blocks in TLS SLL (not yet drained)
meta->used == SUM(state == ALLOCATED || state == IN_TLS_SLL || state == FREELIST)
// Corollary:
// Empty detection requires checking BOTH meta->used AND TLS SLL
is_empty(slab) ⟺ (meta->used == 0) ∧ (tls_sll_count(slab) == 0)
```
#### I2: TLS SLL の一貫性
```c
// Definition:
// TLS SLL contains only pointers that:
// 1. Were allocated from this allocator
// 2. Have valid headers (magic + class_idx)
// 3. Are still in ALLOCATED or IN_TLS_SLL state
∀ptr ∈ TLS_SLL[class]:
- ptr is BASE pointer (user - 1)
- header[ptr].magic == HEADER_MAGIC
- header[ptr].class_idx == class
- state[ptr] == IN_TLS_SLL
```
#### I3: Slab 再利用の安全性
```c
// Definition:
// Slab can be reused only if:
// 1. meta->used == 0 (all blocks returned)
// 2. No TLS SLL contains pointers from this slab
// 3. Remote queues are drained
tiny_slab_can_reuse(ss, slab_idx, class) ⟺
(meta->used == 0) ∧
(tls_sll_count_refs(ss, slab_idx, class) == 0) ∧
(remote_count[slab_idx] == 0)
```
#### I4: Drain の冪等性
```c
// Definition:
// Draining an empty TLS SLL is a no-op
TLS_SLL[class].count == 0 ⟹ tiny_tls_sll_drain(class, N) == 0
```
### API 契約の明文化
#### `tls_sll_push(class_idx, ptr, capacity)`
**事前条件**:
- `0 <= class_idx < TINY_NUM_CLASSES`
- `ptr` は BASE ポインタuser - 1
- `ptr` は現在 TLS SLL に含まれていない(重複不可)
- `capacity > 0`
**事後条件**:
- 成功時: `TLS_SLL[class].count++`, `ptr` がリストの先頭
- 失敗時: 状態変更なし
- **重要**: `meta->used` は変更されない
**副作用**:
- Header 書き戻し(クラス 1-6 のみ)
- 重複チェックdebug ビルドのみ、先頭 256 要素)
---
#### `tls_sll_pop(class_idx, *out)`
**事前条件**:
- `0 <= class_idx < TINY_NUM_CLASSES`
- `out != NULL`
**事後条件**:
- 成功時: `TLS_SLL[class].count--`, `*out` に BASE ポインタ
- 失敗時: `return false`, `*out` は未定義
- **重要**: `meta->used` は変更されない
**副作用**:
- Next ポインタのクリア(リスト切り離し)
- Header 検証debug ビルド)
---
#### `tiny_tls_sll_drain(class_idx, batch_size)`
**事前条件**:
- `0 <= class_idx < TINY_NUM_CLASSES`
- `batch_size == 0` → すべて drain
**事後条件**:
- `N = min(batch_size, TLS_SLL[class].count)` 個の要素を drain
- 各要素に対して `tiny_free_local_box()` 呼び出し
- **重要**: `meta->used -= N`freelist 経由)
**副作用**:
- Freelist への追加
- `meta->used` の decrement
- Empty マーク(`meta->used == 0` の場合)
---
#### `tiny_slab_can_reuse(ss, slab_idx, class_idx)`
**事前条件**:
- `ss` は有効な SuperSlab
- `0 <= slab_idx < ss_slabs_capacity(ss)`
- `meta->used == 0`caller が確認済み)
**事後条件**:
- 成功時: `return true`, TLS SLL に refs なし
- 失敗時: `return false`, TLS SLL に refs ありdrain 必要)
**副作用** (debug ビルドのみ):
- TLS SLL の自動 drainrefs が見つかった場合)
- ログ出力
---
## 付録 A: 主要ファイルのサマリー
### `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_box.h`
**役割**: TLS SLL のコア実装push/pop/splice
**主要関数**:
- `tls_sll_push_impl()`: 要素を TLS SLL に追加(重複チェック付き)
- `tls_sll_pop_impl()`: 要素を TLS SLL から取り出し
- `tls_sll_splice()`: チェーンを一括で TLS SLL に統合
**重要な設計**:
- すべて BASE ポインタで動作
- Header の読み書き(クラス 1-6
- 重複検出debug ビルド、先頭 256 要素)
---
### `/mnt/workdisk/public_share/hakmem/core/box/tls_sll_drain_box.h`
**役割**: Periodic drainOption Bの実装
**主要関数**:
- `tiny_tls_sll_drain()`: TLS SLL から freelist に移動
- `tiny_tls_sll_try_drain()`: Counter ベースの自動 drain
**設計**:
- デフォルト間隔: 2048 frees
- ENV: `HAKMEM_TINY_SLL_DRAIN_INTERVAL` で調整可能
- **重要**: `tiny_free_local_box()` 経由で `meta->used--`
---
### `/mnt/workdisk/public_share/hakmem/core/box/free_local_box.c`
**役割**: Freelist への追加 + `meta->used` の decrement
**主要関数**:
- `tiny_free_local_box()`: **唯一の `meta->used--` 実行箇所**
**フロー**:
1. BASE ポインタ計算user - 1
2. Freelist に push
3. **`meta->used--`** ← ここが重要
4. `meta->used == 0` なら `ss_mark_slab_empty()`
---
### `/mnt/workdisk/public_share/hakmem/core/tiny_free_fast_v2.inc.h`
**役割**: Fast free pathHeader ベース)
**主要関数**:
- `hak_tiny_free_fast_v2()`: Header から class 読み取り → TLS SLL に push
**フロー**:
1. Header から `class_idx` 読み取り2-3 cycles
2. `tls_sll_push(class_idx, base, UINT32_MAX)`
3. Periodic drain トリガー2048 frees ごと)
**重要**: `meta->used` は変更しないdrain 時に初めて更新)
---
### `/mnt/workdisk/public_share/hakmem/core/hakmem_shared_pool.c`
**役割**: SuperSlab の割り当てと slot 管理
**主要関数**:
- `shared_pool_acquire_slab()`: 3-stage allocation
- Stage 1: EMPTY slots最優先
- Stage 2: UNUSED slots
- Stage 3: 新規 SuperSlab
**問題箇所**: Stage 1 で EMPTY slot を再利用する時、TLS SLL のチェックがない
---
## 付録 B: 環境変数リファレンス
| 環境変数 | デフォルト | 説明 | ファイル |
|---------|-----------|------|---------|
| `HAKMEM_TINY_SLL_DIAG` | 0 | TLS SLL 詳細診断 | tls_sll_box.h:118 |
| `HAKMEM_TINY_SLL_DRAIN_ENABLE` | 1 | Periodic drain 有効化 | tls_sll_drain_box.h:40 |
| `HAKMEM_TINY_SLL_DRAIN_INTERVAL` | 2048 | Drain 間隔frees 単位) | tls_sll_drain_box.h:54 |
| `HAKMEM_TINY_SLL_DRAIN_DEBUG` | 0 | Drain 詳細ログ | tls_sll_drain_box.h:121 |
| `HAKMEM_TINY_SLL_DRAIN_ADAPTIVE` | 0 | Class ごとの adaptive 間隔 | (新規提案) |
| `HAKMEM_TINY_SLL_SAFEHEADER` | 0 | Header 上書き防止 | tls_sll_box.h:336 |
| `HAKMEM_TINY_LARSON_FIX` | 0 | Cross-thread free 検出 | tiny_free_fast_v2.inc.h:189 |
| `HAKMEM_TINY_SLAB_REUSE_GUARD` | 0 (本番) | Slab 再利用ガード | (新規提案) |
| `HAKMEM_TINY_TLS_SLL_TRACK_USED` | 0 | TLS SLL inventory 追跡 | (新規提案) |
| `HAKMEM_TINY_META_USED_VERIFY` | 0 | Meta->used 整合性検証 | (新規提案) |
---
## まとめ
### 即座に実施すべき修正
1. **Slab refill 時の TLS SLL drain** (`tiny_superslab_alloc.inc.h`)
- 工数: 小1-2 時間)
- 影響: Larson double-free の根本修正
2. **Adaptive drain interval** (`tls_sll_drain_box.h`)
- 工数: 小2-3 時間)
- 影響: Hot classes の drain 頻度 ↑ → `meta->used` 正確性 ↑
### 中期的な改善
3. **Slab reuse guard** (`box/slab_reuse_guard_box.h`)
- 工数: 中1-2 日)
- 影響: 再発防止の予防的チェック
4. **診断機能の強化**
- 工数: 中3-5 日)
- 影響: デバッグ効率の向上
### 長期的なアーキテクチャ改善
5. **Meta->used の正確な同期** (`TinySlabMeta.used_tls_sll`)
- 工数: 大14-21 日)
- 影響: 根本的な解決、将来的な問題の予防
---
**END OF REPORT**