# 機能設計書 81-sleep関数

## 概要

本ドキュメントは、mrubyのmruby-sleep gemが提供するKernel#sleepおよびKernel#usleepメソッドの機能設計を記述する。これらのメソッドは、現在のスレッド（実行コンテキスト）を指定時間だけ一時停止させるスリープ機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：組み込みシステムやスクリプト実行環境において、時間遅延が必要な場面は多い。センサーの読み取り間隔の制御、通信のタイミング調整、リトライ待機、デバッグ時の動作確認など、プログラムの実行を一定時間一時停止させる機能は基本的な要件である。mruby-sleepはCRubyのKernel#sleepに相当する機能をmruby環境で提供する。

**機能の利用シーン**：ポーリング処理におけるウェイト、組み込みデバイスのタイミング制御、テスト実行時の遅延シミュレーション、プロトタイピングにおける簡易的な時間管理等で利用される。

**主要な処理内容**：
1. `sleep(duration)` - 秒単位（浮動小数点対応）でスリープし、実際にスリープした秒数（整数）を返す
2. `usleep(microseconds)` - マイクロ秒単位（整数）でスリープし、実際にスリープしたマイクロ秒数を返す
3. 負の値が指定された場合はArgumentErrorを発生させる
4. Windows/POSIX両対応のクロスプラットフォーム実装

**関連システム・外部連携**：OSのsleep/usleepシステムコール、WindowsのSleep API、gettimeofday/GetSystemTimeAsFileTime

**権限による制御**：特になし。Kernelモジュールのプライベートメソッドとして全オブジェクトから利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | CLI | 主画面 | mrubyスクリプト内でsleep/usleepを呼び出す |

## 機能種別

システム制御処理（時間遅延制御）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| sec (sleep) | Float/Integer | Yes | スリープ秒数。MRB_NO_FLOAT定義時はIntegerのみ | 0以上であること。負の値はArgumentError |
| usec (usleep) | Integer | Yes | スリープマイクロ秒数 | 0以上であること。負の値はArgumentError |

### 入力データソース

Ruby関数呼び出しの引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| sleep戻り値 | Integer | 実際にスリープした秒数（time_t差分） |
| usleep戻り値 | Integer | 実際にスリープしたマイクロ秒数 |

### 出力先

Ruby関数の戻り値

## 処理フロー

### 処理シーケンス

```
1. sleep(sec)の場合
   ├─ time(0)で開始時刻を記録
   ├─ MRB_NO_FLOATの有無で分岐
   │   ├─ Float有効: mrb_get_args(mrb, "f", &sec)でFloat取得、usleep(sec*1000000)呼び出し
   │   └─ Float無効: mrb_get_args(mrb, "i", &sec)でInteger取得、sleep(sec)呼び出し
   ├─ 負の値の場合: ArgumentError発生
   ├─ time(0)で終了時刻を記録
   └─ 差分を整数で返却

2. usleep(usec)の場合
   ├─ gettimeofday/GetSystemTimeAsFileTimeで開始時刻を記録
   ├─ mrb_get_args(mrb, "i", &usec)でInteger取得
   ├─ 負の値の場合: ArgumentError発生
   ├─ usleep(usec)でスリープ実行
   ├─ gettimeofday/GetSystemTimeAsFileTimeで終了時刻を記録
   └─ マイクロ秒差分を整数で返却
```

### フローチャート

```mermaid
flowchart TD
    A[sleep/usleep呼び出し] --> B{パラメータ取得}
    B --> C{値 >= 0?}
    C -->|No| D[ArgumentError発生]
    C -->|Yes| E[開始時刻記録]
    E --> F[OS sleep/usleep実行]
    F --> G[終了時刻記録]
    G --> H[経過時間を計算]
    H --> I[整数値として返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-81-1 | 非負制約 | スリープ時間は0以上でなければならない | 常時 |
| BR-81-2 | Float対応 | MRB_NO_FLOAT未定義時はFloat値を受け付ける | コンパイル設定依存 |
| BR-81-3 | 戻り値型 | 戻り値は常にInteger（mrb_fixnum_value） | 常時 |

### 計算ロジック

- sleepの戻り値: `time(0) - beg` (秒単位の整数)
- usleepの戻り値（POSIX）: `ed_tm.tv_usec - st_tm.tv_usec` (繰り上がり考慮)
- usleepの戻り値（Windows）: `(ed_time - st_time) / 10` (100ナノ秒単位から変換)

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

該当なし（データベース操作は行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | 負の値が指定された場合 | "time interval must not be negative" メッセージ表示 |

### リトライ仕様

リトライは行わない。

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

該当なし

## パフォーマンス要件

- sleepは秒単位の精度（time_tベース）
- usleepはマイクロ秒単位の精度（gettimeofday/FILETIMEベース）
- OSのスケジューラに依存するため、実際のスリープ時間は指定値より長くなる可能性がある

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

特になし。OS提供のsleep/usleepを呼び出すのみ。

## 備考

- 引数なしの永久スリープ（forever sleep）は未実装
- mruby-taskが有効な場合、sleep/usleepはタスクスケジューラ対応版に上書きされる

---

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

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

### 推奨読解順序

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

本gemは独自のデータ構造を持たない。OSのtime_t、struct timeval、FILETIME等のシステム型を使用する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sleep.c | `mrbgems/mruby-sleep/src/sleep.c` | ヘッダインクルード（29-40行目）でプラットフォーム分岐を理解する |

**読解のコツ**: `#ifdef _WIN32` によるプラットフォーム分岐が多い。Windows環境では `Sleep()` APIを使い、POSIX環境では `unistd.h` の `sleep()`/`usleep()` を使う。32行目でWindowsのsleepマクロが秒からミリ秒への変換を行っている点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sleep.c | `mrbgems/mruby-sleep/src/sleep.c` | `mrb_mruby_sleep_gem_init`（173-177行目）でKernelモジュールへのメソッド登録を確認 |

**主要処理フロー**:
1. **175行目**: `mrb_define_module_function_id` でKernel#sleepを登録（引数1つ必須）
2. **176行目**: `mrb_define_module_function_id` でKernel#usleepを登録（引数1つ必須）

#### Step 3: sleep関数の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sleep.c | `mrbgems/mruby-sleep/src/sleep.c` | `f_sleep`（60-89行目）でsleepの実装を確認 |

**主要処理フロー**:
- **63行目**: `time(0)` で開始時刻を記録
- **65-71行目**: Float有効時 - `mrb_get_args(mrb, "f", &sec)` でFloat取得、`usleep(sec * 1000000)` でマイクロ秒変換して呼び出し
- **76-80行目**: Float無効時 - `mrb_get_args(mrb, "i", &sec)` でInteger取得、`sleep(sec)` で直接呼び出し
- **73,83行目**: 負の値チェックで `mrb_raise` を呼ぶ
- **86-88行目**: `time(0) - beg` で経過秒数を計算して返却

#### Step 4: usleep関数の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sleep.c | `mrbgems/mruby-sleep/src/sleep.c` | `f_usleep`（107-159行目）でusleepの実装を確認 |

**主要処理フロー**:
- **120-124行目**: プラットフォーム別に開始時刻を取得（Windows: GetSystemTimeAsFileTime、POSIX: gettimeofday）
- **127行目**: `mrb_get_args(mrb, "i", &usec)` でマイクロ秒数を取得
- **129-134行目**: 非負チェック後、OSの`usleep(usec)`を呼び出し
- **136-156行目**: プラットフォーム別に終了時刻を取得し、差分を計算
- **158行目**: 経過マイクロ秒数を整数で返却

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

```
mrb_mruby_sleep_gem_init
    │
    ├─ mrb_define_module_function_id(sleep) ──▶ f_sleep
    │                                              ├─ time(0) [開始]
    │                                              ├─ usleep() or sleep() [OS呼び出し]
    │                                              ├─ time(0) [終了]
    │                                              └─ mrb_fixnum_value [戻り値]
    │
    └─ mrb_define_module_function_id(usleep) ──▶ f_usleep
                                                   ├─ gettimeofday/GetSystemTimeAsFileTime [開始]
                                                   ├─ usleep() [OS呼び出し]
                                                   ├─ gettimeofday/GetSystemTimeAsFileTime [終了]
                                                   └─ mrb_fixnum_value [戻り値]
```

### データフロー図

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

Ruby引数(秒/マイクロ秒) ───▶ f_sleep / f_usleep ───▶ 経過時間(Integer)
                              │
                              ├─ バリデーション
                              ├─ 開始時刻記録
                              ├─ OS sleep/usleep呼び出し
                              └─ 終了時刻記録・差分計算
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sleep.c | `mrbgems/mruby-sleep/src/sleep.c` | ソース | sleep/usleep関数の実装 |
| mrbgem.rake | `mrbgems/mruby-sleep/mrbgem.rake` | 設定 | gem仕様定義（名前、ライセンス、著者） |
| sleep_test.rb | `mrbgems/mruby-sleep/test/sleep_test.rb` | テスト | sleep/usleepのテストケース |
| sleep.rb | `mrbgems/mruby-sleep/example/sleep.rb` | サンプル | 使用例 |
