[インデックス 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()
システムコールをタイムアウト機能のみで使用することにあります。
-
時間の変換:
func Sleep(ns int64) *os.Error
は、ns
というint64
型の引数でナノ秒を受け取ります。var tv syscall.Timeval;
でsyscall.Timeval
型の変数を宣言します。この構造体はselect()
システムコールに渡す時間情報を保持します。syscall.Nstotimeval(ns, &tv);
を呼び出して、入力されたナノ秒ns
をtv
(syscall.Timeval
型) に変換します。これにより、tv
には秒とマイクロ秒に分解されたスリープ時間が設定されます。
-
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.Pointer
をuintptr
(符号なし整数型) に変換します。これにより、ポインタの値を整数として扱うことができます。int64(...)
:uintptr
をint64
に変換します。これはsyscall.Syscall6
が引数をint64
で受け取るためです。- この一連の変換は、Goの型システムを迂回して、
syscall.Timeval
構造体のアドレスを直接システムコールに渡すために必要です。
- 最後の
0
引数:select()
の最後の引数(通常は使用されないか、特定のフラグ用)に対応します。 r1, r2, err
: システムコールからの戻り値を受け取ります。err
はシステムコールが失敗した場合のエラー番号です。
-
エラーハンドリング:
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);
: 入力されたナノ秒ns
をtv
に変換します。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言語の
time
パッケージのドキュメント (現在のバージョン): https://pkg.go.dev/time - Go言語の
syscall
パッケージのドキュメント (現在のバージョン): https://pkg.go.dev/syscall select()
システムコールに関するmanページ (Linux): https://man7.org/linux/man-pages/man2/select.2.html
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- Unix系OSのシステムプログラミングに関する一般的な知識
select()
システムコールに関する情報 (例: Wikipedia, プログラミング関連のブログ記事など)- Go言語の初期の設計に関する議論やメーリングリストのアーカイブ (Goの歴史的背景を理解するため)
- Go Nutsメーリングリストアーカイブ: https://groups.google.com/g/golang-nuts
- Go開発者メーリングリストアーカイブ: https://groups.google.com/g/golang-dev
- Go言語の
unsafe
パッケージに関するドキュメント: https://pkg.go.dev/unsafe - Go言語の
os
パッケージに関するドキュメント: https://pkg.go.dev/os - Go言語の
Timeval
構造体に関する情報 (Goのソースコードまたは古いドキュメント)