Phase 20.23 — Arc/RefCell in Hakorune(メモリ所有権管理)
期間: 2-3週間 ステータス: 計画更新(20.21 RCテーブル導入と整合) 前提条件: Phase 20.21(Manual Memory Management)完了
🎯 Executive Summary
Critical Strategic Decision: Arc/RefCell を C ではなく Hakorune で実装する。
理由(Why Hakorune, Not C)
| 観点 | C実装 | Hakorune実装 |
|---|---|---|
| 開発期間 | 2-3ヶ月 | 2-3週間 ⚡ |
| 複雑性 | ❌ 高(低レベル並行制御) | ✅ 低(ポリシー記述) |
| リスク | ❌ 高(メモリ安全性) | ✅ 低(境界明確) |
| 保守性 | ❌ C コード保守 | ✅ Hakorune コード保守 |
| テスト容易性 | ⚠️ 中 | ✅ 高(スクリプトレベル) |
実装パターン(GcBox を踏襲)
既存の成功事例: lang/src/runtime/gc/gc_box.hako
// Policy plane (Hakorune) - ポリシー・ロジック層
static box GcBox {
stats() {
return call("env.gc.stats/0") // Data plane呼び出し
}
collect() {
call("env.gc.collect/0") // Data plane呼び出し
}
}
// Data plane (C/Rust) - 実際のメモリ操作
// Rust: fn hako_gc_stats() -> String { /* 実メモリ走査 */ }
// Rust: fn hako_gc_collect() { /* 実GC実行 */ }
このパターンを Arc/RefCell に適用:
- Policy plane: ArcBox/RefCellBox (Hakorune) - retain/release ロジック
- Data plane: Phase 20.21 の
hako_alloc/hako_free(C) - 実メモリ操作
Time Savings(時間短縮効果)
Before (C implementation plan):
Phase 20.23: Arc/RefCell (C) 2-3 months ❌
After (Hakorune implementation):
Phase 20.23: Arc/RefCell (Hako) 2-3 weeks ✅
Savings: ~2.5 months = 10-11 weeks ⚡
🏗️ アーキテクチャ設計
Policy-Data Plane 分離(現状の足場)
┌─────────────────────────────────────────────────┐
│ Policy Plane (Hakorune) │
│ │
│ ┌────────────┐ ┌──────────────┐ │
│ │ ArcBox │ │ RefCellBox │ │
│ │ │ │ │ │
│ │ - retain() │ │ - borrow() │ │
│ │ - release()│ │ - borrow_mut │ │
│ │ - clone() │ │ - try_borrow │ │
│ └──────┬─────┘ └──────┬───────┘ │
│ │ │ │
└────────┼────────────────┼────────────────────────┘
│ │
│ call("env.arc.retain/1", ptr)
│ call("env.arc.release/1", ptr)
│ call("env.refcell.borrow/1", ptr)
▼ ▼
┌─────────────────────────────────────────────────┐
│ Data Plane (C - Phase 20.21 既存API) │
│ │
│ void* hako_alloc(size_t size) │
│ void hako_free(void* ptr) │
│ │
│ // Atomic operations (C11 標準) │
│ atomic_fetch_add(&ref_count, 1) │
│ atomic_fetch_sub(&ref_count, 1) │
└─────────────────────────────────────────────────┘
ArcBox API 設計(Hakorune Layer)
// lang/src/runtime/memory/arc_box.hako
static box ArcBox {
// Opaque handle to C memory
ptr: IntegerBox // 64-bit pointer/handle
ref_count: IntegerBox // Local cache (optional)
// Constructor: Allocate and initialize ref_count=1
birth(data_ptr) {
me.ptr = data_ptr
me.ref_count = 1
call("env.arc.init/2", me.ptr, 1) // C side初期化
}
// Increment ref count (atomic)
retain() {
local new_count = call("env.arc.retain/1", me.ptr)
me.ref_count = new_count
return new_count
}
// Decrement ref count, free if 0 (atomic)
release() {
local new_count = call("env.arc.release/1", me.ptr)
me.ref_count = new_count
if (new_count == 0) {
call("env.arc.free/1", me.ptr) // C側でhako_free呼び出し
me.ptr = 0 // Dangling pointer防止
}
return new_count
}
// Clone (increment ref_count and return new ArcBox)
clone() {
me.retain()
local new_arc = new ArcBox()
new_arc.ptr = me.ptr
new_arc.ref_count = me.ref_count
return new_arc
}
// Get raw pointer (unsafe, for C interop)
as_ptr() {
return me.ptr
}
}
RefCellBox API 設計(Hakorune Layer)
// lang/src/runtime/memory/refcell_box.hako
static box RefCellBox {
ptr: IntegerBox // Opaque handle
borrow_state: IntegerBox // 0=free, >0=shared, -1=mutable
birth(data_ptr) {
me.ptr = data_ptr
me.borrow_state = 0 // Initially free
call("env.refcell.init/2", me.ptr, 0)
}
// Shared borrow (multiple readers allowed)
borrow() {
local state = call("env.refcell.try_borrow/1", me.ptr)
if (state < 0) {
// Already mutably borrowed - panic
call("env.panic/1", "RefCellBox: already mutably borrowed")
}
me.borrow_state = state + 1 // Increment reader count
return me.ptr // Return borrowed reference
}
// Mutable borrow (exclusive)
borrow_mut() {
local state = call("env.refcell.try_borrow_mut/1", me.ptr)
if (state != 0) {
// Already borrowed - panic
call("env.panic/1", "RefCellBox: already borrowed")
}
me.borrow_state = -1 // Mark as mutably borrowed
return me.ptr
}
// Release borrow (caller must specify type)
release_borrow(is_mutable) {
if (is_mutable == 1) {
call("env.refcell.release_mut/1", me.ptr)
me.borrow_state = 0
} else {
call("env.refcell.release_shared/1", me.ptr)
me.borrow_state = me.borrow_state - 1
}
}
// Try operations (non-panicking)
try_borrow() {
local state = call("env.refcell.try_borrow/1", me.ptr)
if (state < 0) {
return null // Failed
}
me.borrow_state = state + 1
return me.ptr // Success
}
try_borrow_mut() {
local state = call("env.refcell.try_borrow_mut/1", me.ptr)
if (state != 0) {
return null // Failed
}
me.borrow_state = -1
return me.ptr // Success
}
}
🔧 C Side Minimal API(Data Plane)
Phase 20.21 既存APIに以下を追加(100行程度):
(補足)現状、include/hako_mem.h と src/abi/c/mem_libc.c は RC テーブルへ委譲済み:
src/abi/c/handle_registry.{h,c}— ハンドルの参照カウントをC11 atomicで管理hako_retain/hako_releaseは未知/二重解放で Fail‑Fast(安定メッセージ) この上に Arc/RefCell の data‑plane API を薄く足す。
// lang/src/runtime/memory/arc.c (NEW, ~50 lines)
#include <stdatomic.h>
#include <stdint.h>
#include "hakmem.h" // Phase 20.21
// Arc metadata (prepended to allocation)
typedef struct {
atomic_uint_fast64_t ref_count;
size_t size;
} ArcHeader;
// Initialize Arc (called from ArcBox.birth)
void hako_arc_init(void* ptr, uint64_t initial_count) {
ArcHeader* hdr = (ArcHeader*)ptr;
atomic_init(&hdr->ref_count, initial_count);
}
// Atomic retain (returns new count)
uint64_t hako_arc_retain(void* ptr) {
ArcHeader* hdr = (ArcHeader*)ptr;
return atomic_fetch_add(&hdr->ref_count, 1) + 1;
}
// Atomic release (returns new count)
uint64_t hako_arc_release(void* ptr) {
ArcHeader* hdr = (ArcHeader*)ptr;
uint64_t old = atomic_fetch_sub(&hdr->ref_count, 1);
return old - 1; // New count
}
// Free Arc (called when ref_count=0)
void hako_arc_free(void* ptr) {
hako_free(ptr); // Phase 20.21 API
}
// lang/src/runtime/memory/refcell.c (NEW, ~50 lines)
#include <stdatomic.h>
// RefCell metadata
typedef struct {
atomic_int_fast32_t borrow_state; // 0=free, >0=shared, -1=mutable
} RefCellHeader;
void hako_refcell_init(void* ptr, int32_t initial_state) {
RefCellHeader* hdr = (RefCellHeader*)ptr;
atomic_init(&hdr->borrow_state, initial_state);
}
// Try shared borrow (returns current state or -1 if failed)
int32_t hako_refcell_try_borrow(void* ptr) {
RefCellHeader* hdr = (RefCellHeader*)ptr;
int32_t state = atomic_load(&hdr->borrow_state);
if (state < 0) return -1; // Already mutably borrowed
atomic_fetch_add(&hdr->borrow_state, 1);
return state + 1;
}
// Try mutable borrow
int32_t hako_refcell_try_borrow_mut(void* ptr) {
RefCellHeader* hdr = (RefCellHeader*)ptr;
int32_t expected = 0;
if (atomic_compare_exchange_strong(&hdr->borrow_state, &expected, -1)) {
return 0; // Success
}
return expected; // Failed (return current state)
}
// Release shared borrow
void hako_refcell_release_shared(void* ptr) {
RefCellHeader* hdr = (RefCellHeader*)ptr;
atomic_fetch_sub(&hdr->borrow_state, 1);
}
// Release mutable borrow
void hako_refcell_release_mut(void* ptr) {
RefCellHeader* hdr = (RefCellHeader*)ptr;
atomic_store(&hdr->borrow_state, 0);
}
Total C code added: ~100 lines(Phase 20.21 の 5%)
📋 Week-by-Week Implementation Plan
Week 1: ArcBox Implementation(RCテーブル上に実装)
Day 1-2: C Data Plane
arc.c/arc.h実装(50行)- Atomic operations テスト(retain/release)
- Memory leak テスト(valgrind)
Day 3-4: Hakorune Policy Plane
lang/src/runtime/memory/arc_box.hako実装birth/retain/release/cloneメソッドcall("env.arc.*")extern binding
Day 5: Integration Testing
- スモークテスト追加(
tools/smokes/v2/suites/memory/arc_*.sh) - Multi-threaded stress test(4 threads, 10000 ops)
- Memory leak verification(valgrind clean)
Week 2: RefCellBox Implementation
Day 1-2: C Data Plane
refcell.c/refcell.h実装(50行)- Borrow state transitions テスト
- Panic case validation(double mut borrow)
Day 3-4: Hakorune Policy Plane
lang/src/runtime/memory/refcell_box.hako実装borrow/borrow_mut/release_borrowメソッドtry_borrow/try_borrow_mut実装
Day 5: Integration Testing
- Borrow check テスト(panic cases)
- Nested borrow テスト
- Shared + mutable conflict テスト
Week 3: Integration & Documentation
Day 1-2: Integration with Box System
- StringBox + Arc 統合テスト
- ArrayBox + RefCell 統合テスト
- MapBox + Arc/RefCell 統合テスト
Day 3: Performance Benchmarking
- Arc clone overhead(vs Rust
Arc::clone) - RefCell borrow overhead(vs Rust
RefCell::borrow) - Multi-threaded throughput
Day 4-5: Documentation
- API documentation(ArcBox/RefCellBox)
- Migration guide(Rust Arc/RefCell → Hakorune)
- Best practices(when to use Arc vs RefCell)
- Failure modes & error handling
✅ 受け入れ基準(Acceptance Criteria)
Functional Requirements
- ArcBox: retain/release/clone 正常動作
- RefCellBox: borrow/borrow_mut 正常動作
- Panic cases: 不正なborrow操作で適切にpanic
- Memory safety: valgrind clean(leak なし)
- Thread safety: 4 threads concurrent access で破損なし
Performance Requirements
- Arc clone: < 50ns per operation(mimalloc程度)
- RefCell borrow: < 10ns per operation(ほぼゼロコスト)
- Overhead: Rust Arc/RefCell の 2倍以内
Testing Requirements
- Unit tests: 20+ tests(C layer + Hakorune layer)
- Integration tests: 10+ Box system 統合テスト
- Smoke tests:
tools/smokes/v2/profiles/quick/追加 - Stress tests: 10000 ops, 4 threads, no crashes
Documentation Requirements
- API docs: ArcBox/RefCellBox 完全仕様
- Implementation docs: Policy-Data plane 設計図
- Migration guide: Rust → Hakorune 移行手順
- Troubleshooting: よくあるエラーと対処法
🚨 リスク分析と軽減策
Risk Matrix
| リスク | 確率 | 影響 | 軽減策 |
|---|---|---|---|
| Atomic operations バグ | 低 | 高 | C11 標準 atomic 使用、既存実装参照 |
| Memory leak | 中 | 高 | valgrind 継続検証、ref_count audit |
| Race condition | 低 | 高 | TSan(Thread Sanitizer)使用 |
| Borrow check 不完全 | 中 | 中 | Rust RefCell テストケース移植 |
| Performance 劣化 | 低 | 中 | Benchmark driven development |
Mitigation Strategies
- Continuous Testing: 毎日 valgrind + TSan 実行
- Reference Implementation: Rust std::sync::Arc/RefCell をリファレンス
- Incremental Integration: Week 1 で Arc 完成後、Week 2 で RefCell
- Early Performance Validation: Week 1 で overhead 確認、問題なければ継続
- Rollback Plan: Phase 20.21 API は変更なし、いつでも Arc/RefCell 削除可能
📊 Timeline Comparison(再掲)
Before (C Implementation - Original Plan)
Phase 20.23: Arc/RefCell (C) 2-3 months ❌
├─ Week 1-4: Arc implementation (C)
├─ Week 5-8: RefCell implementation (C)
└─ Week 9-12: Integration & testing
Risk: HIGH ❌
Complexity: HIGH ❌
After (Hakorune Implementation - This Plan)
Phase 20.23: Arc/RefCell (Hako) 2-3 weeks ✅
├─ Week 1: ArcBox (Hakorune + 50 lines C)
├─ Week 2: RefCellBox (Hakorune + 50 lines C)
└─ Week 3: Integration & docs
Risk: LOW ✅
Complexity: LOW ✅
Savings: ~2.5 months (10-11 weeks) ⚡
🔗 関連ドキュメント
- Phase 20.21: Manual Memory Management - C API 基盤
- Phase 20.22: String/Array C-ABI - Box system 連携
- Phase 20.24: Parser削除 - 次のフェーズ
- GcBox Pattern:
lang/src/runtime/gc/gc_box.hako- 実装パターン参照
💡 Key Insights(まとめ)
Why This Approach Works
- Policy-Data Separation: Hakorune はロジック、C は実メモリ操作
- Minimal C Code: 100行のみ追加(Phase 20.21 の 5%)
- Proven Pattern: GcBox で既に検証済み
- Low Risk: Hakorune レイヤーなので変更容易
- Fast Development: スクリプトレベル実装は C より 10倍速い
Strategic Advantage
User の洞察: "Arc/RefCell はHakoruneスクリプトで実装するやつじゃない?"
→ 完全に正しい! ✅
これにより:
- 2.5ヶ月の時間短縮 ⚡
- リスク大幅削減(C バグ vs Hakorune バグ)
- 保守性向上(Hakorune コードは読みやすい)
- テスト容易性向上(スクリプトレベルでテスト)
ステータス: 未開始 開始可能条件: Phase 20.21 完了 期間: 2-3週間 次フェーズ: Phase 20.24(Parser削除)