Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1686] ファイルの概要

このコミットは、Go言語の標準ライブラリである time パッケージに sleep.go という新しいファイルを追加し、Sleep 関数を実装するものです。この関数は指定されたナノ秒間、現在のゴルーチンをスリープさせる機能を提供します。

コミット

time パッケージに sleep.go を追加し、Sleep 関数を実装。ビルドエラーを修正するためのコミットです。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/aa35aee07c9872f902512924f7a5fc6a6b58a611

元コミット内容

fix build - missed this file before

TBR=r
OCL=25074
CL=25074

変更の背景

このコミットは、Go言語の初期開発段階におけるビルドプロセスの修正の一環として行われました。コミットメッセージにある「fix build - missed this file before」という記述から、以前のコミットで sleep.go ファイルがビルドシステムに適切に組み込まれていなかったことが示唆されます。Go言語の標準ライブラリは、その設計思想として、OS固有の機能へのアクセスを抽象化し、クロスプラットフォームで一貫したAPIを提供することを目指しています。sleep 機能は、プログラムの実行フローを一時停止させる基本的な操作であり、多くのアプリケーションで必要とされるため、標準ライブラリの一部として提供されることは自然な流れです。このコミットは、time パッケージにおける基本的な時間操作機能の整備を進めるためのものであり、Go言語の初期の安定化と機能拡充に貢献しています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびシステムプログラミングに関する基本的な知識が必要です。

  • Go言語のパッケージシステム: Go言語はパッケージによってコードをモジュール化します。package time は時間関連の機能を提供するパッケージであることを示します。
  • import: 外部パッケージの機能を利用するために import 文を使用します。ここでは os, syscall, unsafe パッケージがインポートされています。
    • os パッケージ: オペレーティングシステムとのインタフェースを提供します。os.Error はエラーハンドリングに使用されます。
    • syscall パッケージ: 低レベルのシステムコールへのアクセスを提供します。OS固有の機能に直接アクセスするために使用されます。
    • unsafe パッケージ: Goの型安全性をバイパスする操作(ポインタ演算など)を可能にします。ここでは unsafe.Pointer が使用されています。
  • システムコール (Syscall): オペレーティングシステムが提供するサービス(ファイルI/O、プロセス管理、メモリ管理など)をプログラムから呼び出すためのインタフェースです。syscall.Syscall6 は、6つの引数を取るシステムコールを呼び出すためのGoの関数です。
  • select() システムコール: Unix系OSにおいて、複数のファイルディスクリプタ(ソケット、パイプなど)のI/O準備状況を監視したり、タイムアウトを設定して待機したりするために使用されるシステムコールです。このコミットでは、ファイルディスクリプタの監視ではなく、タイムアウト機能を利用してスリープを実現しています。select の第一引数 nfds は監視するファイルディスクリプタの最大値+1ですが、ここではファイルディスクリプタを監視しないため 0 が渡されています。
  • syscall.Timeval 構造体: select() システムコールなどで使用される時間値を表現するための構造体です。秒とマイクロ秒で時間を指定します。
  • syscall.Nstotimeval 関数: ナノ秒単位の時間を syscall.Timeval 構造体に変換するGoのヘルパー関数です。
  • os.ErrnoToError 関数: システムコールが返すエラー番号 (errno) をGoの os.Error 型に変換する関数です。

技術的詳細

このコミットで追加された Sleep 関数は、Go言語で指定されたナノ秒間プログラムの実行を一時停止させるための実装です。その核心は、Unix系OSの select() システムコールをタイムアウト機能のみで使用することにあります。

  1. 時間の変換:

    • func Sleep(ns int64) *os.Error は、ns という int64 型の引数でナノ秒を受け取ります。
    • var tv syscall.Timeval;syscall.Timeval 型の変数を宣言します。この構造体は select() システムコールに渡す時間情報を保持します。
    • syscall.Nstotimeval(ns, &tv); を呼び出して、入力されたナノ秒 nstv (syscall.Timeval 型) に変換します。これにより、tv には秒とマイクロ秒に分解されたスリープ時間が設定されます。
  2. select() システムコールの呼び出し:

    • r1, r2, err := syscall.Syscall6(syscall.SYS_SELECT, 0, 0, 0, 0, int64(uintptr(unsafe.Pointer(&tv))), 0);Sleep 関数の主要な部分です。
    • syscall.Syscall6 は、6つの引数を取るシステムコールを呼び出すためのGoの関数です。
    • syscall.SYS_SELECT: select() システムコールに対応する定数です。
    • 最初の4つの 0 引数: select()readfds, writefds, exceptfds (監視するファイルディスクリプタセット) および nfds (ファイルディスクリプタの最大値+1) に対応します。ここではファイルディスクリプタを監視しないため、すべて 0 が渡されます。
    • int64(uintptr(unsafe.Pointer(&tv))): これが select() システムコールの timeout 引数に渡される部分です。
      • &tv: tv 変数のメモリアドレスを取得します。
      • unsafe.Pointer(&tv): tv のアドレスを汎用ポインタ型 unsafe.Pointer に変換します。これは型チェックをバイパスします。
      • uintptr(...): unsafe.Pointeruintptr (符号なし整数型) に変換します。これにより、ポインタの値を整数として扱うことができます。
      • int64(...): uintptrint64 に変換します。これは syscall.Syscall6 が引数を int64 で受け取るためです。
      • この一連の変換は、Goの型システムを迂回して、syscall.Timeval 構造体のアドレスを直接システムコールに渡すために必要です。
    • 最後の 0 引数: select() の最後の引数(通常は使用されないか、特定のフラグ用)に対応します。
    • r1, r2, err: システムコールからの戻り値を受け取ります。err はシステムコールが失敗した場合のエラー番号です。
  3. エラーハンドリング:

    • return os.ErrnoToError(err); は、syscall.Syscall6 から返されたエラー番号 err をGoの os.Error 型に変換し、Sleep 関数の戻り値として返します。これにより、Goの慣用的なエラーハンドリングメカニズムに統合されます。

この実装は、Go言語がまだ初期段階であり、標準ライブラリが成熟していなかった時期のものです。後のGoのバージョンでは、time.Sleep はより高精度でプラットフォームに依存しない方法で実装されるようになります(例えば、Goランタイムのスケジューラと連携して、より効率的なスリープを実現するなど)。しかし、このコミットは、GoがどのようにしてOSの低レベル機能にアクセスし、それを抽象化してユーザーフレンドリーなAPIを提供しようとしていたかを示す良い例です。

コアとなるコードの変更箇所

src/lib/time/sleep.go ファイルが新規追加されています。

--- /dev/null
+++ b/src/lib/time/sleep.go
@@ -0,0 +1,20 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package time
+
+import (
+	"os";
+	"syscall";
+	"unsafe";
+)
+
+func Sleep(ns int64) *os.Error {
+	var tv syscall.Timeval;
+	syscall.Nstotimeval(ns, &tv);
+	r1, r2, err := syscall.Syscall6(syscall.SYS_SELECT, 0, 0, 0, 0,
+		int64(uintptr(unsafe.Pointer(&tv))), 0);
+	return os.ErrnoToError(err);
+}
+

コアとなるコードの解説

上記のコードスニペットは、Go言語の time パッケージに Sleep 関数を導入するものです。

  • ライセンスヘッダ: ファイルの冒頭には、Go言語の標準的なBSDスタイルのライセンスヘッダが含まれています。これは、Goプロジェクトのすべてのソースコードに適用されるものです。
  • パッケージ宣言: package time は、このファイルが time パッケージの一部であることを示します。
  • インポート:
    • "os": オペレーティングシステムとのインタフェースを提供します。特に os.Error を使用してエラーを返します。
    • "syscall": 低レベルのシステムコールへのアクセスを提供します。select システムコール、Timeval 構造体、Nstotimeval 関数、Syscall6 関数、SYS_SELECT 定数を使用します。
    • "unsafe": Goの型安全性をバイパスする機能を提供します。ここではポインタを整数に変換するために unsafe.Pointer が使用されています。
  • Sleep 関数:
    • func Sleep(ns int64) *os.Error: Sleep 関数は ns という int64 型の引数(ナノ秒単位の時間)を受け取り、*os.Error 型の値を返します。エラーがない場合は nil を返します。
    • var tv syscall.Timeval;: syscall.Timeval 型の変数 tv を宣言します。これは select システムコールに渡すタイムアウト値を保持します。
    • syscall.Nstotimeval(ns, &tv);: 入力されたナノ秒 nstv に変換します。syscall.Timeval は秒とマイクロ秒で時間を表現するため、この変換が必要です。
    • r1, r2, err := syscall.Syscall6(syscall.SYS_SELECT, 0, 0, 0, 0, int64(uintptr(unsafe.Pointer(&tv))), 0);:
      • syscall.Syscall6: 6つの引数を取るシステムコールを呼び出すためのGoの関数です。
      • syscall.SYS_SELECT: select() システムコールに対応する定数です。
      • 最初の4つの 0: select()nfds, readfds, writefds, exceptfds に対応します。ここではファイルディスクリプタを監視しないため、すべて 0 が渡されます。
      • int64(uintptr(unsafe.Pointer(&tv))): tv のアドレスを int64 型にキャストして select()timeout 引数に渡します。これはGoの型システムを迂回して、C言語のポインタのようにシステムコールに直接メモリアドレスを渡すためのGoのイディオムです。
      • 0: select() の最後の引数(通常は使用されないか、特定のフラグ用)に対応します。
      • r1, r2, err: システムコールからの戻り値を受け取ります。err はシステムコールが失敗した場合のエラー番号です。
    • return os.ErrnoToError(err);: システムコールが返したエラー番号 err をGoの os.Error 型に変換して返します。これにより、Goの標準的なエラーハンドリングに適合します。

この実装は、Go言語がまだ初期段階であり、time.Sleep のような基本的な機能がOSの低レベルなシステムコールを直接呼び出すことで実現されていたことを示しています。後のGoのバージョンでは、より抽象化された、プラットフォームに依存しない、そしてより効率的なスリープメカニズムが導入されますが、このコミットはGoの進化の過程における重要な一歩です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: https://go.dev/doc/
  • Go言語のソースコードリポジトリ: https://github.com/golang/go
  • Unix系OSのシステムプログラミングに関する一般的な知識
  • select() システムコールに関する情報 (例: Wikipedia, プログラミング関連のブログ記事など)
  • Go言語の初期の設計に関する議論やメーリングリストのアーカイブ (Goの歴史的背景を理解するため)
  • Go言語の unsafe パッケージに関するドキュメント: https://pkg.go.dev/unsafe
  • Go言語の os パッケージに関するドキュメント: https://pkg.go.dev/os
  • Go言語の Timeval 構造体に関する情報 (Goのソースコードまたは古いドキュメント)