Phase 1 完了:環境変数整理 + fprintf デバッグガード ENV変数削除(BG/HotMag系): - core/hakmem_tiny_init.inc: HotMag ENV 削除 (~131 lines) - core/hakmem_tiny_bg_spill.c: BG spill ENV 削除 - core/tiny_refill.h: BG remote 固定値化 - core/hakmem_tiny_slow.inc: BG refs 削除 fprintf Debug Guards (#if !HAKMEM_BUILD_RELEASE): - core/hakmem_shared_pool.c: Lock stats (~18 fprintf) - core/page_arena.c: Init/Shutdown/Stats (~27 fprintf) - core/hakmem.c: SIGSEGV init message ドキュメント整理: - 328 markdown files 削除(旧レポート・重複docs) 性能確認: - Larson: 52.35M ops/s (前回52.8M、安定動作✅) - ENV整理による機能影響なし - Debug出力は一部残存(次phase で対応) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
523 lines
19 KiB
Markdown
523 lines
19 KiB
Markdown
# Box Theory 残り境界の徹底検証レポート
|
||
|
||
## 調査概要
|
||
HAKMEM tiny allocator の Box Theory(箱理論)における 3つの残り境界(Box 3, 2, 4)の詳細検証結果。
|
||
|
||
検証対象ファイル:
|
||
- core/hakmem_tiny_free.inc (メイン free ロジック)
|
||
- core/slab_handle.h (所有権管理)
|
||
- core/tiny_publish.c (publish 実装)
|
||
- core/tiny_mailbox.c (mailbox 実装)
|
||
- core/tiny_remote.c (remote queue 操作)
|
||
- core/hakmem_tiny_superslab.h (owner/drain 実装)
|
||
- core/hakmem_tiny.c (publish/adopt 実装)
|
||
|
||
---
|
||
|
||
## Box 3: Same-thread Freelist Push 検証
|
||
|
||
### 不変条件
|
||
**freelist への push は `owner_tid == my_tid` の時のみ**
|
||
|
||
### 検証結果
|
||
|
||
#### ✅ 問題なし: slab_handle.h の slab_freelist_push()
|
||
```c
|
||
// core/slab_handle.h:205-236
|
||
static inline int slab_freelist_push(SlabHandle* h, void* ptr) {
|
||
if (!h || !h->valid) {
|
||
return 0; // Box: No ownership → FAIL
|
||
}
|
||
// ...
|
||
// Ownership guaranteed by valid==1 → safe to modify freelist
|
||
*(void**)ptr = h->meta->freelist;
|
||
h->meta->freelist = ptr;
|
||
// ...
|
||
return 1;
|
||
}
|
||
```
|
||
✓ 所有権チェック(valid==1)を確認後のみ freelist 操作
|
||
✓ 直接 freelist push の唯一の安全な入口
|
||
|
||
#### ✅ 問題なし: hakmem_tiny_free.inc の same-thread freelist push
|
||
```c
|
||
// core/hakmem_tiny_free.inc:1044-1076
|
||
if (!g_tiny_force_remote && meta->owner_tid != 0 && meta->owner_tid == my_tid) {
|
||
// Fast path: Direct freelist push (same-thread)
|
||
// ...
|
||
if (!tiny_remote_guard_allow_local_push(ss, slab_idx, meta, ptr, "local_free", my_tid)) {
|
||
// Fall back to remote if guard fails
|
||
int transitioned = ss_remote_push(ss, slab_idx, ptr);
|
||
// ...
|
||
return;
|
||
}
|
||
void* prev = meta->freelist;
|
||
*(void**)ptr = prev;
|
||
meta->freelist = ptr; // ← Safe freelist push
|
||
// ...
|
||
}
|
||
```
|
||
✓ owner_tid == my_tid の厳密なチェック
|
||
✓ guard check で追加の安全性確保
|
||
✓ owner_tid != my_tid の場合は確実に remote_push へ
|
||
|
||
#### ✅ 問題なし: publish 時の owner_tid リセット
|
||
```c
|
||
// core/hakmem_tiny.c:639-670 (ss_partial_publish)
|
||
for (int s = 0; s < cap_pub; s++) {
|
||
uint32_t prev = __atomic_exchange_n(&ss->slabs[s].owner_tid, 0u, __ATOMIC_RELEASE);
|
||
// ...記録のみ...
|
||
}
|
||
```
|
||
✓ publish 時に明示的に owner_tid=0 をセット
|
||
✓ ATOMIC_RELEASE で memory barrier 確保
|
||
|
||
**Box 3 評価: ✅ PASS - 境界は堅牢。直接 freelist push は所有権ガード完全。**
|
||
|
||
---
|
||
|
||
## Box 2: Remote Push の重複(dup_push)検証
|
||
|
||
### 不変条件
|
||
**同じノードを remote queue に二重 push しない**
|
||
|
||
### 検証結果
|
||
|
||
#### ✅ 問題なし: tiny_remote_queue_contains_guard()
|
||
```c
|
||
// core/hakmem_tiny_free.inc:10-30
|
||
static inline int tiny_remote_queue_contains_guard(SuperSlab* ss, int slab_idx, void* target) {
|
||
if (!ss || slab_idx < 0) return 0;
|
||
uintptr_t cur = atomic_load_explicit(&ss->remote_heads[slab_idx], memory_order_acquire);
|
||
int limit = 8192;
|
||
while (cur && limit-- > 0) {
|
||
if ((void*)cur == target) {
|
||
return 1; // Found duplicate
|
||
}
|
||
uintptr_t next;
|
||
if (__builtin_expect(g_remote_side_enable, 0)) {
|
||
next = tiny_remote_side_get(ss, slab_idx, (void*)cur);
|
||
} else {
|
||
next = atomic_load_explicit((_Atomic uintptr_t*)cur, memory_order_relaxed);
|
||
}
|
||
cur = next;
|
||
}
|
||
if (limit <= 0) {
|
||
return 1; // fail-safe: treat unbounded traversal as duplicate
|
||
}
|
||
return 0;
|
||
}
|
||
```
|
||
✓ 8192 ノード上限でループ安全化
|
||
✓ Fail-safe: 上限に達したら dup として扱う(conservative)
|
||
✓ remote_side 両対応
|
||
|
||
#### ✅ 問題なし: free 時の dup_remote チェック
|
||
```c
|
||
// core/hakmem_tiny_free.inc:1183-1197
|
||
int dup_remote = tiny_remote_queue_contains_guard(ss, slab_idx, ptr);
|
||
if (!dup_remote && __builtin_expect(g_remote_side_enable, 0)) {
|
||
dup_remote = (head_word == TINY_REMOTE_SENTINEL) ||
|
||
tiny_remote_side_contains(ss, slab_idx, ptr);
|
||
}
|
||
// ...
|
||
if (dup_remote) {
|
||
uintptr_t aux = tiny_remote_pack_diag(0xA214u, ss_base, ss_size, (uintptr_t)ptr);
|
||
tiny_remote_watch_mark(ptr, "dup_prevent", my_tid);
|
||
tiny_remote_watch_note("dup_prevent", ss, slab_idx, ptr, 0xA214u, my_tid, 0);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
||
(uint16_t)ss->size_class, ptr, aux);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
return; // ← Prevent double-push
|
||
}
|
||
```
|
||
✓ 二重チェック(queue walk + side table)
|
||
✓ A214 コード(dup_prevent)で検出を記録
|
||
✓ Fail-Fast: 検出後は即座に return(push しない)
|
||
|
||
#### ✅ 問題なし: ss_remote_push() の CAS ループ
|
||
```c
|
||
// core/hakmem_tiny_superslab.h:282-376
|
||
_Atomic(uintptr_t)* head = &ss->remote_heads[slab_idx];
|
||
uintptr_t old;
|
||
do {
|
||
old = atomic_load_explicit(head, memory_order_acquire);
|
||
if (!g_remote_side_enable) {
|
||
*(void**)ptr = (void*)old; // legacy embedding
|
||
}
|
||
} while (!atomic_compare_exchange_weak_explicit(head, &old, (uintptr_t)ptr,
|
||
memory_order_release,
|
||
memory_order_relaxed));
|
||
```
|
||
✓ CAS ループで atomic な single-pop-then-push
|
||
✓ ptr は new head になるのみ(二重化不可)
|
||
|
||
#### ✅ 問題なし: tiny_remote_side_set() で remote_side への重複登録防止
|
||
```c
|
||
// core/tiny_remote.c:529-575
|
||
uint32_t i = hmix(k) & (REM_SIDE_SIZE - 1);
|
||
for (uint32_t n=0; n<REM_SIDE_SIZE; n++, i=(i+1)&(REM_SIDE_SIZE-1)) {
|
||
uintptr_t expect = 0;
|
||
if (atomic_compare_exchange_weak_explicit(&g_rem_side[i].key, &expect, k,
|
||
memory_order_acq_rel,
|
||
memory_order_relaxed)) {
|
||
atomic_store_explicit(&g_rem_side[i].val, next, memory_order_release);
|
||
tiny_remote_sentinel_set(node);
|
||
return;
|
||
} else if (expect == k) {
|
||
// ← Duplicate detection
|
||
if (__builtin_expect(g_debug_remote_guard, 0)) {
|
||
uintptr_t observed = atomic_load_explicit((_Atomic uintptr_t*)node,
|
||
memory_order_relaxed);
|
||
tiny_remote_report_corruption("dup_push", node, observed);
|
||
uintptr_t aux = tiny_remote_pack_diag(0xA212u, base, ss_size, (uintptr_t)node);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
||
(uint16_t)ss->size_class, node, aux);
|
||
// ...dump + raise...
|
||
}
|
||
return; // ← Prevent duplicate
|
||
}
|
||
}
|
||
```
|
||
✓ Side table の CAS-or-collision チェック
|
||
✓ A212 コード(dup_push)で検出・記録
|
||
✓ 既に key=k の entry があれば即座に return(二重登録防止)
|
||
|
||
**Box 2 評価: ✅ PASS - 二重 push は 3 層で防止。A214/A212 コード検出も有効。**
|
||
|
||
---
|
||
|
||
## Box 4: Publish/Fetch は通知のみ検証
|
||
|
||
### 不変条件
|
||
**publish/fetch 側から drain や owner_tid を触らない**
|
||
|
||
### 検証結果
|
||
|
||
#### ✅ 問題なし: tiny_publish_notify() は通知のみ
|
||
```c
|
||
// core/tiny_publish.c:13-34
|
||
void tiny_publish_notify(int class_idx, SuperSlab* ss, int slab_idx) {
|
||
if (__builtin_expect(class_idx < 0 || class_idx >= TINY_NUM_CLASSES, 0)) {
|
||
tiny_debug_ring_record(TINY_RING_EVENT_SUPERSLAB_ADOPT_FAIL,
|
||
(uint16_t)0xEEu, ss, (uintptr_t)class_idx);
|
||
return;
|
||
}
|
||
g_pub_notify_calls[class_idx]++;
|
||
tiny_debug_ring_record(TINY_RING_EVENT_SUPERSLAB_PUBLISH,
|
||
(uint16_t)class_idx, ss, (uintptr_t)slab_idx);
|
||
// ...トレース(副作用なし)...
|
||
tiny_mailbox_publish(class_idx, ss, slab_idx); // ← 単なる通知
|
||
}
|
||
```
|
||
✓ drain 呼び出しなし
|
||
✓ owner_tid 操作なし
|
||
✓ mailbox へ (class_idx, ss, slab_idx) の 3-tuple を記録するのみ
|
||
|
||
#### ✅ 問題なし: tiny_mailbox_publish() は記録のみ
|
||
```c
|
||
// core/tiny_mailbox.c:109-119
|
||
void tiny_mailbox_publish(int class_idx, SuperSlab* ss, int slab_idx) {
|
||
tiny_mailbox_register(class_idx);
|
||
// Encode entry locally
|
||
uintptr_t ent = ((uintptr_t)ss) | ((uintptr_t)slab_idx & 0x3Fu);
|
||
uint32_t slot = g_tls_mailbox_slot[class_idx];
|
||
tiny_debug_ring_record(TINY_RING_EVENT_MAILBOX_PUBLISH, ...);
|
||
atomic_store_explicit(&g_pub_mailbox_entries[class_idx][slot], ent,
|
||
memory_order_release); // ← 単なる記録
|
||
}
|
||
```
|
||
✓ drain 呼び出しなし
|
||
✓ owner_tid 操作なし
|
||
✓ メモリへの記録のみ
|
||
|
||
#### ✅ 問題なし: tiny_mailbox_fetch() は読み込みと提示のみ
|
||
```c
|
||
// core/tiny_mailbox.c:130-252
|
||
uintptr_t tiny_mailbox_fetch(int class_idx) {
|
||
// ...スロット走査...
|
||
uintptr_t ent = atomic_exchange_explicit(mailbox, (uintptr_t)0, memory_order_acq_rel);
|
||
if (ent) {
|
||
g_pub_mail_hits[class_idx]++;
|
||
SuperSlab* ss = (SuperSlab*)(ent & ~((uintptr_t)SUPERSLAB_SIZE_MIN - 1u));
|
||
int slab = (int)(ent & 0x3Fu);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_MAILBOX_FETCH, ...);
|
||
return ent; // ← ヒントを返すのみ
|
||
}
|
||
return (uintptr_t)0;
|
||
}
|
||
```
|
||
✓ drain 呼び出しなし
|
||
✓ owner_tid 操作なし
|
||
✓ fetch は単なる "ヒント提供"(候補の推薦)
|
||
|
||
#### ✅ 問題なし: ss_partial_publish() は owner リセット + unbind + 通知
|
||
```c
|
||
// core/hakmem_tiny.c:639-717
|
||
void ss_partial_publish(int class_idx, SuperSlab* ss) {
|
||
if (!ss) return;
|
||
|
||
// ① owner_tid リセット(publish の一部)
|
||
unsigned prev = atomic_exchange_explicit(&ss->listed, 1u, memory_order_acq_rel);
|
||
if (prev != 0u) return; // already listed
|
||
|
||
// ② 所有者をリセット(adopt 準備)
|
||
int cap_pub = ss_slabs_capacity(ss);
|
||
for (int s = 0; s < cap_pub; s++) {
|
||
uint32_t prev = __atomic_exchange_n(&ss->slabs[s].owner_tid, 0u, __ATOMIC_RELEASE);
|
||
// ...記録のみ...
|
||
}
|
||
|
||
// ③ TLS unbind(publish 側が使わなくするため)
|
||
extern __thread TinyTLSSlab g_tls_slabs[];
|
||
if (g_tls_slabs[class_idx].ss == ss) {
|
||
g_tls_slabs[class_idx].ss = NULL;
|
||
g_tls_slabs[class_idx].meta = NULL;
|
||
g_tls_slabs[class_idx].slab_base = NULL;
|
||
g_tls_slabs[class_idx].slab_idx = 0;
|
||
}
|
||
|
||
// ④ hint 計算(提示用)
|
||
// ...hint を計算して ss->publish_hint セット...
|
||
|
||
// ⑤ ring に登録(通知)
|
||
for (int i = 0; i < SS_PARTIAL_RING; i++) {
|
||
// ...ring の empty slot を探して登録...
|
||
}
|
||
}
|
||
```
|
||
✓ drain 呼び出しなし(重要!)
|
||
✓ owner_tid リセットは「publish の責務」の範囲内(adopter 準備)
|
||
✓ **NOTE: publish 側から drain を呼ばない** ← Box 4 厳守
|
||
✓ 以下のコメント参照:
|
||
```c
|
||
// NOTE: Do NOT drain here! The old SuperSlab may have slabs owned by other threads
|
||
// that just adopted from it. Draining without ownership checks causes freelist corruption.
|
||
// The adopter will drain when needed (with proper ownership checks in tiny_refill.h).
|
||
```
|
||
|
||
#### ✅ 問題なし: ss_partial_adopt() は fetch + リセット+利用のみ
|
||
```c
|
||
// core/hakmem_tiny.c:719-742
|
||
SuperSlab* ss_partial_adopt(int class_idx) {
|
||
for (int i = 0; i < SS_PARTIAL_RING; i++) {
|
||
SuperSlab* ss = atomic_exchange_explicit(&g_ss_partial_ring[class_idx][i],
|
||
NULL, memory_order_acq_rel);
|
||
if (ss) {
|
||
// Clear listed flag to allow future publish
|
||
atomic_store_explicit(&ss->listed, 0u, memory_order_release);
|
||
g_ss_adopt_dbg[class_idx]++;
|
||
return ss; // ← 利用側へ返却
|
||
}
|
||
}
|
||
// Fallback: adopt from overflow stack
|
||
while (1) {
|
||
SuperSlab* head = atomic_load_explicit(&g_ss_partial_over[class_idx],
|
||
memory_order_acquire);
|
||
if (!head) break;
|
||
SuperSlab* next = head->partial_next;
|
||
if (atomic_compare_exchange_weak_explicit(&g_ss_partial_over[class_idx], &head, next,
|
||
memory_order_acq_rel, memory_order_relaxed)) {
|
||
atomic_store_explicit(&head->listed, 0u, memory_order_release);
|
||
g_ss_adopt_dbg[class_idx]++;
|
||
return head; // ← 利用側へ返却
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
```
|
||
✓ drain 呼び出しなし
|
||
✓ owner_tid 操作なし(すでに publish で 0 にされている)
|
||
✓ 単なる slab の検索+返却
|
||
|
||
#### ✅ 問題なし: adopt 側での drain は refill 境界で実施
|
||
```c
|
||
// core/hakmem_tiny_free.inc:696-740
|
||
// (superslab_refill 内)
|
||
SuperSlab* adopt = ss_partial_adopt(class_idx);
|
||
if (adopt && adopt->magic == SUPERSLAB_MAGIC) {
|
||
// ...best slab 探索...
|
||
if (best >= 0) {
|
||
uint32_t self = tiny_self_u32();
|
||
SlabHandle h = slab_try_acquire(adopt, best, self); // ← Box 3: 所有権取得
|
||
if (slab_is_valid(&h)) {
|
||
slab_drain_remote_full(&h); // ← Box 2: 所有権ガード下で drain
|
||
if (slab_remote_pending(&h)) {
|
||
// ...pending check...
|
||
slab_release(&h);
|
||
}
|
||
if (slab_freelist(&h)) {
|
||
tiny_tls_bind_slab(tls, h.ss, h.slab_idx); // ← Box 3: bind
|
||
return h.ss;
|
||
}
|
||
slab_release(&h);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
✓ **drain は採用側の refill 境界で実施** ← Box 4 完全遵守
|
||
✓ 所有権取得 → drain → bind の順序が正確
|
||
✓ slab_handle.h の slab_drain_remote() でガード
|
||
|
||
**Box 4 評価: ✅ PASS - publish/fetch は純粋な通知。drain は adopter 側境界でのみ実施。**
|
||
|
||
---
|
||
|
||
## 残り問題の検証: TOCTOU バグ(既知)
|
||
|
||
### 既知: Box 2→3 の TOCTOU バグ(修正済み)
|
||
|
||
前述の "drain 後に remote_pending チェック漏れ" は以下で修正済み:
|
||
|
||
```c
|
||
// core/hakmem_tiny_free.inc:714-717
|
||
SlabHandle h = slab_try_acquire(adopt, best, self);
|
||
if (slab_is_valid(&h)) {
|
||
slab_drain_remote_full(&h);
|
||
if (slab_remote_pending(&h)) { // ← チェック追加(修正)
|
||
slab_release(&h);
|
||
// continue to next candidate
|
||
}
|
||
}
|
||
```
|
||
|
||
✓ drain 完了後に remote_pending をチェック
|
||
✓ pending がまだあれば acquire を release して次へ
|
||
✓ TOCTOU window を最小化
|
||
|
||
---
|
||
|
||
## 追加調査: Remote A213/A202 コードの生成源特定
|
||
|
||
### A213: pre_push corruption(TLS guard scribble)
|
||
```c
|
||
// core/hakmem_tiny_free.inc:1187-1207
|
||
if (__builtin_expect(head_word == TINY_REMOTE_SENTINEL && !dup_remote && g_debug_remote_guard, 0)) {
|
||
tiny_remote_watch_note("dup_scan_miss", ss, slab_idx, ptr, 0xA215u, my_tid, 0);
|
||
}
|
||
if (dup_remote) {
|
||
// ...A214...
|
||
}
|
||
if (__builtin_expect(g_remote_side_enable && (head_word & 0xFFFFu) == 0x6261u, 0)) {
|
||
// TLS guard scribble detected on the node's first word
|
||
uintptr_t aux = tiny_remote_pack_diag(0xA213u, ss_base, ss_size, (uintptr_t)ptr);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
||
(uint16_t)ss->size_class, ptr, aux);
|
||
tiny_remote_watch_mark(ptr, "pre_push", my_tid);
|
||
tiny_remote_watch_note("pre_push", ss, slab_idx, ptr, 0xA231u, my_tid, 0);
|
||
tiny_remote_report_corruption("pre_push", ptr, head_word);
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
return;
|
||
}
|
||
```
|
||
✓ A213: 発見元は hakmem_tiny_free.inc:1198-1206
|
||
✓ 原因: node の first word に 0x6261 (ba) scribble が見られた
|
||
✓ 意味: 同じ pointer で既に ss_remote_side_set が呼ばれている可能性
|
||
✓ 修正: dup_remote チェックで事前に防止(現実装で動作)
|
||
|
||
### A202: sentinel corruption(drain 時)
|
||
```c
|
||
// core/hakmem_tiny_superslab.h:409-427
|
||
if (__builtin_expect(g_remote_side_enable, 0)) {
|
||
if (!tiny_remote_sentinel_ok(node)) {
|
||
uintptr_t aux = tiny_remote_pack_diag(0xA202u, base, ss_size, (uintptr_t)node);
|
||
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
||
(uint16_t)ss->size_class, node, aux);
|
||
// ...corruption report...
|
||
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
||
}
|
||
tiny_remote_side_clear(ss, slab_idx, node);
|
||
}
|
||
```
|
||
✓ A202: 発見元は hakmem_tiny_superslab.h:410
|
||
✓ 原因: drain 時に node の sentinel が不正(0xBADA55... ではない)
|
||
✓ 意味: node の first word が何らかの理由で上書きされた
|
||
✓ 対策: g_remote_side_enable でも sentinel チェック
|
||
|
||
---
|
||
|
||
## Box Theory の完全性評価
|
||
|
||
### Box 境界チェックリスト
|
||
|
||
| Box | 機能 | 不変条件 | 検証 | 評価 |
|
||
|-----|------|---------|------|------|
|
||
| **Box 1** | Atomic Ops | CAS/Exchange の秩序付け(Release/Acquire) | 記載省略(下層) | ✅ |
|
||
| **Box 2** | Remote Queue | push は freelist/owner に触れない | 二重 push: A214/A212 | ✅ PASS |
|
||
| **Box 3** | Ownership | acquire/release の正確性 | owner_tid CAS | ✅ PASS |
|
||
| **Box 4** | Publish/Adopt | publish から drain 呼ばない | 採用境界分離確認 | ✅ PASS |
|
||
| **Box 3↔2** | Drain boundary | ownership 確保後 drain | slab_handle.h 経由 | ✅ PASS |
|
||
| **Box 4→3** | Adopt boundary | drain→bind→owner の順序 | refill 1箇所 | ✅ PASS |
|
||
|
||
### 結論
|
||
|
||
**✅ Box 境界の不変条件は厳密に守られている。**
|
||
|
||
1. **Box 3 (Ownership)**:
|
||
- freelist push は owner_tid==my_tid のみ
|
||
- publish 時の owner リセットが明確
|
||
- slab_handle.h の SlabHandle でガード完全
|
||
|
||
2. **Box 2 (Remote Queue)**:
|
||
- 二重 push は 3 層で防止(free 側: A214, side-set: A212, traverse limit: fail-safe)
|
||
- remote_side の sentinel で追加保護
|
||
- drain 時の sentinel チェックで corruption 検出
|
||
|
||
3. **Box 4 (Publish/Fetch)**:
|
||
- publish は owner リセット+通知のみ
|
||
- drain は publish 側では呼ばない
|
||
- 採用側 refill 境界でのみ drain(ownership ガード下)
|
||
|
||
4. **remote_invalid の A213/A202 検出**:
|
||
- A213: dup_remote チェック(1183)で事前防止
|
||
- A202: sentinel 検査(410)で drain 時検出
|
||
- どちらも fail-fast で即座に報告・停止
|
||
|
||
---
|
||
|
||
## 推奨事項
|
||
|
||
### 現在の状態
|
||
**Box Theory の実装は健全です。散発的な remote_invalid は以下に起因する可能性:**
|
||
|
||
1. **Timing window**
|
||
- publish → unlisted(catalog から外れる)→ adopt の間に
|
||
- owner=0 のまま別スレッドが allocate する可能性は低いが、エッジケースあり得る
|
||
|
||
2. **Platform memory ordering**
|
||
- x86: Acquire/Release は効くが、他の platform では要注意
|
||
- memory_order_acq_rel で CAS してるので current は安全
|
||
|
||
3. **Rare race in ss_partial_adopt()**
|
||
- overflow stack での LIFO pop と新規登録の タイミング
|
||
- 概率は低いが、同時並行で複数スレッドが overflow を走査
|
||
|
||
### テスト・デバッグ提案
|
||
```bash
|
||
# 散発的なバグを局所化
|
||
HAKMEM_TINY_REMOTE_SIDE=1 # Side table 有効化
|
||
HAKMEM_DEBUG_COUNTERS=1 # 統計カウント
|
||
HAKMEM_TINY_RF_TRACE=1 # publish/fetch の トレース
|
||
HAKMEM_TINY_SS_ADOPT=1 # SuperSlab adopt 有効化
|
||
|
||
# 検出時のダンプ
|
||
HAKMEM_TINY_MAILBOX_SLOWDISC=1 # Slow discovery
|
||
```
|
||
|
||
---
|
||
|
||
## まとめ
|
||
|
||
**徹底検証の結果、Box 3, 2, 4 の不変条件は守られている。**
|
||
|
||
- Box 3: freelist push は所有権ガード完全 ✅
|
||
- Box 2: 二重 push は 3 層で防止 ✅
|
||
- Box 4: publish/fetch は純粋な通知、drain は adopter 側 ✅
|
||
|
||
remote_invalid (A213/A202) の散発は、Box Theory のバグではなく、
|
||
**edge case in timing** である可能性が高い。
|
||
|
||
TOCTOU window 最小化と memory barrier の強化で、
|
||
さらに robust化できる可能性あり。
|
||
|