# 機能設計書 31-ストリーム書き込み

## 概要

本ドキュメントは、libuvのストリーム書き込み機能（uv_write()/uv_write2()）の設計仕様を記述する。ストリームへの非同期データ書き込みおよびIPCパイプを介したハンドル送信機能を提供する。

### 本機能の処理概要

uv_write()およびuv_write2()は、ストリーム（TCP、パイプ、TTY）に対して非同期にデータを書き込むための関数である。uv_write2()はIPCパイプを通じてファイルディスクリプタを別プロセスに送信する機能を追加で提供する。

**業務上の目的・背景**：ネットワーク通信やプロセス間通信において、ストリームへのデータ書き込みは最も基本的な操作の1つである。ノンブロッキングI/Oモデルでは、書き込み操作を非同期に行い、完了時にコールバックで通知を受けることで、イベントループをブロックすることなく高スループットなデータ送信を実現する。uv_write2()によるハンドル送信は、マルチプロセスアーキテクチャにおいてプロセス間でソケットを共有するために必要な機能である。

**機能の利用シーン**：TCPサーバーがクライアントにレスポンスを返す場合、パイプを通じたプロセス間通信、TTY端末への出力、Node.jsのclusterモジュールで子プロセスへソケットを渡す場合など、あらゆるストリーム型の書き込み操作に使用される。

**主要な処理内容**：
1. ストリームの状態検証（書き込み可能状態、ファイルディスクリプタの有効性確認）
2. uv_write_tリクエストの初期化（バッファコピー、キューサイズ更新）
3. 書き込みキューへのリクエスト追加
4. キューが空の場合は即座にwritev()システムコールを試行
5. 書き込み完了時またはエラー発生時にコールバック呼び出し

**関連システム・外部連携**：OSのwritev()、sendmsg()システムコールを使用。IPCパイプ経由のハンドル送信ではSCM_RIGHTSメカニズムを使用。

**権限による制御**：特になし。ストリームがUV_HANDLE_WRITABLEフラグを持つ場合のみ書き込みが許可される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | ストリーム | 主画面 | uv_write()/uv_write2()による非同期書き込みのAPI定義 |
| 35 | ネットワーク | 参照画面 | クライアントへのデータ書き込みの使用例 |

## 機能種別

データ送信処理（非同期I/O）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| req | uv_write_t* | Yes | 書き込みリクエスト構造体 | NULLでないこと |
| handle | uv_stream_t* | Yes | 書き込み対象のストリームハンドル | TCP/パイプ/TTYのいずれか |
| bufs | const uv_buf_t[] | Yes | 書き込みバッファ配列 | 1以上1048576以下 |
| nbufs | unsigned int | Yes | バッファ数 | 1以上1048576以下 |
| send_handle | uv_stream_t* | No | 送信するハンドル（uv_write2のみ） | IPCパイプ経由の場合のみ |
| cb | uv_write_cb | No | 完了コールバック関数 | NULLでも可 |

### 入力データソース

アプリケーションコードから直接呼び出し。書き込みデータはアプリケーションが確保したバッファで提供される。バッファの内容はコールバックが呼ばれるまで有効に保つ必要がある。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 0: 成功、負値: エラーコード |
| コールバック引数 req | uv_write_t* | 完了した書き込みリクエスト |
| コールバック引数 status | int | 0: 成功、負値: エラーコード |

### 出力先

ストリームに関連付けられたファイルディスクリプタへの書き込み。コールバックを通じてアプリケーションに結果通知。

## 処理フロー

### 処理シーケンス

```
1. uv_write() -> uv_write2()へ委譲（send_handle=NULL）
   └─ uv_write()はuv_write2()のラッパーとして実装

2. uv__check_before_write()による事前検証
   ├─ ストリーム型チェック（TCP/パイプ/TTYのみ許可）
   ├─ バッファ数チェック（1〜1048576）
   ├─ ファイルディスクリプタ有効性チェック
   ├─ UV_HANDLE_WRITABLEフラグチェック
   └─ send_handle指定時のIPCパイプ検証

3. uv_write_tリクエスト初期化
   ├─ uv__req_init()でリクエスト登録
   ├─ バッファ配列のコピー（小さい場合はbufsml使用、大きい場合はmalloc）
   └─ write_queue_sizeの加算

4. 書き込みキュー（write_queue）にリクエスト追加

5. 即時書き込み試行
   ├─ connect_req中 -> 何もしない
   ├─ キューが空だった -> uv__write()で即時書き込み試行
   └─ キューに既存データあり -> io_watcherでPOLLOUT監視開始

6. 非同期書き込み実行（uv__write）
   ├─ uv__try_write()でwritev()/sendmsg()実行
   ├─ 部分書き込みの場合はバッファインデックス更新
   └─ 完了時にuv__write_req_finish()

7. コールバック呼び出し
   └─ uv__write_callbacks()経由でcb(req, status)呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[uv_write/uv_write2 呼び出し] --> B[uv__check_before_write]
    B -->|エラー| C[エラーコード返却]
    B -->|OK| D[リクエスト初期化]
    D --> E[バッファコピー]
    E -->|メモリ不足| F[UV_ENOMEM返却]
    E -->|OK| G[write_queueに追加]
    G --> H{接続中?}
    H -->|Yes| I[何もしない]
    H -->|No| J{キュー空だった?}
    J -->|Yes| K[uv__write で即時書き込み]
    J -->|No| L[POLLOUT監視開始]
    K --> M[0返却]
    L --> M
    I --> M
    K --> N{書き込み完了?}
    N -->|完了| O[uv__write_req_finish]
    N -->|部分書き込み| P[POLLOUT監視開始]
    O --> Q[コールバック呼び出し]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | ストリーム型制約 | TCP、名前付きパイプ、TTYのストリームのみ書き込み可能 | 常時 |
| BR-31-02 | バッファ数制限 | nbufsは1以上1048576（1024*1024）以下 | 常時 |
| BR-31-03 | 書き込み可能状態 | UV_HANDLE_WRITABLEフラグが設定されていること | 常時 |
| BR-31-04 | IPC制約 | send_handleはIPCパイプのみで使用可能 | uv_write2使用時 |
| BR-31-05 | バッファ有効期間 | バッファ内容はコールバック呼び出しまで有効であること | 常時 |
| BR-31-06 | 即時書き込み | キューが空の場合は即座にシステムコールを試行 | キュー空時 |

### 計算ロジック

- write_queue_size: 全バッファの合計サイズが加算される（uv__count_bufs()で計算）
- バッファ管理: nbufsがARRAY_SIZE(req->bufsml)（通常4）以下の場合はスタック上の小バッファ（bufsml）を使用し、超える場合はヒープ確保

## データベース操作仕様

本機能はデータベース操作を行わない。

### 操作別データベース影響一覧

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | 入力エラー | 不正なストリーム型またはバッファ数が範囲外 | パラメータを修正して再試行 |
| UV_EBADF | 状態エラー | ファイルディスクリプタが無効 | ストリームを再初期化 |
| UV_EPIPE | 状態エラー | ストリームが書き込み不可 | 接続を確認して再接続 |
| UV_ENOMEM | リソースエラー | バッファ配列のメモリ確保失敗 | メモリを解放して再試行 |
| UV_ENOSYS | 環境エラー | Cygwin/MSYSでのハンドル送信 | 非対応環境 |
| UV_ECANCELED | 接続エラー | 接続失敗時のキュー内リクエスト | 再接続後に再送信 |

### リトライ仕様

書き込みでEAGAIN/EWOULDBLOCKが返された場合、libuvが自動的にPOLLOUT監視を設定しリトライする。アプリケーション側でのリトライは不要。

## トランザクション仕様

該当なし。書き込みリクエストは個別に完了またはエラーとなる。

## パフォーマンス要件

- キューが空の場合は即座にwritev()を試行し、レイテンシを最小化する
- writev()により複数バッファを1回のシステムコールで書き込み可能
- ループスターベーション防止のため、1回のI/Oイベントあたり最大32回の書き込み試行に制限（uv__write内のcount変数）
- 小バッファ数（4以下）の場合はヒープ確保を回避

## セキュリティ考慮事項

- send_handleによるファイルディスクリプタ送信はIPCパイプ経由のみに制限
- バッファ数の上限チェック（1048576）により符号変換バグを防止
- 送信対象ハンドルのクローズ中チェック（uv__is_closing）

## 備考

- uv_write()はuv_write2()の単純なラッパーであり、send_handle=NULLで呼び出す
- macOS特有のEPROTOTYPEエラーはECONNRESETに変換される
- uv_buf_t配列自体はコピーされるためコールバック前に解放可能だが、バッファの中身（base/len）は有効に保つ必要がある

---

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

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、書き込みリクエストとストリームのデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | uv_write_s構造体の定義（570-576行目）。cb、send_handle、handleフィールドを確認 |
| 1-2 | uv.h | `include/uv.h` | UV_STREAM_FIELDSマクロ（520-526行目）。write_queue_size、alloc_cb、read_cbを確認 |
| 1-3 | uv.h | `include/uv.h` | uv_stream_s構造体（535-538行目）。UV_HANDLE_FIELDSとUV_STREAM_FIELDSで構成 |

**読解のコツ**: libuvではCのマクロによる「擬似継承」を用いている。UV_HANDLE_FIELDSが基底クラスのフィールド、UV_STREAM_FIELDSがストリーム固有のフィールドを定義し、各具象型（uv_tcp_s等）にインライン展開される。

#### Step 2: エントリーポイントを理解する

書き込みAPIの公開インターフェースを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | uv.h | `include/uv.h` | uv_write()のシグネチャ（550-554行目） |
| 2-2 | uv.h | `include/uv.h` | uv_write2()のシグネチャ（555-560行目） |

**主要処理フロー**:
1. **550-554行目**: uv_write()のAPI宣言。req、handle、bufs、nbufs、cbを受け取る
2. **555-560行目**: uv_write2()のAPI宣言。send_handleパラメータが追加

#### Step 3: 書き込み実装を理解する

Unix環境での書き込み処理の実装を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | stream.c | `src/unix/stream.c` | uv_write()（1413-1418行目）- uv_write2()への委譲 |
| 3-2 | stream.c | `src/unix/stream.c` | uv__check_before_write()（1297-1339行目）- 事前検証ロジック |
| 3-3 | stream.c | `src/unix/stream.c` | uv_write2()（1341-1407行目）- 実際の書き込みキューイング処理 |
| 3-4 | stream.c | `src/unix/stream.c` | uv__try_write()（753-837行目）- writev()/sendmsg()の実行 |
| 3-5 | stream.c | `src/unix/stream.c` | uv__write()（839-870行目）- キューからの書き込みループ |

**主要処理フロー**:
- **1297-1339行目**: ストリーム型チェック、バッファ数チェック、FD有効性チェック、書き込み可能フラグチェック
- **1360行目**: empty_queueフラグでキューが空かどうかを記録
- **1370-1378行目**: バッファ配列のコピー。bufsmlが使えればスタック、超える場合はmalloc
- **1380行目**: write_queue_sizeにバッファサイズを加算
- **1383行目**: write_queueの末尾にリクエストを追加
- **1392-1393行目**: キューが空だった場合はuv__write()で即時書き込みを試行

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

```
uv_write(req, handle, bufs, nbufs, cb)
    │
    └─ uv_write2(req, handle, bufs, nbufs, NULL, cb)
           │
           ├─ uv__check_before_write(stream, nbufs, send_handle)
           │      ├─ ストリーム型チェック
           │      ├─ nbufsバリデーション
           │      ├─ FD有効性チェック
           │      └─ send_handle検証（IPC専用）
           │
           ├─ uv__req_init(loop, req, UV_WRITE)
           │
           ├─ uv__count_bufs(bufs, nbufs)  ... write_queue_size加算
           │
           └─ uv__write(stream)  ... キュー空時に即時実行
                  │
                  └─ uv__try_write(stream, bufs, nbufs, send_handle)
                         │
                         ├─ sendmsg()  ... send_handleあり時
                         └─ writev()   ... 通常書き込み時
```

### データフロー図

```
[入力]                     [処理]                          [出力]

uv_buf_t bufs[]  ───▶ uv_write2()                    ───▶ write_queue
                       │                                    │
                       ├─ バッファコピー                      │
                       └─ キューイング                       ▼
                                                      uv__write()
                                                          │
                                              writev()/sendmsg()
                                                          │
                                                          ▼
                                              ファイルディスクリプタへの
                                              データ送出
                                                          │
                                                          ▼
                                              uv_write_cb コールバック
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | uv_write_t構造体定義、API宣言 |
| stream.c | `src/unix/stream.c` | ソース | Unix環境でのストリーム書き込み実装 |
| stream.c | `src/win/stream.c` | ソース | Windows環境でのストリーム書き込み実装 |
| uv-common.h | `src/uv-common.h` | ヘッダー | 内部共通定義 |
| internal.h | `src/unix/internal.h` | ヘッダー | Unix内部関数宣言 |
