[インデックス 10272] ファイルの概要
このコミットは、Go言語のランタイムがOpenBSD上でスレッドの待機処理を行う際に使用するthrsleep
システムコールのプロトタイプ定義を修正し、関連する型定義と定数を追加するものです。これにより、OpenBSD環境でのGoランタイムの安定性と正確性が向上します。
コミット
- コミットハッシュ:
a1c622dfea65416c024c570727e101ecc1479ea8
- 作者: Joel Sing jsing@google.com
- コミット日時: 2011年11月7日 月曜日 11:57:34 -0500
- コミットメッセージ:
runtime: fix prototype for openbsd thrsleep - Fix function prototype for thrsleep(). - Provide enums for clock identifiers. - Provide timespec structure for use with thrsleep(). R=golang-dev, dave, rsc CC=golang-dev https://golang.org/cl/5360042
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a1c622dfea65416c024c570727e101ecc1479ea8
元コミット内容
上記の「コミット」セクションに記載されているコミットメッセージが元のコミット内容です。
変更の背景
Go言語のランタイムは、GoプログラムがOS上で効率的に動作するために、OSのシステムコールを直接利用することが多々あります。特に、ゴルーチン(Goの軽量スレッド)のスケジューリングや同期処理においては、スレッドを一時的に停止させたり、特定の条件が満たされるまで待機させたりする機能が不可欠です。
OpenBSDは、セキュリティとコードの品質に重点を置いたUnix系OSであり、そのシステムコールやライブラリのインターフェースは他のUnix系OS(LinuxやFreeBSDなど)と異なる場合があります。このコミットが行われた当時、GoランタイムのOpenBSD向け実装において、スレッドの待機に使用されるthrsleep
システムコールのプロトタイプ(関数の引数と戻り値の型)がOSの実際の定義と一致していなかったと考えられます。
プロトタイプが一致しない場合、コンパイルエラーや実行時エラー、あるいは未定義の動作を引き起こす可能性があります。また、thrsleep
が正確な時間指定を必要とする場合、時間を表現するためのtimespec
構造体や、どのクロック(時間源)を使用するかを指定するクロック識別子(CLOCK_REALTIME
など)の定義がGoランタイム側に不足していたことも問題でした。
このコミットは、これらの不整合と不足を解消し、GoランタイムがOpenBSD上でthrsleep
システムコールを正しく、かつ安全に利用できるようにするために行われました。
前提知識の解説
Goランタイム
Goランタイムは、Go言語で書かれたプログラムを実行するための環境を提供するソフトウェアです。主な役割は以下の通りです。
- ゴルーチン(Goroutine)のスケジューリング: 多数の軽量スレッドであるゴルーチンを効率的にOSスレッドにマッピングし、並行処理を実現します。
- ガベージコレクション(Garbage Collection): メモリ管理を自動で行い、不要になったメモリを解放します。
- システムコールインターフェース: OSの機能(ファイルI/O、ネットワーク通信、スレッド管理など)をGoプログラムから利用するための橋渡しをします。
- メモリ管理: ヒープメモリの割り当てと解放を行います。
このコミットは、特に「システムコールインターフェース」と「ゴルーチンのスケジューリング」に関連する部分、つまりOSのスレッド管理機能との連携を改善するものです。
OpenBSD
OpenBSDは、セキュリティを最優先に設計されたUnix系オペレーティングシステムです。その開発哲学は「Proactive Security」(能動的なセキュリティ)であり、コードの監査を徹底し、脆弱性を最小限に抑えることに注力しています。このため、他のOSとは異なる独自のシステムコールやライブラリの実装を持つことがあります。GoランタイムがOpenBSDをサポートするためには、これらのOS固有のインターフェースに正確に対応する必要があります。
thrsleep
システムコール
thrsleep
は、OpenBSDに存在するシステムコールの一つで、スレッドを特定の条件が満たされるまで、または指定された時間だけ待機させるために使用されます。これは、POSIX標準のpthread_cond_wait
やnanosleep
のような機能に相当しますが、OpenBSD独自のインターフェースを持つ場合があります。
一般的なスレッドの待機メカニズムでは、以下のような引数を取ることが多いです。
- 識別子(
ident
): 待機する対象を識別するためのポインタ。例えば、条件変数やミューテックスのアドレスなど。 - クロックID(
clock_id
): どの時間源(クロック)に基づいて待機時間を計算するかを指定します。 - タイムアウト(
tsp
): 待機する最大時間を指定する構造体(例:timespec
)。 - ロック(
lock
): 待機に入る前に解放し、待機から戻った後に再取得するミューテックスなどのロック。
このコミットでは、thrsleep
のプロトタイプが修正され、引数の型がより正確に定義されています。
timespec
構造体
timespec
構造体は、秒とナノ秒の精度で時間を表現するために広く使用されるデータ構造です。通常、以下のようなメンバーを持ちます。
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // ナノ秒 (0から999,999,999まで)
};
この構造体は、高精度なタイマーやスレッドの待機時間指定など、時間に関するシステムコールで頻繁に利用されます。Goランタイムがthrsleep
に正確なタイムアウト値を渡すためには、このtimespec
構造体を正しく定義し、利用できる必要があります。
クロック識別子
クロック識別子は、システムが提供する複数の時間源(クロック)の中から、どのクロックを使用して時間を測定するかを指定するための定数です。主なクロック識別子には以下のようなものがあります。
CLOCK_REALTIME
: システムのリアルタイムクロック。壁時計時間とも呼ばれ、ユーザーが設定でき、NTPなどによって調整されます。システム時刻の変更に影響されます。CLOCK_MONOTONIC
: システム起動時からの経過時間を表す単調増加クロック。システム時刻の変更に影響されず、タイマーやタイムアウトの計算に適しています。CLOCK_VIRTUAL
: プロセスがCPUを使用している時間のみをカウントするクロック。CLOCK_PROF
: プロセスと、そのプロセスがシステムコールでカーネルモードにいる時間の両方をカウントするクロック。
thrsleep
のような待機関数では、タイムアウトの計算にCLOCK_MONOTONIC
が使われることが多いですが、用途に応じて適切なクロックを選択する必要があります。このコミットでは、これらのクロック識別子がGoランタイム内で定数として定義されています。
関数プロトタイプ
関数プロトタイプ(または関数宣言)は、関数の名前、戻り値の型、および引数の型と順序をコンパイラに伝えるものです。C言語やC++では、関数を呼び出す前にそのプロトタイプが宣言されている必要があります。これにより、コンパイラは関数呼び出しが正しい引数でなされているか、戻り値の型が適切に扱われているかをチェックできます。
このコミットでは、thrsleep
関数のプロトタイプが修正されており、これはGoランタイムがOpenBSDのthrsleep
システムコールを呼び出す際に、コンパイラが正しい型チェックを行えるようにするために不可欠です。
技術的詳細
このコミットの技術的な核心は、GoランタイムがOpenBSDのネイティブなスレッド同期メカニズムであるthrsleep
システムコールと正しく連携できるようにすることです。具体的には以下の3つの主要な問題に対処しています。
-
thrsleep
プロトタイプの不一致: 以前のGoランタイムでは、thrsleep
の関数プロトタイプがOpenBSDの実際の定義と異なっていたため、コンパイラが誤った引数の型や数を期待し、結果として不正なシステムコール呼び出しやメモリ破損を引き起こす可能性がありました。このコミットでは、thread.c
内のthrsleep
のプロトタイプをOpenBSDの期待する形式に修正しています。 -
timespec
構造体の欠如:thrsleep
システムコールは、タイムアウト値をtimespec
構造体で受け取ることが一般的です。しかし、GoランタイムのOpenBSD向け定義ファイルには、このtimespec
構造体の定義が欠けていました。これにより、Goランタイムがthrsleep
にタイムアウト値を渡すことができませんでした。このコミットでは、32ビット(386/defs.h
)と64ビット(amd64/defs.h
)の両アーキテクチャ向けにtimespec
構造体をGoランタイムの内部表現として追加し、defs.c
でC言語のstruct timespec
をGoの内部型にマッピングするtypedef
を追加しています。 -
クロック識別子の欠如:
thrsleep
は、どのクロック(例:CLOCK_REALTIME
,CLOCK_MONOTONIC
)を使用してタイムアウトを計算するかを指定する引数を取ることがあります。これらのクロック識別子の定数がGoランタイム内に定義されていなかったため、thrsleep
を正確に制御することができませんでした。このコミットでは、thread.c
内でOpenBSDのsys/time.h
からこれらのクロック識別子をenum
としてGoランタイム内に導入しています。
これらの変更により、GoランタイムはOpenBSDのthrsleep
システムコールを、その本来の意図と正確なインターフェースに従って呼び出すことが可能になり、GoプログラムがOpenBSD上でより堅牢なスレッド同期とタイマー機能を利用できるようになります。特に、runtime·thrsleep
の呼び出しでタイムアウト引数にnil
を渡す変更は、プロトタイプ修正とtimespec
の導入により、以前は不正な引数だったものが、タイムアウトなし(無限待機)を意味する有効な引数として扱えるようになったことを示唆しています。
コアとなるコードの変更箇所
src/pkg/runtime/openbsd/386/defs.h
--- a/src/pkg/runtime/openbsd/386/defs.h
+++ b/src/pkg/runtime/openbsd/386/defs.h
@@ -97,6 +97,12 @@ struct StackT {\n \tint32 ss_flags;\n };\n \n+typedef struct Timespec Timespec;\n+struct Timespec {\n+\tint32 tv_sec;\n+\tint32 tv_nsec;\n+};\n+\n typedef struct Timeval Timeval;\
struct Timeval {\
\tint32 tv_sec;
Timespec
構造体の定義が追加されています。32ビット環境(i386)では、tv_sec
とtv_nsec
が両方ともint32
として定義されています。
src/pkg/runtime/openbsd/amd64/defs.h
--- a/src/pkg/runtime/openbsd/amd64/defs.h
+++ b/src/pkg/runtime/openbsd/amd64/defs.h
@@ -100,6 +100,13 @@ struct StackT {\n \tbyte pad_godefs_0[4];\n };\n \n+typedef struct Timespec Timespec;\n+struct Timespec {\n+\tint32 tv_sec;\n+\tbyte pad_godefs_0[4];\n+\tint64 tv_nsec;\n+};\n+\n typedef struct Timeval Timeval;\
struct Timeval {\
\tint64 tv_sec;
Timespec
構造体の定義が追加されています。64ビット環境(amd64)では、tv_sec
がint32
、tv_nsec
がint64
として定義されており、パディングバイト(pad_godefs_0
)も含まれています。これは、64ビットアーキテクチャでのアライメント要件と、tv_nsec
がより広い範囲をカバーする必要があるためと考えられます。
src/pkg/runtime/openbsd/defs.c
--- a/src/pkg/runtime/openbsd/defs.c
+++ b/src/pkg/runtime/openbsd/defs.c
@@ -93,6 +93,7 @@ typedef union sigval $Sigval;\n \n typedef stack_t $StackT;\n \n+typedef struct timespec $Timespec;\
typedef struct timeval $Timeval;\
typedef struct itimerval $Itimerval;
- C言語の
struct timespec
をGoランタイムの内部型である$Timespec
としてtypedef
する行が追加されています。これにより、GoランタイムがOpenBSDのシステムコールインターフェースと連携する際に、timespec
型を正しく扱えるようになります。
src/pkg/runtime/openbsd/thread.c
--- a/src/pkg/runtime/openbsd/thread.c
+++ b/src/pkg/runtime/openbsd/thread.c
@@ -18,13 +18,19 @@ enum\n \n \tESRCH = 3,\n \tENOTSUP = 91,\n+\n+\t// From OpenBSD\'s sys/time.h\n+\tCLOCK_REALTIME = 0,\n+\tCLOCK_VIRTUAL = 1,\n+\tCLOCK_PROF = 2,\n+\tCLOCK_MONOTONIC = 3\n };\n \n extern SigTab runtime·sigtab[];\n \n extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));\n-extern int32 runtime·thrsleep(void *, void *, void*, void *);\n-extern int32 runtime·thrwakeup(void *, int32);\n+extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock);\n+extern int32 runtime·thrwakeup(void *ident, int32 n);\n \n // From OpenBSD\'s <sys/sysctl.h>\
#define\tCTL_HW\t6\
@@ -65,7 +71,7 @@ retry:\n \t\truntime·osyield();\n \tif(m->waitsemacount == 0) {\n \t\t// the function unlocks the spinlock\n-\t\truntime·thrsleep(&m->waitsemacount, 0, 0, &m->waitsemalock);\n+\t\truntime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock);\n \t\tgoto retry;\n \t}\n \tm->waitsemacount--;
- クロック識別子の追加: OpenBSDの
sys/time.h
から取得したCLOCK_REALTIME
,CLOCK_VIRTUAL
,CLOCK_PROF
,CLOCK_MONOTONIC
の各定数がenum
として追加されています。これにより、thrsleep
呼び出し時に適切なクロックを指定できるようになります。 thrsleep
プロトタイプの修正:runtime·thrsleep
関数のプロトタイプが、より具体的な引数名と型(void *ident, int32 clock_id, void *tsp, void *lock
)を持つように修正されました。以前は汎用的なvoid *
が使われていましたが、これにより引数の意味が明確になり、型安全性が向上します。runtime·thrsleep
呼び出しの修正:runtime·thrsleep
の呼び出し箇所で、3番目の引数(タイムアウトを指定するtsp
)が0
からnil
に変更されています。これは、プロトタイプが修正され、tsp
がvoid *
型として明示されたことで、nil
(GoにおけるNULL
に相当)が有効な「タイムアウトなし」を意味する引数として扱えるようになったためです。
コアとなるコードの解説
このコミットのコード変更は、GoランタイムがOpenBSDの低レベルなスレッド同期プリミティブとより正確かつ安全に連携するための基盤を構築しています。
-
Timespec
構造体の導入:defs.h
ファイル群にTimespec
構造体を定義したことで、GoランタイムはOpenBSDのthrsleep
システムコールが期待する時間表現(秒とナノ秒)を内部で保持できるようになりました。32ビットと64ビットアーキテクチャでtv_nsec
の型が異なるのは、それぞれのOSのABI(Application Binary Interface)に合わせたもので、これによりクロスプラットフォームでの互換性を保ちつつ、ネイティブなシステムコールを正しく呼び出すことが可能になります。defs.c
でのtypedef struct timespec $Timespec;
は、C言語の標準的なtimespec
構造体をGoランタイムが認識する内部型$Timespec
にマッピングする役割を果たします。これは、GoのFFI(Foreign Function Interface)またはCgoのようなメカニズムを通じてCの構造体とGoの型を連携させる際に必要となる典型的なパターンです。
-
クロック識別子の定義:
thread.c
にCLOCK_REALTIME
などのクロック識別子をenum
として追加したことで、Goランタイムはthrsleep
システムコールに、どの時間源に基づいて待機時間を計算すべきかを明示的に指示できるようになりました。これにより、例えばシステム時刻の変更に影響されない単調増加クロック(CLOCK_MONOTONIC
)を使用して、より信頼性の高いタイムアウト処理を実装することが可能になります。
-
thrsleep
プロトタイプの修正と呼び出しの変更:thread.c
におけるruntime·thrsleep
のプロトタイプ修正は、このコミットの最も直接的な目的です。以前の汎用的なvoid *
引数から、void *ident, int32 clock_id, void *tsp, void *lock
という具体的な引数リストに変更されたことで、コンパイラはthrsleep
への呼び出しがOpenBSDの期待するシグネチャと一致するかを厳密にチェックできるようになります。これにより、誤った引数渡しによる実行時エラーやクラッシュのリスクが大幅に減少します。runtime·thrsleep(&m->waitsemacount, 0, 0, &m->waitsemalock);
からruntime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock);
への変更は、プロトタイプ修正の直接的な結果です。以前は0
が単なる整数リテラルとして渡されていましたが、tsp
引数がvoid *
型として明示されたことで、nil
(Goにおけるポインタのゼロ値)を渡すことが「タイムアウトなし」または「タイムアウト構造体へのポインタがNULL」を意味する有効な表現となりました。これは、Goランタイムがスレッドを無限に待機させる必要がある場合に、より意図が明確で正しい方法でthrsleep
を呼び出せるようになったことを示しています。
これらの変更は、GoランタイムがOpenBSDの低レベルなスレッド同期メカニズムをより正確に、かつ堅牢に利用するための重要なステップであり、OpenBSD上でのGoプログラムの安定性とパフォーマンスに寄与します。
関連リンク
- Go CL (Code Review) へのリンク: https://golang.org/cl/5360042
参考にした情報源リンク
- OpenBSD
thrsleep
man page (一般的な情報): https://man.openbsd.org/thrsleep.2 (OpenBSDのバージョンによって内容が異なる場合があります) - OpenBSD
timespec
man page (一般的な情報): https://man.openbsd.org/timespec.3 - OpenBSD
clock_gettime
man page (クロック識別子に関する情報): https://man.openbsd.org/clock_gettime.2 - Go言語のランタイムに関する一般的な情報: https://go.dev/doc/effective_go#concurrency (Goの公式ドキュメント)
- C言語の関数プロトタイプに関する一般的な情報: https://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97
- Goのソースコード(GitHub): https://github.com/golang/go
- OpenBSDの公式ウェブサイト: https://www.openbsd.org/