183 lines
5.2 KiB
Markdown
183 lines
5.2 KiB
Markdown
|
|
# 🔌 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)
|