|
Document of c-modernization-kit (porter) 1.0.0
|
本ドキュメントは、porter にパス単位のイベント通知機能 (POTR_EVENT_PATH_CONNECTED / POTR_EVENT_PATH_DISCONNECTED) を追加するための設計仕様を記述します。
porter は現在、セッション全体の接続・切断を以下の 2 つのイベントでアプリケーションへ通知します。
| イベント | 発火条件 |
|---|---|
| POTR_EVENT_CONNECTED | セッション全体で初めてパケットが届いた時 (TCP: 初パケット受信時) |
| POTR_EVENT_DISCONNECTED | セッション全体が断した時 (全パスタイムアウト / FIN 受信 / REJECT 受信) |
これらはセッション全体の状態変化のみを表し、個々のパスの状態は通知されません。
マルチパス構成 (最大 4 パス) では、複数の物理経路を同時に使用します。このとき以下の状況を現行の API では検知できません。
セッション内の個々のパスが疎通状態に変化した際に、アプリケーションへイベントを通知します。これにより、アプリケーションはどのパスが現在有効かをリアルタイムで把握できます。
既存の PotrEvent 列挙型に以下の 2 つの値を追加します。
FIXME: POTR_EVENT_PATH_CONNECTED, POTR_EVENT_PATH_DISCONNECTED ではなく、POTR_EVENT_PATH_STATUS 1 つにする。
PotrRecvCallback のシグネチャは以下のとおりです。
PATH イベント時の引数解釈は以下のとおりです。
| 引数 | PATH イベント時の意味 |
|---|---|
| service_id | サービス ID(変更なし) |
| peer_id | N:1 モードでは対象ピアの ID。1:1 モードでは POTR_PEER_NA |
| event | POTR_EVENT_PATH_CONNECTED または POTR_EVENT_PATH_DISCONNECTED |
| data | const int * としてキャストして使用。int path_states[POTR_MAX_PATH] を指す。コールバック復帰後は無効(スタック変数) |
| len | 変化したパスのインデックス (0 〜 POTR_MAX_PATH-1) |
FIXME: len は、sizeof(data) が自然。
data が指す配列には、コールバック呼び出し時点での全パスの疎通状態が格納されます。
path_states[k] の値の意味:
| 値 | 意味 |
|---|---|
| 1 | パス k は疎通中 |
| 0 | パス k は未接続 または 疎通断 |
potrContext.h の PotrContext_ 構造体に以下のフィールドを追加します。
配置位置: health_alive フィールドの直後が適切です。
初期化: potrOpenService() 内で memset(ctx, 0, sizeof(*ctx)) が実行されるため、明示的な初期化は不要です。
N:1 モード (POTR_TYPE_UNICAST_BIDIR_N1) では、PotrPeerContext 内の既存フィールドをそのままパス状態の判定に使用します。
PotrPeerContext には新しいフィールドを追加しません。
path_alive[path_idx] フラグで制御します。
n1_update_path_recv() の新規パス学習 (else ブランチ) で PATH_CONNECTED を発火します。 このブランチは dest_addr[path_idx].sin_family が AF_UNSPEC (0) のときのみ実行されるため、二重発火は発生しません。
n1_check_health_timeout() の peer_path_clear() 呼び出し前で PATH_DISCONNECTED を発火します。 peer_path_clear() が dest_addr[path_idx] をゼロクリアするため、その後は再び PATH_CONNECTED が発火できる状態に戻ります。
発火場所: potrRecvThread.c の update_path_recv() 関数末尾
update_path_recv() の呼び出し元は、その直後に notify_health_alive() を呼んでセッション全体の CONNECTED を判定します。PATH_CONNECTED → CONNECTED の順序が自然に保証されます。
発火場所: potrRecvThread.c の check_health_timeout() 関数内、パス単位タイムアウト判定ブロック
このループが終わった後、同関数内でセッション全体のタイムアウト判定が行われます。全パスが消滅していれば DISCONNECTED が発火します。PATH_DISCONNECTED → DISCONNECTED の順序が保証されます。
FIXME: FIN メッセージを受信した後の DISCONNCTED の前には、当該セッションのパスの状態変化として全パスが 0 になるか確認する。
発火場所: potrRecvThread.c の n1_update_path_recv() 関数内、新規パス学習の else ブランチ末尾
n1_fire_path_event() の内部では peer->dest_addr[k].sin_family でパス状態を取得するため、既に AF_INET に設定済みの状態 (= このパスが alive) で path_states が構築されます。
発火場所: potrRecvThread.c の n1_check_health_timeout() 関数内、peer_path_clear() 呼び出しの直前
peer_path_clear() が dest_addr[k] をゼロクリアします。直前に呼ぶことで、PATH_DISCONNECTED の path_states にはこのパスがまだ alive として反映されます(呼び出し元視点での「最後の疎通状態」)。
注意: PATH_DISCONNECTED 直後の path_states[k] の値については、1:1 モードと N:1 モードで扱いが異なります。
モード PATH_DISCONNECTED 発火時の path_states[k] 1:1 UDP / TCP 0(発火前に path_alive[k] = 0 を設定するため) N:1 UDP 1(peer_path_clear() 前のため dest_addr[k].sin_family == AF_INET のまま) N:1 モードの仕様については、将来の改訂で 1:1 モードに合わせることを検討できます。
FIXME: N:1 UDP でも、PATH_DISCONNECTED 発火時の path_states[k] は 0 としたい。
TCP では、パスの状態変化は TCP 接続の確立・切断に対応します。
FIXME: PING メッセージが有効な場合は、PING メッセージの着信を以て接続にすべきでは。
発火場所: potrConnectThread.c の start_connected_threads() 関数末尾、全スレッド起動成功後
スレッド起動が失敗した場合 (POTR_ERROR を返す場合) は path_alive をセットしないため、PATH_DISCONNECTED は発火しません。
start_connected_threads() は sender_connect_loop() と receiver_accept_loop() の両方から呼ばれます。
発火場所: potrConnectThread.c の sender_connect_loop() と receiver_accept_loop() 内、join_recv_thread() 直後・stop_connected_threads() 直前
stop_connected_threads() の後で tcp_active_paths をデクリメントし、0 になれば reset_all_paths_disconnected() が DISCONNECTED を発火します。PATH_DISCONNECTED → DISCONNECTED の順序が保証されます。
各発火箇所で重複するボイラープレートを静的ヘルパー関数としてまとめます。
potrRecvThread.c に追加します。
potrRecvThread.c に追加します。
potrConnectThread.c に追加します。
すべてのシナリオで「PATH イベントを先に発火し、その直後にセッション全体の変化があれば CONNECTED / DISCONNECTED を発火する」順序が保証されます。
FIXME: サービスオープン時は、すべてのPATHが0で、DISCONNECTED状態から始まること。(初期値が0である前提で、呼び出しプロセスはイベントを追跡する)
| シナリオ | 発火順序 |
|---|---|
| UDP 1:1 シングルパス初受信 (セッション開始) | PATH_CONNECTED(0) → CONNECTED |
| UDP 1:1 追加パス初受信 (セッション既接続) | PATH_CONNECTED(k) のみ |
| UDP 1:1 一部パスのみタイムアウト | PATH_DISCONNECTED(k) のみ |
| UDP 1:1 全パスタイムアウト | PATH_DISCONNECTED(0) ... PATH_DISCONNECTED(n) → DISCONNECTED |
| UDP 1:1 タイムアウト後の復帰 (セッション既 CONNECTED) | PATH_CONNECTED(k) のみ |
| UDP 1:1 タイムアウト後の復帰 (セッション再接続) | PATH_CONNECTED(k) → CONNECTED |
| UDP N:1 新規ピア・新規パス | PATH_CONNECTED(k) → CONNECTED |
| UDP N:1 既存ピアへの新規パス | PATH_CONNECTED(k) のみ |
| UDP N:1 パスタイムアウト (他パスあり) | PATH_DISCONNECTED(k) のみ |
| UDP N:1 全パスタイムアウト → ピア削除 | PATH_DISCONNECTED(k) ... → DISCONNECTED |
| TCP 初パス接続確立 | PATH_CONNECTED(0) → CONNECTED (初パケット受信時) |
| TCP 追加パス接続確立 | PATH_CONNECTED(k) のみ |
| TCP 一部パス切断 | PATH_DISCONNECTED(k) のみ |
| TCP 全パス切断 | PATH_DISCONNECTED(0) ... PATH_DISCONNECTED(n) → DISCONNECTED |
| potrCloseService() 呼び出し | 発火なし (既存の設計方針を踏襲) |
TCP における PATH_CONNECTED と CONNECTED の間隔
TCP では PATH_CONNECTED (TCP 接続確立) と CONNECTED (初パケット受信) は別スレッドから発火します。
- PATH_CONNECTED: 接続管理スレッドが start_connected_threads() の末尾で発火
- CONNECTED: recv スレッドが最初のパケットを受信したときに発火
典型的な発火順序は PATH_CONNECTED → (短い時間差) → CONNECTED ですが、スレッドスケジューリングにより逆転する可能性があります。アプリケーションは CONNECTED を受け取るまでデータ送信が可能になったとみなさないでください。
実装時に変更が必要なファイルを以下に示します。
| ファイル | 変更種別 | 変更内容 |
|---|---|---|
| prod/porter/include/porter_type.h | 変更 | PotrEvent に POTR_EVENT_PATH_CONNECTED = 3、POTR_EVENT_PATH_DISCONNECTED = 4 を追加。PotrEvent および PotrRecvCallback の Doxygen コメントを更新(len/data の意味、発火順序保証、TCP スレッドからの呼び出し注記) |
| prod/porter/libsrc/porter/potrContext.h | 変更 | PotrContext_ 構造体に volatile int path_alive[POTR_MAX_PATH] を health_alive の直後に追加 |
| prod/porter/libsrc/porter/thread/potrRecvThread.c | 変更 | fire_path_event() と n1_fire_path_event() を静的関数として追加。update_path_recv() 末尾に PATH_CONNECTED 発火処理を追加。check_health_timeout() のパス単位タイムアウト判定に PATH_DISCONNECTED 発火処理を追加。n1_update_path_recv() の新規パス学習ブランチ末尾に PATH_CONNECTED 発火処理を追加。n1_check_health_timeout() の peer_path_clear() 直前に PATH_DISCONNECTED 発火処理を追加 |
| prod/porter/libsrc/porter/thread/potrConnectThread.c | 変更 | tcp_fire_path_event() を静的関数として追加。start_connected_threads() の return POTR_SUCCESS 直前に PATH_CONNECTED 発火処理を追加。sender_connect_loop() と receiver_accept_loop() の join_recv_thread() 直後・stop_connected_threads() 直前に PATH_DISCONNECTED 発火処理を追加 |
実装後に以下のドキュメントを更新してください。
| ファイル | 更新内容 |
|---|---|
| docs/api.md | 新イベント種別 POTR_EVENT_PATH_CONNECTED・POTR_EVENT_PATH_DISCONNECTED の説明と呼び出し規約 (len/data) を追記 |
| docs/architecture.md | イベント管理セクション (CONNECTED/DISCONNECTED の説明箇所) に PATH イベントの発火条件を追記。PotrContext_ の説明に path_alive[] フィールドを追加 |
| docs/sequence.md | 各シナリオのシーケンス図に PATH_CONNECTED / PATH_DISCONNECTED の発火タイミングを追加。「補足: 接続状態の遷移」の状態遷移図にパス単位の遷移を追加 |
| 観点 | POTR_EVENT_CONNECTED / DISCONNECTED | POTR_EVENT_PATH_CONNECTED / PATH_DISCONNECTED |
|---|---|---|
| 粒度 | セッション全体 | 個別パス (0〜3) |
| len | 常に 0 | パスインデックス (0〜POTR_MAX_PATH-1) |
| data | 常に NULL | const int *path_states (POTR_MAX_PATH 要素) |
| TCP 発火スレッド | recv スレッド / 接続管理スレッド | 接続管理スレッド (PATH_CONNECTED/DISCONNECTED) |
| N:1 peer_id | ピア ID | ピア ID (同じ) |
| シングルパス時 | 従来どおり発火 | PATH_* が CONNECTED/DISCONNECTED と同時に発火 |
| potrCloseService() | 発火しない | 発火しない |
| ヘルスチェック無効時 | FIN/REJECT のみ | パスタイムアウトは発生しない (FIN/REJECT は従来どおり) |