Files
hakorune/docs/説明書/reference/box-design/implementation-notes/socket-box-problem.md

183 lines
5.2 KiB
Markdown
Raw Normal View History

# 🔌 SocketBox Arc<Mutex>二重化問題の詳細分析
## 🚨 問題の症状
### 観測された現象
```nyash
// テストコード
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. 責務の二重化
```rust
// 現在の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. 状態更新の問題
```rust
// 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実装の複雑性
```rust
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を共有
}
}
}
```
## 💀 デッドロックの危険性
### 発生パターン
1. インタープリターがSocketBoxをロック
2. SocketBoxメソッドが内部フィールドをロック
3. 別スレッドが逆順でロックを試みる
4. デッドロック発生
### 実際に観測されたデッドロック
```rust
// PR #75修正前
pub fn bind(&self, ...) -> Box<dyn NyashBox> {
// ...
let updated = SocketBox { /* ... */ };
Box::new(updated.clone()) // ここでデッドロック
}
```
## 🔄 試みられた修正と結果
### PR #75: Arc<dyn NyashBox>統合
**目的**: 状態を共有するためにArc参照を使用
**結果**:
- ✅ デッドロック解消
- ❌ 状態保持問題は未解決
### PR #81: フィールド更新メカニズム
**目的**: インスタンスフィールドの更新を修正
**結果**:
- ✅ フィールド更新は動作
- ❌ SocketBox内部状態は依然として失われる
## 🎯 Gemini先生の根本解決策
### 設計原則の転換
```rust
// ❌ 現在の設計: Box自身がロック責務を持つ
pub struct SocketBox {
is_server: Arc<Mutex<bool>>, // Box内部でロック管理
}
// ✅ 新設計: 純粋なデータコンテナ
pub struct PlainSocketBox {
pub listener: Option<TcpListener>,
pub is_server: bool, // シンプルなフィールド
}
```
### 責務の明確化
- **Box**: 純粋なデータとロジックのみ
- **インタープリター**: すべてのロック管理を一元化
### 実装例
```rust
// 新しい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型
1. **HTTPServerBox**: SocketBoxを内包、同様の問題
2. **ArrayBox**: `Arc<Mutex<Vec<...>>>`
3. **MapBox**: `Arc<Mutex<HashMap<...>>>`
4. **P2PBox**: 複雑な内部状態管理
5. その他10個のBox型
### リファクタリングの規模
- 影響Box数: 15個
- 推定作業量: 2-3週間Phase 9.75
- リスク: 既存コードの互換性
## 🚀 移行戦略
### Phase A: 設計ガイドライン3日
1. 新Box実装パターンの確立
2. Arc<Mutex>禁止ルールの明文化
3. テンプレート・サンプルコード作成
### Phase B: 最優先修正1週間
1. SocketBox → PlainSocketBox
2. HTTPServerBox → PlainHTTPServerBox
3. 状態保持テストスイート作成
### Phase C: 全Box統一1-2週間
1. 残り13個のBox型修正
2. 統合テスト実施
3. パフォーマンス検証
## 🎉 期待される効果
1. **デッドロック根絶**: 二重ロック構造の排除
2. **状態整合性保証**: インタープリター一元管理
3. **デバッグ容易性**: シンプルな実装
4. **パフォーマンス向上**: ロック競合の削減
5. **保守性向上**: 統一的な実装パターン
---
関連ドキュメント:
- [現在の課題一覧](current-issues.md)
- [Phase 9.75実装計画](phase-9-75-redesign.md)
- [メモリ管理設計](../memory-management.md)