[インデックス 16839] ファイルの概要
このコミットは、Goランタイムの内部構造を定義するヘッダーファイルである src/pkg/runtime/runtime.h
(現在のGoのソースツリーでは src/runtime/runtime.h
に相当) 内のコメントの変更に関するものです。具体的には、M
(Machine、OSスレッドを表すランタイムの内部構造体) の m->locked
フィールドに関する説明を明確にすることを目的としています。このファイルは、Goランタイムのスケジューラ、メモリ管理、ゴルーチン管理など、低レベルな動作を制御する重要な定義を含んでいます。
コミット
- コミットハッシュ:
27134567fa70f5610dbc865a9c4b18b2c446f112
- Author: Dmitriy Vyukov dvyukov@google.com
- Date: Mon Jul 22 16:37:31 2013 +0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/27134567fa70f5610dbc865a9c4b18b2c446f112
元コミット内容
runtime: clarify comment for m->locked
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11671043
変更の背景
このコミットは、Goランタイムの M
構造体内の m->locked
フィールドに関する既存のコメントが、そのフィールドの実際の使われ方を十分に説明していなかったため、これを明確にすることを目的としています。Goランタイムは非常に複雑であり、特にスケジューラやOSスレッドとの連携に関する部分は、正確な理解が求められます。m->locked
フィールドは、特定のゴルーチンが特定のOSスレッドに「ロック」されている状態を管理するための重要なメカニズムに関連しています。
以前のコメントは、m->locked
が LockOSThread
による外部からのロックと、内部的な lockOSThread
/unlockOSThread
のネスト深度を保持していると述べていましたが、その詳細なビット表現や、外部ロックが再帰的でないこと、内部ロックが再帰的であることなどの重要なニュアンスが欠けていました。このような不明瞭さは、ランタイムのデバッグや将来の変更において混乱を招く可能性がありました。
この変更は、コードの動作自体を変更するものではなく、その動作を説明するドキュメント(コメント)の品質を向上させるものです。正確で詳細なコメントは、Goランタイムのような複雑なシステムを理解し、保守するために不可欠です。特に、低レベルな並行処理やOSとのインタラクションに関わる部分は、コメントが唯一の信頼できる情報源となることが多いため、その正確性が極めて重要です。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念と機能に関する知識が必要です。
-
Goroutine (ゴルーチン): Go言語における軽量な実行単位です。OSスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行できます。Goランタイムのスケジューラによって管理されます。
-
OS Thread (OSスレッド): オペレーティングシステムが管理する実行単位です。Goランタイムは、複数のゴルーチンを少数のOSスレッド上で多重化して実行します。
-
M (Machine): Goランタイムの内部構造体で、OSスレッドを表します。各
M
は1つのOSスレッドに対応し、そのスレッド上でゴルーチンを実行します。 -
P (Processor): Goランタイムの内部構造体で、論理プロセッサを表します。ゴルーチンを実行するためのコンテキストを提供し、ローカルなゴルーチンキューを保持します。スケジューラは
P
を介してM
にゴルーチンを割り当てます。 -
G (Goroutine): Goランタイムの内部構造体で、個々のゴルーチンを表します。
-
Go Scheduler (Goスケジューラ): Goランタイムの心臓部であり、ゴルーチンを
M
とP
に効率的に割り当て、実行を管理します。ゴルーチンは通常、どのOSスレッドで実行されるかを意識しません。 -
runtime.LockOSThread()
/runtime.UnlockOSThread()
:runtime.LockOSThread()
は、現在のゴルーチンを、現在実行されているOSスレッドに「ロック」します。これにより、そのゴルーチンは他のOSスレッドに移動することなく、その特定のOSスレッド上で排他的に実行され続けます。runtime.UnlockOSThread()
は、LockOSThread()
によって設定されたロックを解除し、ゴルーチンが再び任意のOSスレッド上で実行されるようにします。- これらの関数は、Cgo(GoからCのコードを呼び出す機能)や、特定のOSスレッドに依存するシステムコール(例: GUIライブラリの初期化など)を扱う際に必要となることがあります。例えば、OpenGLのコンテキストは通常、特定のOSスレッドにバインドされているため、GoからOpenGLを扱う際には
LockOSThread
が必要になることがあります。
-
lockOSThread
/unlockOSThread
(内部関数): これらはruntime.LockOSThread()
の実装の基盤となるランタイム内部の関数です。ランタイム内部の様々な処理(Cgoの呼び出しなど)で、一時的にゴルーチンをOSスレッドにロックする必要がある場合に使用されます。これらはruntime.LockOSThread()
とは異なり、再帰的に呼び出すことが可能です。 -
m->locked
フィールド:M
構造体内のフィールドで、現在のOSスレッドがゴルーチンにロックされている状態を管理するためのカウンタまたはフラグとして機能します。このフィールドのビットが、外部からのロックと内部からのロックの両方の状態をエンコードしています。
技術的詳細
このコミットは、m->locked
フィールドのコメントを、そのビット表現と意味論をより正確に反映するように変更しています。
変更前のコメントは以下の通りでした。
// The m->locked word holds a single bit saying whether
// external calls to LockOSThread are in effect, and then a counter
// of the internal nesting depth of lockOSThread / unlockOSThread.
このコメントは、m->locked
が LockOSThread
による外部ロックの単一ビットと、lockOSThread
/unlockOSThread
の内部ネスト深度カウンタを保持していると述べています。しかし、この説明はやや曖昧で、特に「単一ビット」と「カウンタ」の具体的な配置や、外部ロックと内部ロックの振る舞いの違い(再帰性など)が不明瞭でした。
変更後のコメントは以下の通りです。
// The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread.
// The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active.
// External locks are not recursive; a second lock is silently ignored.
// The upper bits of m->lockedcount record the nesting depth of calls to lockOSThread
// (counting up by LockInternal), popped by unlockOSThread (counting down by LockInternal).
// Internal locks can be recursive. For instance, a lock for cgo can occur while the main
// goroutine is holding the lock during the initialization phase.
この新しいコメントは、以下の重要な点を明確にしています。
- 2つの状態の保持:
m->locked
はLockOSThread
とlockOSThread
のアクティブな呼び出しをカウントする2つの状態を保持していることを明示しています。 - 低位ビット (
LockExternal
): 最下位ビットがLockOSThread
による外部ロックの状態を示すブーリアンフラグ (LockExternal
) であることを明確にしています。 - 外部ロックの非再帰性:
LockOSThread
による外部ロックは再帰的ではないこと、つまり2回目の呼び出しは黙って無視されることを明記しています。これは、ユーザーがLockOSThread
を複数回呼び出しても、OSスレッドへのロックが二重にかかるわけではないという重要な動作特性です。 - 上位ビット (
LockInternal
):m->locked
の上位ビットがlockOSThread
の呼び出しのネスト深度を記録するカウンタ (LockInternal
によって増減する) であることを明確にしています。 - 内部ロックの再帰性:
lockOSThread
による内部ロックは再帰的であること、つまり複数回呼び出すことでネスト深度が増加することを明記しています。 - 具体例: Cgoの例を挙げることで、内部ロックが再帰的であることの必要性を示しています。メインゴルーチンが初期化フェーズでロックを保持している間に、Cgoのためのロックが発生する可能性があるというシナリオは、この再帰性の重要性を強調しています。
また、このコメントの変更に伴い、enum
定義も追加されています。
enum
{
LockExternal = 1,
// ... (LockInternal の定義が続く)
};
LockExternal
は 1
と定義されており、これは m->locked
の最下位ビットが外部ロックの状態を示すことを裏付けています。LockInternal
は、内部ロックのカウンタを増減させるための値として使用されますが、このコミットのdiffにはその具体的な定義は含まれていません。しかし、コメントからその役割は明確です。
この変更は、Goランタイムの M
構造体の locked
フィールドが、外部からの LockOSThread
呼び出しと、ランタイム内部からの lockOSThread
呼び出しの両方の状態をどのように効率的に1つのワードにパックしているかを、より正確かつ詳細に説明するものです。これにより、ランタイムの動作を理解しようとする開発者にとって、このフィールドのセマンティクスが格段に明確になりました。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -359,9 +359,13 @@ struct P
byte pad[64];
};
-// The m->locked word holds a single bit saying whether
-// external calls to LockOSThread are in effect, and then a counter
-// of the internal nesting depth of lockOSThread / unlockOSThread.
+// The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread.
+// The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active.
+// External locks are not recursive; a second lock is silently ignored.
+// The upper bits of m->lockedcount record the nesting depth of calls to lockOSThread
+// (counting up by LockInternal), popped by unlockOSThread (counting down by LockInternal).
+// Internal locks can be recursive. For instance, a lock for cgo can occur while the main
+// goroutine is holding the lock during the initialization phase.
enum
{
LockExternal = 1,
コアとなるコードの解説
変更は src/pkg/runtime/runtime.h
ファイル内の struct M
(または関連する構造体) の locked
フィールドの直前にあるコメントに集中しています。
-
削除された行:
// The m->locked word holds a single bit saying whether // external calls to LockOSThread are in effect, and then a counter // of the internal nesting depth of lockOSThread / unlockOSThread.
この3行のコメントが削除されました。これは、
m->locked
が外部ロックの単一ビットと内部ロックのカウンタを保持しているという基本的な説明でしたが、詳細が不足していました。 -
追加された行:
// The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread. // The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active. // External locks are not recursive; a second lock is silently ignored. // The upper bits of m->lockedcount record the nesting depth of calls to lockOSThread // (counting up by LockInternal), popped by unlockOSThread (counting down by LockInternal). // Internal locks can be recursive. For instance, a lock for cgo can occur while the main // goroutine is holding the lock during the initialization phase.
この5行の新しいコメントが追加されました。
- 最初の行で、
m->locked
がLockOSThread
とlockOSThread
の両方のアクティブな呼び出しをカウントする2つの状態を保持していることを明確にしています。 - 2行目で、最下位ビットが
LockExternal
というブーリアンフラグであり、LockOSThread
呼び出しがアクティブかどうかを示すことを説明しています。 - 3行目で、外部ロック(
LockOSThread
)は再帰的ではなく、2回目の呼び出しは黙って無視されるという重要な動作特性を述べています。 - 4行目で、上位ビットが
lockOSThread
のネスト深度を記録するカウンタであり、LockInternal
によって増減することを示しています。 - 5行目で、内部ロック(
lockOSThread
)は再帰的であり、Cgoの例を挙げてその必要性を補足しています。
- 最初の行で、
-
追加された
enum
定義:enum { LockExternal = 1, };
LockExternal
が1
として定義されていることが示されています。これは、m->locked
の最下位ビットがこのフラグとして使用されることをコードレベルで明確にしています。
この変更は、コードの機能的な動作には影響を与えませんが、Goランタイムの内部メカニズム、特にOSスレッドへのゴルーチンのロックに関する m->locked
フィールドのセマンティクスを、開発者にとって遥かに理解しやすいものにしました。これにより、ランタイムのデバッグや将来の機能拡張、あるいは単にGoの内部動作を深く理解したい人々にとって、非常に価値のある改善となります。
関連リンク
- Go CL 11671043: https://golang.org/cl/11671043
参考にした情報源リンク
- Goソースコード (特に
src/runtime/runtime.h
およびsrc/runtime/proc.go
内のLockOSThread
およびlockOSThread
の実装) - Goスケジューラに関する一般的なドキュメントやブログ記事 (GoのM, P, Gモデルの理解のため)
- Cgoに関するGoの公式ドキュメント (Cgoと
LockOSThread
の関連性を理解するため) - Goのコミット履歴とコードレビュープロセスに関する情報 (コメントの重要性を理解するため)
- Go のスケジューラについて (GoスケジューラのM, P, Gモデルの理解に役立つ一般的な情報源)
- Goのruntime.LockOSThreadとruntime.UnlockOSThreadについて (
LockOSThread
の具体的な使用例と背景に関する一般的な情報源) - Goのruntimeパッケージのm.lockedについて (Goのソースコードへの直接リンク)
- Goのruntimeパッケージのproc.goについて (Goのソースコードへの直接リンク)
- GoのCgoについて (Cgoの公式ブログ記事)