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

[インデックス 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_waitnanosleepのような機能に相当しますが、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つの主要な問題に対処しています。

  1. thrsleepプロトタイプの不一致: 以前のGoランタイムでは、thrsleepの関数プロトタイプがOpenBSDの実際の定義と異なっていたため、コンパイラが誤った引数の型や数を期待し、結果として不正なシステムコール呼び出しやメモリ破損を引き起こす可能性がありました。このコミットでは、thread.c内のthrsleepのプロトタイプをOpenBSDの期待する形式に修正しています。

  2. 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を追加しています。

  3. クロック識別子の欠如: 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_sectv_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_secint32tv_nsecint64として定義されており、パディングバイト(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に変更されています。これは、プロトタイプが修正され、tspvoid *型として明示されたことで、nil(GoにおけるNULLに相当)が有効な「タイムアウトなし」を意味する引数として扱えるようになったためです。

コアとなるコードの解説

このコミットのコード変更は、GoランタイムがOpenBSDの低レベルなスレッド同期プリミティブとより正確かつ安全に連携するための基盤を構築しています。

  1. 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の型を連携させる際に必要となる典型的なパターンです。
  2. クロック識別子の定義:

    • thread.cCLOCK_REALTIMEなどのクロック識別子をenumとして追加したことで、Goランタイムはthrsleepシステムコールに、どの時間源に基づいて待機時間を計算すべきかを明示的に指示できるようになりました。これにより、例えばシステム時刻の変更に影響されない単調増加クロック(CLOCK_MONOTONIC)を使用して、より信頼性の高いタイムアウト処理を実装することが可能になります。
  3. 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プログラムの安定性とパフォーマンスに寄与します。

関連リンク

参考にした情報源リンク