✅ Phase 9.75実装計画追加: - copilot_issues.txtにPhase 9.75追加(Phase 9.7と9.8の間) - Arc<Mutex>二重化問題の根本解決計画 - 段階的実装戦略(Phase A-D)定義 📚 Box設計ドキュメント完全体系化: - docs/説明書/reference/box-design/ 新設 - everything-is-box.md: 核心哲学の完全解説 - memory-management.md: Arc<Mutex>設計・fini/weak参照 - delegation-system.md: 完全明示デリゲーション仕様 - box-types-catalog.md: 全Box型の完全カタログ - ffi-abi-specification.md: FFI/ABI仕様(移動済み) 🔧 実装ノート完備: - current-issues.md: 現在進行中の設計課題 - socket-box-problem.md: Arc<Mutex>二重化問題詳細分析 - phase-9-75-redesign.md: 実装計画詳細 👥 Copilot実装ガイド作成: - phase9_75_socketbox_arc_mutex_redesign.md - SocketBox優先対応の具体的実装手順 - 完全テストスイート設計 - 段階的実装戦略(Step 1-5) 📋 CURRENT_TASK.md更新: - Box設計ドキュメント完成記録 - Phase 9.75準備完了状況 🎯 効果: - Everything is Box哲学の体系的文書化 - SocketBox問題解決の明確な道筋 - Copilot協調実装の準備完了 - 新規開発者オンボーディング改善 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
5.2 KiB
5.2 KiB
🔌 SocketBox Arc二重化問題の詳細分析
🚨 問題の症状
観測された現象
// テストコード
server = new SocketBox()
server.bind("127.0.0.1", 8080) // ✅ 成功: true
server.isServer() // ❌ 失敗: false (期待値: true)
デバッグ出力
🔥 SOCKETBOX DEBUG: bind() called
🔥 Socket ID = 17
🔥 Arc pointer = 0x7ffd5b8a3d20
🔥 Arc data pointer = 0x5565423a6d60
🔥 AFTER MUTATION: is_server = true
🔥 SOCKETBOX DEBUG: isServer() called
🔥 Socket ID = 17
🔥 Arc pointer = 0x7ffd5b8a3d20
🔥 Arc data pointer = 0x5565423a6d60 // 同じポインタ!
🔥 IS_SERVER READ: is_server = false // しかし値は失われている
🔍 根本原因の分析
1. 責務の二重化
// 現在のSocketBox実装
pub struct SocketBox {
base: BoxBase,
listener: Arc<Mutex<Option<TcpListener>>>, // 内部ロック
stream: Arc<Mutex<Option<TcpStream>>>, // 内部ロック
is_server: Arc<Mutex<bool>>, // 内部ロック
is_connected: Arc<Mutex<bool>>, // 内部ロック
}
// インタープリター側
let socket: Arc<Mutex<dyn NyashBox>> = Arc::new(Mutex::new(SocketBox::new()));
2. 状態更新の問題
// bind()メソッド内での状態更新
pub fn bind(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// ...
*self.is_server.lock().unwrap() = true; // 内部Mutexへの書き込み
// ...
}
3. Clone実装の複雑性
impl Clone for SocketBox {
fn clone(&self) -> Self {
Self {
base: BoxBase::new(), // 新しいID(デバッグ用)
listener: Arc::clone(&self.listener), // Arcを共有
stream: Arc::clone(&self.stream), // Arcを共有
is_server: Arc::clone(&self.is_server), // Arcを共有
is_connected: Arc::clone(&self.is_connected), // Arcを共有
}
}
}
💀 デッドロックの危険性
発生パターン
- インタープリターがSocketBoxをロック
- SocketBoxメソッドが内部フィールドをロック
- 別スレッドが逆順でロックを試みる
- デッドロック発生
実際に観測されたデッドロック
// PR #75修正前
pub fn bind(&self, ...) -> Box<dyn NyashBox> {
// ...
let updated = SocketBox { /* ... */ };
Box::new(updated.clone()) // ここでデッドロック
}
🔄 試みられた修正と結果
PR #75: Arc統合
目的: 状態を共有するためにArc参照を使用 結果:
- ✅ デッドロック解消
- ❌ 状態保持問題は未解決
PR #81: フィールド更新メカニズム
目的: インスタンスフィールドの更新を修正 結果:
- ✅ フィールド更新は動作
- ❌ SocketBox内部状態は依然として失われる
🎯 Gemini先生の根本解決策
設計原則の転換
// ❌ 現在の設計: Box自身がロック責務を持つ
pub struct SocketBox {
is_server: Arc<Mutex<bool>>, // Box内部でロック管理
}
// ✅ 新設計: 純粋なデータコンテナ
pub struct PlainSocketBox {
pub listener: Option<TcpListener>,
pub is_server: bool, // シンプルなフィールド
}
責務の明確化
- Box: 純粋なデータとロジックのみ
- インタープリター: すべてのロック管理を一元化
実装例
// 新しいbind()実装
impl PlainSocketBox {
pub fn bind(&mut self, addr: &str, port: u16) -> bool {
match TcpListener::bind((addr, port)) {
Ok(listener) => {
self.listener = Some(listener);
self.is_server = true; // 直接代入、ロック不要
true
},
Err(_) => false
}
}
}
📊 影響分析
同じ問題を抱えるBox型
- HTTPServerBox: SocketBoxを内包、同様の問題
- ArrayBox:
Arc<Mutex<Vec<...>>> - MapBox:
Arc<Mutex<HashMap<...>>> - P2PBox: 複雑な内部状態管理
- その他10個のBox型
リファクタリングの規模
- 影響Box数: 15個
- 推定作業量: 2-3週間(Phase 9.75)
- リスク: 既存コードの互換性
🚀 移行戦略
Phase A: 設計ガイドライン(3日)
- 新Box実装パターンの確立
- Arc禁止ルールの明文化
- テンプレート・サンプルコード作成
Phase B: 最優先修正(1週間)
- SocketBox → PlainSocketBox
- HTTPServerBox → PlainHTTPServerBox
- 状態保持テストスイート作成
Phase C: 全Box統一(1-2週間)
- 残り13個のBox型修正
- 統合テスト実施
- パフォーマンス検証
🎉 期待される効果
- デッドロック根絶: 二重ロック構造の排除
- 状態整合性保証: インタープリター一元管理
- デバッグ容易性: シンプルな実装
- パフォーマンス向上: ロック競合の削減
- 保守性向上: 統一的な実装パターン
関連ドキュメント: