# 機能設計書: 信号システム（シグナル）

## 1. 機能概要

### 1.1 機能の目的

信号システム（Signal System）は、Godot Engineにおけるオブジェクト間通信の基盤を提供する機能である。オブザーバーパターン（Observer Pattern）を実装し、オブジェクト間の疎結合な通信を実現する。シグナルの発行（emit）と接続（connect）により、ゲームオブジェクト間のイベント駆動型プログラミングを可能にする。

### 1.2 主要な責務

| 責務 | 説明 |
|------|------|
| シグナル定義 | 組み込みシグナルとユーザー定義シグナルの管理 |
| シグナル接続 | オブジェクト間のシグナル接続の確立と管理 |
| シグナル発行 | シグナルの発行と接続されたCallableの呼び出し |
| 接続管理 | 接続のフラグ管理（遅延実行、ワンショット等） |
| スレッド安全性 | マルチスレッド環境でのシグナル操作の安全性確保 |

### 1.3 アーキテクチャ上の位置づけ

```
+------------------+
|   Game Scripts   |  <-- GDScript/C#でシグナルを使用
+------------------+
         |
         v
+------------------+
|   Scene Tree     |  <-- シーン全体のシグナル伝播
+------------------+
         |
         v
+------------------+
|  Signal System   |  <-- Object基底クラスに実装
|  (Object class)  |
+------------------+
         |
         v
+------------------+
| Message Queue    |  <-- 遅延呼び出しの実行
+------------------+
```

## 2. クラス構造

### 2.1 主要クラス

```
Object (core/object/object.h)
├── SignalData構造体
│   ├── Slot構造体
│   │   ├── reference_count: int
│   │   ├── conn: Connection
│   │   └── cE: List<Connection>::Element*
│   ├── user: MethodInfo
│   ├── slot_map: HashMap<Callable, Slot>
│   └── removable: bool
├── Connection構造体
│   ├── signal: ::Signal
│   ├── callable: Callable
│   └── flags: uint32_t
├── signal_map: HashMap<StringName, SignalData>
├── connections: List<Connection>
└── signal_mutex: Mutex*
```

### 2.2 接続フラグ（ConnectFlags）

```cpp
// core/object/object.h 573-580行目
enum ConnectFlags {
    CONNECT_DEFERRED = 1,          // 遅延実行（フレーム終了時に呼び出し）
    CONNECT_PERSIST = 2,           // 永続化（シーン保存時に接続を保持）
    CONNECT_ONE_SHOT = 4,          // 1回のみ実行後に自動切断
    CONNECT_REFERENCE_COUNTED = 8, // 参照カウント方式
    CONNECT_APPEND_SOURCE_OBJECT = 16, // 送信元オブジェクトを引数に追加
    CONNECT_INHERITED = 32,        // エディタ用（継承接続）
};
```

## 3. 処理フロー

### 3.1 シグナル接続フロー

```
[connect()呼び出し]
        |
        v
+------------------+
| 引数検証         |
| (Callable検証)   |
+------------------+
        |
        v
+------------------+
| シグナル存在確認 |
| (ClassDB/Script) |
+------------------+
        |
        v
+------------------+
| SignalData取得   |
| または新規作成   |
+------------------+
        |
        v
+------------------+
| 重複接続チェック |
| (参照カウント)   |
+------------------+
        |
        v
+------------------+
| Slot作成・登録   |
| Connection追加   |
+------------------+
        |
        v
[接続完了]
```

### 3.2 シグナル発行フロー

```
[emit_signalp()呼び出し]
        |
        v
+------------------+
| _block_signals   |  --> true: 即座に返却
| チェック         |
+------------------+
        |
        v
+------------------+
| SignalData取得   |
| (signal_map)     |
+------------------+
        |
        v
+------------------+
| スロット複製     |  <-- ロック中にスロットをコピー
| (スタック/ヒープ)|
+------------------+
        |
        v
+------------------+
| ONE_SHOT接続     |  <-- emit前に切断
| 切断処理         |
+------------------+
        |
        v
+------------------+
| RefCounted保護   |  <-- emit中のオブジェクト破棄防止
+------------------+
        |
        v
+------------------+
| 各Callableを     |
| 順次呼び出し     |
|  - DEFERRED: MQ  |
|  - 通常: 直接    |
+------------------+
        |
        v
+------------------+
| クリーンアップ   |
| (メモリ解放)     |
+------------------+
        |
        v
[発行完了]
```

## 4. データ構造

### 4.1 シグナルマップ構造

```cpp
// core/object/object.h 630-640行目
struct SignalData {
    struct Slot {
        int reference_count = 0;      // 参照カウント
        Connection conn;               // 接続情報
        List<Connection>::Element *cE = nullptr;  // ターゲットの接続リスト要素
    };

    MethodInfo user;                   // ユーザー定義シグナルの情報
    HashMap<Callable, Slot> slot_map;  // 接続スロットマップ
    bool removable = false;            // 削除可能フラグ
};
```

### 4.2 接続構造

```cpp
// core/object/object.h 607-618行目
struct Connection {
    ::Signal signal;    // シグナル参照
    Callable callable;  // 呼び出し対象
    uint32_t flags = 0; // 接続フラグ

    bool operator<(const Connection &p_conn) const;
    operator Variant() const;
    Connection() {}
    Connection(const Variant &p_variant);
};
```

## 5. 主要メソッド詳細

### 5.1 connect（シグナル接続）

```cpp
// core/object/object.cpp 1607-1674行目
Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
    ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, ...);
    OBJ_SIGNAL_LOCK  // シグナルミューテックスロック

    // Callable検証
    if (p_callable.is_standard()) {
        ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, ...);
    } else {
        ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, ...);
    }

    // シグナル存在確認
    SignalData *s = signal_map.getptr(p_signal);
    if (!s) {
        bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
        if (!signal_is_valid && script_instance) {
            signal_is_valid = script_instance->get_script()->has_script_signal(p_signal);
        }
        ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, ...);
        signal_map[p_signal] = SignalData();
        s = &signal_map[p_signal];
    }

    // 重複接続チェック
    if (s->slot_map.has(*p_callable.get_base_comparator())) {
        if (p_flags & CONNECT_REFERENCE_COUNTED) {
            s->slot_map[*p_callable.get_base_comparator()].reference_count++;
            return OK;
        } else {
            ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, ...);
        }
    }

    // Slot作成と登録
    SignalData::Slot slot;
    Connection conn;
    conn.callable = p_callable;
    conn.signal = ::Signal(this, p_signal);
    conn.flags = p_flags;
    slot.conn = conn;

    Object *target_object = p_callable.get_object();
    if (target_object) {
        slot.cE = target_object->connections.push_back(conn);
    }

    s->slot_map[*p_callable.get_base_comparator()] = slot;
    return OK;
}
```

### 5.2 emit_signalp（シグナル発行）

```cpp
// core/object/object.cpp 1274-1432行目
Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) {
    if (_block_signals) {
        return ERR_CANT_ACQUIRE_RESOURCE;
    }

    constexpr int MAX_SLOTS_ON_STACK = 5;
    alignas(Callable) uint8_t slot_callable_stack[sizeof(Callable) * MAX_SLOTS_ON_STACK];
    uint32_t slot_flags_stack[MAX_SLOTS_ON_STACK];

    Callable *slot_callables = (Callable *)slot_callable_stack;
    uint32_t *slot_flags = slot_flags_stack;
    uint32_t slot_count = 0;

    {
        OBJ_SIGNAL_LOCK  // ロック範囲を限定

        SignalData *s = signal_map.getptr(p_name);
        if (!s) {
            // シグナル未接続
            return ERR_UNAVAILABLE;
        }

        // 動的メモリ確保（必要な場合）
        if (s->slot_map.size() > MAX_SLOTS_ON_STACK) {
            slot_callables = (Callable *)memalloc(sizeof(Callable) * s->slot_map.size());
            slot_flags = (uint32_t *)memalloc(sizeof(uint32_t) * s->slot_map.size());
        }

        // スロット情報をコピー（ロック中）
        for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
            memnew_placement(&slot_callables[slot_count], Callable(slot_kv.value.conn.callable));
            slot_flags[slot_count] = slot_kv.value.conn.flags;
            ++slot_count;
        }

        // ONE_SHOT接続の切断（emit前）
        for (uint32_t i = 0; i < slot_count; ++i) {
            if (slot_flags[i] & CONNECT_ONE_SHOT) {
                _disconnect(p_name, slot_callables[i]);
            }
        }
    }  // ロック解除

    // RefCountedオブジェクトの保護
    bool pending_unref = Object::cast_to<RefCounted>(this) ?
                         ((RefCounted *)this)->reference() : false;

    Error err = OK;

    // 各スロットを呼び出し
    for (uint32_t i = 0; i < slot_count; ++i) {
        const Callable &callable = slot_callables[i];
        const uint32_t &flags = slot_flags[i];

        if (!callable.is_valid()) {
            continue;  // ターゲットが削除された場合
        }

        if (flags & CONNECT_DEFERRED) {
            // 遅延実行: MessageQueueに追加
            MessageQueue::get_singleton()->push_callablep(callable, p_args, p_argcount, true);
        } else {
            // 即時実行
            _emitting = true;
            Variant ret;
            Callable::CallError ce;
            callable.callp(p_args, p_argcount, ret, ce);
            _emitting = false;

            if (ce.error != Callable::CallError::CALL_OK) {
                err = ERR_METHOD_NOT_FOUND;
            }
        }
    }

    // クリーンアップ
    for (uint32_t i = 0; i < slot_count; ++i) {
        slot_callables[i].~Callable();
    }

    if (slot_callables != (Callable *)slot_callable_stack) {
        memfree(slot_callables);
        memfree(slot_flags);
    }

    // RefCounted保護の解除
    if (pending_unref) {
        if (((RefCounted *)this)->unreference()) {
            memdelete(this);
        }
    }

    return err;
}
```

### 5.3 disconnect（シグナル切断）

```cpp
// core/object/object.cpp 1721-1759行目
bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
    ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, ...);
    OBJ_SIGNAL_LOCK

    SignalData *s = signal_map.getptr(p_signal);
    ERR_FAIL_NULL_V_MSG(s, false, ...);
    ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, ...);

    SignalData::Slot *slot = &s->slot_map[*p_callable.get_base_comparator()];

    if (!p_force) {
        slot->reference_count--;
        if (slot->reference_count > 0) {
            return false;  // まだ参照が残っている
        }
    }

    // ターゲットの接続リストから削除
    if (slot->cE) {
        Object *target_object = p_callable.get_object();
        if (target_object) {
            target_object->connections.erase(slot->cE);
        }
    }

    // スロットマップから削除
    s->slot_map.erase(*p_callable.get_base_comparator());

    // 組み込みシグナルの場合、空になったらSignalData削除
    if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
        signal_map.erase(p_signal);
    }

    return true;
}
```

## 6. スレッド安全性

### 6.1 シグナルロック機構

```cpp
// core/object/object.cpp 68-84行目
struct _ObjectSignalLock {
    Mutex *mutex;
    _ObjectSignalLock(const Object *const p_obj) {
        mutex = p_obj->signal_mutex;
        if (mutex) {
            mutex->lock();
        }
    }
    ~_ObjectSignalLock() {
        if (mutex) {
            mutex->unlock();
        }
    }
};

// マクロ定義
#define OBJ_SIGNAL_LOCK _ObjectSignalLock _signal_lock_(this);
```

### 6.2 ロック範囲の最適化

シグナル発行時、ロック範囲を最小限に抑えることで、デッドロックを防止しつつパフォーマンスを確保する。

```cpp
Error Object::emit_signalp(...) {
    {
        OBJ_SIGNAL_LOCK  // ここでロック
        // スロット情報のコピーのみ
    }  // ここでアンロック

    // Callableの呼び出しはロック外で実行
    for (...) {
        callable.callp(...);  // ロックなし
    }
}
```

## 7. ユーザーシグナル

### 7.1 ユーザーシグナルの追加

```cpp
// core/object/object.cpp 1209-1219行目
void Object::add_user_signal(const MethodInfo &p_signal) {
    ERR_FAIL_COND_MSG(p_signal.name.is_empty(), "Signal name cannot be empty.");
    ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), ...);

    OBJ_SIGNAL_LOCK

    ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), ...);
    SignalData s;
    s.user = p_signal;
    signal_map[p_signal.name] = s;
}
```

### 7.2 スクリプトからの使用

```cpp
// core/object/object.cpp 1444-1473行目
void Object::_add_user_signal(const String &p_name, const Array &p_args) {
    OBJ_SIGNAL_LOCK

    MethodInfo mi;
    mi.name = p_name;

    for (const Variant &arg : p_args) {
        Dictionary d = arg;
        PropertyInfo param;
        if (d.has("name")) param.name = d["name"];
        if (d.has("type")) param.type = (Variant::Type)(int)d["type"];
        mi.arguments.push_back(param);
    }

    add_user_signal(mi);
    signal_map.getptr(p_name)->removable = true;  // 削除可能フラグ
}
```

## 8. コードリーディングガイド

### 8.1 推奨読解順序

1. **データ構造の理解** (object.h 630-644行目)
   - SignalData, Slot, Connectionの構造を把握
   - signal_mapとconnectionsの関係を理解

2. **接続フラグ** (object.h 573-580行目)
   - ConnectFlagsの各フラグの意味を確認

3. **シグナル接続** (object.cpp 1607-1674行目)
   - connect()の処理フローを追跡
   - 重複接続と参照カウントの扱い

4. **シグナル発行** (object.cpp 1274-1432行目)
   - emit_signalp()の最適化手法を理解
   - ロック範囲とメモリ管理

5. **スレッド安全性** (object.cpp 68-84行目)
   - _ObjectSignalLockの仕組み

### 8.2 読解のコツ

- `OBJ_SIGNAL_LOCK`マクロはRAIIパターンでミューテックスロックを管理
- `slot_callable_stack`は小規模なスロット用のスタック最適化
- `get_base_comparator()`はCallableのバインドを無視した比較に使用
- `_emitting`フラグは再帰的emit検出用

### 8.3 プログラム呼び出し階層図

```
emit_signal<VarArgs...>()
    |
    v
emit_signalp()
    |
    +---> OBJ_SIGNAL_LOCK (ロック取得)
    |
    +---> signal_map.getptr() (シグナルデータ取得)
    |
    +---> memnew_placement() (スロットコピー)
    |
    +---> _disconnect() [ONE_SHOT時]
    |
    +---> OBJ_SIGNAL_LOCK (ロック解放)
    |
    +---> RefCounted::reference() [RefCounted保護]
    |
    +---> Callable::callp() または
    |     MessageQueue::push_callablep()
    |
    +---> RefCounted::unreference() [保護解除]
    |
    v
[完了]
```

### 8.4 データフロー図

```
+----------------+     connect()      +----------------+
|  Source Object |------------------>|  Target Object |
|                |                    |                |
| signal_map:    |                    | connections:   |
| "signal_name"  |                    | [Connection]   |
|   -> Slot      |<-------------------|   <- cE        |
|      callable -|------------------->|   callable     |
+----------------+   emit_signalp()   +----------------+
        |                                    ^
        |                                    |
        v                                    |
+----------------+                           |
| MessageQueue   |------ DEFERRED -----------+
| push_callablep |
+----------------+
```

### 8.5 関連ファイル一覧

| ファイル | 役割 |
|----------|------|
| `core/object/object.h` | Objectクラス定義、SignalData/Connection構造体 |
| `core/object/object.cpp` | シグナルシステムの実装 |
| `core/object/callable.h` | Callable型の定義 |
| `core/object/message_queue.h` | 遅延呼び出し用メッセージキュー |
| `core/object/class_db.h` | ClassDBシグナル登録 |
| `core/variant/variant.h` | シグナル引数の型 |

## 9. 設計上の考慮点

### 9.1 パフォーマンス最適化

1. **スタック割り当て優先**: 5スロット以下はスタック使用
2. **ロック範囲最小化**: コピー後にロック解放
3. **HashMap使用**: O(1)でのスロット検索

### 9.2 安全性考慮

1. **RefCounted保護**: emit中のオブジェクト破棄防止
2. **無効Callable処理**: 削除されたターゲットをスキップ
3. **再帰emit検出**: `_emitting`フラグによる追跡

### 9.3 柔軟性

1. **接続フラグ**: 様々な接続パターンをサポート
2. **ユーザーシグナル**: 動的なシグナル定義
3. **Callable汎用性**: メソッド、ラムダ、バインド引数対応

## 10. 使用例

### 10.1 GDScriptでの使用

```gdscript
# シグナル定義
signal health_changed(new_health)

# 接続
other_node.health_changed.connect(_on_health_changed)

# 発行
health_changed.emit(100)

# 遅延接続
other_node.health_changed.connect(_on_health_changed, CONNECT_DEFERRED)

# ワンショット接続
other_node.health_changed.connect(_on_health_changed, CONNECT_ONE_SHOT)
```

### 10.2 C++での使用

```cpp
// シグナル発行
emit_signal("health_changed", new_health);

// 接続
object->connect("health_changed", callable_mp(this, &MyClass::_on_health_changed));

// 切断
object->disconnect("health_changed", callable_mp(this, &MyClass::_on_health_changed));
```
