[インデックス 15554] ファイルの概要
runtime: deadlock tests now work with GOMAXPROCS>1 Fixes #4826.
コミット
- コミットハッシュ:
01a5b66d95203f64b215de342487a8d522a3532b
- 作者: Dmitriy Vyukov dvyukov@google.com
- コミット日時: 2013年3月2日 土曜日 10:41:53 +0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/01a5b66d95203f64b215de342487a8d522a3532b
元コミット内容
runtime: deadlock tests now work with GOMAXPROCS>1
Fixes #4826.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/7434046
変更の背景
このコミットは、Go言語のランタイムにおけるデッドロックテストが、GOMAXPROCS
環境変数が1より大きい場合にハングアップするという問題(Issue 4826)を修正するために行われました。
Goのテストスイートには、意図的にデッドロックを発生させて、ランタイムがそれを正しく検出し、プログラムをクラッシュさせることを確認するテストが含まれています。しかし、以前の実装では、GOMAXPROCS
が1より大きい値に設定されている(つまり、複数のOSスレッドでGoのゴルーチンが実行される可能性がある)場合、これらのデッドロックテストが完了せずにハングアップしてしまうという問題がありました。
これは、テスト実行時にGOMAXPROCS
環境変数を明示的に削除または上書きしていたためです。exec.Command
で新しいプロセスを起動する際に、親プロセスの環境変数を引き継ぐのがデフォルトの動作ですが、このテストではGOMAXPROCS
を意図的に削除していました。しかし、この削除が、マルチプロセッサ環境でのデッドロックテストの挙動に悪影響を与えていたと考えられます。
この修正は、テストの信頼性を向上させ、Goランタイムのデッドロック検出メカニズムがマルチプロセッサ環境でも期待通りに機能することを保証するために重要です。
前提知識の解説
- Goランタイム (Go Runtime): Goプログラムの実行を管理するシステムです。ゴルーチンのスケジューリング、メモリ管理(ガベージコレクション)、チャネル通信、デッドロック検出など、Go言語の並行処理モデルを支える重要な役割を担っています。
- ゴルーチン (Goroutine): Go言語における軽量な並行実行単位です。OSスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行することも可能です。GoランタイムがゴルーチンをOSスレッドにマッピングし、スケジューリングを行います。
- GOMAXPROCS: Goランタイムが同時に実行できるOSスレッドの最大数を制御する環境変数です。デフォルトでは、CPUの論理コア数に設定されます。
GOMAXPROCS=1
の場合、Goランタイムは同時に1つのOSスレッドしか使用しません。これにより、並行処理がシミュレートされ、真の並列実行は行われません。GOMAXPROCS > 1
の場合、Goランタイムは複数のOSスレッドを使用してゴルーチンを並列実行できます。これにより、マルチコアCPUの恩恵を最大限に受けることができます。
- デッドロック (Deadlock): 複数のプロセスやスレッドが、互いに相手が保持しているリソースの解放を待っている状態になり、結果としてどのプロセスも処理を進められなくなる状態を指します。Goランタイムは、ゴルーチンがデッドロック状態に陥ったことを検出すると、プログラムをクラッシュさせます。
exec.Command
: Go言語の標準ライブラリos/exec
パッケージに含まれる関数で、外部コマンドを実行するために使用されます。この関数は、指定されたコマンドと引数を持つCmd
構造体を返します。Cmd.Env
:exec.Cmd
構造体のフィールドで、実行するコマンドの環境変数を設定するために使用されます。このフィールドが設定されていない場合、子プロセスは親プロセスの環境変数を継承します。CombinedOutput()
:exec.Cmd
構造体のメソッドで、コマンドの標準出力と標準エラー出力を結合してバイトスライスとして返します。
技術的詳細
このコミットの技術的な核心は、Goのテストフレームワークが外部プロセスを起動する際に、GOMAXPROCS
環境変数の扱いを変更した点にあります。
以前のexecuteTest
関数では、テスト対象のGoプログラムをgo run
コマンドで実行する際に、明示的にGOMAXPROCS
環境変数を削除していました。これは以下のコードで行われていました。
cmd := exec.Command("go", "run", src)
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GOMAXPROCS") {
continue
}
cmd.Env = append(cmd.Env, s)
}
got, _ := cmd.CombinedOutput()
このコードは、現在のプロセスの環境変数(os.Environ()
)をループし、GOMAXPROCS
で始まる環境変数を除外して、新しいcmd.Env
スライスにコピーしていました。これにより、子プロセスはGOMAXPROCS
が設定されていない状態で実行されるか、あるいは親プロセスで設定されていたとしても、その値が子プロセスに引き継がれないようにしていました。
Issue 4826の報告によると、このGOMAXPROCS
の削除が、GOMAXPROCS > 1
の環境でデッドロックテストがハングアップする原因となっていました。デッドロックテストは、複数のゴルーチンが互いにリソースを待つ状況を意図的に作り出し、ランタイムがそれを検出してクラッシュすることを期待します。しかし、GOMAXPROCS
が明示的に削除されることで、ランタイムのスケジューリング動作が変わり、デッドロック検出が正しく機能しなかったり、テストが無限に待機状態に陥ったりする可能性がありました。
今回の修正では、このGOMAXPROCS
を削除するロジックが完全に削除されました。
got, _ := exec.Command("go", "run", src).CombinedOutput()
これにより、exec.Command
はデフォルトの動作に戻り、子プロセスは親プロセスの環境変数をそのまま継承するようになります。つまり、テストが実行される環境でGOMAXPROCS
が設定されていれば、その値がテスト対象のプログラムにも引き継がれることになります。
この変更によって、GOMAXPROCS > 1
の環境下でもデッドロックテストが正しく実行され、期待通りにデッドロックを検出してクラッシュするようになりました。これは、Goランタイムのデッドロック検出機能が、マルチプロセッサ環境においても堅牢であることを保証するために重要な修正です。
コアとなるコードの変更箇所
src/pkg/runtime/crash_test.go
ファイルの executeTest
関数内の変更です。
--- a/src/pkg/runtime/crash_test.go
+++ b/src/pkg/runtime/crash_test.go
@@ -37,15 +37,7 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
}\n \tf.Close()\n \n-\t// Deadlock tests hang with GOMAXPROCS>1. Issue 4826.\n-\tcmd := exec.Command(\"go\", \"run\", src)\n-\tfor _, s := range os.Environ() {\n-\t\tif strings.HasPrefix(s, \"GOMAXPROCS\") {\n-\t\t\tcontinue\n-\t\t}\n-\t\tcmd.Env = append(cmd.Env, s)\n-\t}\n-\tgot, _ := cmd.CombinedOutput()\
+\tgot, _ := exec.Command(\"go\", \"run\", src).CombinedOutput()\
return string(got)\
}\
コアとなるコードの解説
変更はexecuteTest
関数内で行われています。この関数は、Goのテストスイートでランタイムのクラッシュ挙動(デッドロックなど)をテストするために、Goプログラムを外部プロセスとして実行し、その出力をキャプチャする役割を担っています。
変更前:
// Deadlock tests hang with GOMAXPROCS>1. Issue 4826.
cmd := exec.Command("go", "run", src)
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GOMAXPROCS") {
continue
}
cmd.Env = append(cmd.Env, s)
}
got, _ := cmd.CombinedOutput()
変更前のコードでは、exec.Command("go", "run", src)
で新しいコマンドオブジェクトを作成した後、os.Environ()
(現在のプロセスの全環境変数)をループし、GOMAXPROCS
で始まる環境変数をスキップして、それ以外の環境変数をcmd.Env
に明示的に追加していました。これは、子プロセスからGOMAXPROCS
環境変数を削除する(または親プロセスの設定を上書きしてGOMAXPROCS
を未設定にする)ことを意図していました。コメントにも「Deadlock tests hang with GOMAXPROCS>1. Issue 4826.」とあり、この問題が認識されていたことがわかります。
変更後:
got, _ := exec.Command("go", "run", src).CombinedOutput()
変更後では、exec.Command("go", "run", src)
を直接呼び出し、その結果に対してCombinedOutput()
を呼び出しています。exec.Command
のデフォルトの動作では、Cmd.Env
が明示的に設定されていない場合、子プロセスは親プロセスの環境変数をそのまま継承します。
この変更により、GOMAXPROCS
環境変数が親プロセス(テストを実行しているプロセス)から子プロセス(テスト対象のGoプログラム)にそのまま引き継がれるようになりました。結果として、GOMAXPROCS > 1
の環境でテストを実行した場合でも、テスト対象のプログラムがそのGOMAXPROCS
設定を認識し、マルチプロセッサ環境下でのデッドロック検出が正しく機能するようになり、テストがハングアップする問題が解決されました。
関連リンク
- Go Issue 4826: https://github.com/golang/go/issues/4826
- Gerrit Change 7434046: https://golang.org/cl/7434046
参考にした情報源リンク
- Go Issue 4826の議論内容
- Go言語の
os/exec
パッケージのドキュメント - Go言語の
GOMAXPROCS
に関する公式ドキュメントやブログ記事 (一般的な知識として) - Go言語のデッドロック検出に関する情報 (一般的な知識として)```markdown
[インデックス 15554] ファイルの概要
runtime: deadlock tests now work with GOMAXPROCS>1 Fixes #4826.
コミット
- コミットハッシュ:
01a5b66d95203f64b215de342487a8d522a3532b
- 作者: Dmitriy Vyukov dvyukov@google.com
- コミット日時: 2013年3月2日 土曜日 10:41:53 +0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/01a5b66d95203f64b215de342487a8d522a3532b
元コミット内容
runtime: deadlock tests now work with GOMAXPROCS>1
Fixes #4826.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/7434046
変更の背景
このコミットは、Go言語のランタイムにおけるデッドロックテストが、GOMAXPROCS
環境変数が1より大きい場合にハングアップするという問題(Issue 4826)を修正するために行われました。
Goのテストスイートには、意図的にデッドロックを発生させて、ランタイムがそれを正しく検出し、プログラムをクラッシュさせることを確認するテストが含まれています。しかし、以前の実装では、GOMAXPROCS
が1より大きい値に設定されている(つまり、複数のOSスレッドでGoのゴルーチンが実行される可能性がある)場合、これらのデッドロックテストが完了せずにハングアップしてしまうという問題がありました。
これは、テスト実行時にGOMAXPROCS
環境変数を明示的に削除または上書きしていたためです。exec.Command
で新しいプロセスを起動する際に、親プロセスの環境変数を引き継ぐのがデフォルトの動作ですが、このテストではGOMAXPROCS
を意図的に削除していました。しかし、この削除が、マルチプロセッサ環境でのデッドロックテストの挙動に悪影響を与えていたと考えられます。
この修正は、テストの信頼性を向上させ、Goランタイムのデッドロック検出メカニズムがマルチプロセッサ環境でも期待通りに機能することを保証するために重要です。
前提知識の解説
- Goランタイム (Go Runtime): Goプログラムの実行を管理するシステムです。ゴルーチンのスケジューリング、メモリ管理(ガベージコレクション)、チャネル通信、デッドロック検出など、Go言語の並行処理モデルを支える重要な役割を担っています。
- ゴルーチン (Goroutine): Go言語における軽量な並行実行単位です。OSスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行することも可能です。GoランタイムがゴルーチンをOSスレッドにマッピングし、スケジューリングを行います。
- GOMAXPROCS: Goランタイムが同時に実行できるOSスレッドの最大数を制御する環境変数です。デフォルトでは、CPUの論理コア数に設定されます。
GOMAXPROCS=1
の場合、Goランタイムは同時に1つのOSスレッドしか使用しません。これにより、並行処理がシミュレートされ、真の並列実行は行われません。GOMAXPROCS > 1
の場合、Goランタイムは複数のOSスレッドを使用してゴルーチンを並列実行できます。これにより、マルチコアCPUの恩恵を最大限に受けることができます。
- デッドロック (Deadlock): 複数のプロセスやスレッドが、互いに相手が保持しているリソースの解放を待っている状態になり、結果としてどのプロセスも処理を進められなくなる状態を指します。Goランタイムは、ゴルーチンがデッドロック状態に陥ったことを検出すると、プログラムをクラッシュさせます。
exec.Command
: Go言語の標準ライブラリos/exec
パッケージに含まれる関数で、外部コマンドを実行するために使用されます。この関数は、指定されたコマンドと引数を持つCmd
構造体を返します。Cmd.Env
:exec.Cmd
構造体のフィールドで、実行するコマンドの環境変数を設定するために使用されます。このフィールドが設定されていない場合、子プロセスは親プロセスの環境変数を継承します。CombinedOutput()
:exec.Cmd
構造体のメソッドで、コマンドの標準出力と標準エラー出力を結合してバイトスライスとして返します。
技術的詳細
このコミットの技術的な核心は、Goのテストフレームワークが外部プロセスを起動する際に、GOMAXPROCS
環境変数の扱いを変更した点にあります。
以前のexecuteTest
関数では、テスト対象のGoプログラムをgo run
コマンドで実行する際に、明示的にGOMAXPROCS
環境変数を削除していました。これは以下のコードで行われていました。
cmd := exec.Command("go", "run", src)
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GOMAXPROCS") {
continue
}
cmd.Env = append(cmd.Env, s)
}
got, _ := cmd.CombinedOutput()
このコードは、現在のプロセスの環境変数(os.Environ()
)をループし、GOMAXPROCS
で始まる環境変数を除外して、新しいcmd.Env
スライスにコピーしていました。これにより、子プロセスはGOMAXPROCS
が設定されていない状態で実行されるか、あるいは親プロセスで設定されていたとしても、その値が子プロセスに引き継がれないようにしていました。
Issue 4826の報告によると、このGOMAXPROCS
の削除が、GOMAXPROCS > 1
の環境でデッドロックテストがハングアップする原因となっていました。デッドロックテストは、複数のゴルーチンが互いにリソースを待つ状況を意図的に作り出し、ランタイムがそれを検出してクラッシュすることを期待します。しかし、GOMAXPROCS
が明示的に削除されることで、ランタイムのスケジューリング動作が変わり、デッドロック検出が正しく機能しなかったり、テストが無限に待機状態に陥ったりする可能性がありました。
今回の修正では、このGOMAXPROCS
を削除するロジックが完全に削除されました。
got, _ := exec.Command("go", "run", src).CombinedOutput()
これにより、exec.Command
はデフォルトの動作に戻り、子プロセスは親プロセスの環境変数をそのまま継承するようになります。つまり、テストが実行される環境でGOMAXPROCS
が設定されていれば、その値がテスト対象のプログラムにも引き継がれることになります。
この変更によって、GOMAXPROCS > 1
の環境下でもデッドロックテストが正しく実行され、期待通りにデッドロックを検出してクラッシュするようになりました。これは、Goランタイムのデッドロック検出機能が、マルチプロセッサ環境においても堅牢であることを保証するために重要な修正です。
コアとなるコードの変更箇所
src/pkg/runtime/crash_test.go
ファイルの executeTest
関数内の変更です。
--- a/src/pkg/runtime/crash_test.go
+++ b/src/pkg/runtime/crash_test.go
@@ -37,15 +37,7 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
}\n \tf.Close()\n \n-\t// Deadlock tests hang with GOMAXPROCS>1. Issue 4826.\n-\tcmd := exec.Command(\"go\", \"run\", src)\n-\tfor _, s := range os.Environ() {\n-\t\tif strings.HasPrefix(s, \"GOMAXPROCS\") {\n-\t\t\tcontinue\n-\t\t}\n-\t\tcmd.Env = append(cmd.Env, s)\n-\t}\n-\tgot, _ := cmd.CombinedOutput()\
+\tgot, _ := exec.Command(\"go\", \"run\", src).CombinedOutput()\
return string(got)\
}\
コアとなるコードの解説
変更はexecuteTest
関数内で行われています。この関数は、Goのテストスイートでランタイムのクラッシュ挙動(デッドロックなど)をテストするために、Goプログラムを外部プロセスとして実行し、その出力をキャプチャする役割を担っています。
変更前:
// Deadlock tests hang with GOMAXPROCS>1. Issue 4826.
cmd := exec.Command("go", "run", src)
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GOMAXPROCS") {
continue
}
cmd.Env = append(cmd.Env, s)
}
got, _ := cmd.CombinedOutput()
変更前のコードでは、exec.Command("go", "run", src)
で新しいコマンドオブジェクトを作成した後、os.Environ()
(現在のプロセスの全環境変数)をループし、GOMAXPROCS
で始まる環境変数をスキップして、それ以外の環境変数をcmd.Env
に明示的に追加していました。これは、子プロセスからGOMAXPROCS
環境変数を削除する(または親プロセスの設定を上書きしてGOMAXPROCS
を未設定にする)ことを意図していました。コメントにも「Deadlock tests hang with GOMAXPROCS>1. Issue 4826.」とあり、この問題が認識されていたことがわかります。
変更後:
got, _ := exec.Command("go", "run", src).CombinedOutput()
変更後では、exec.Command("go", "run", src)
を直接呼び出し、その結果に対してCombinedOutput()
を呼び出しています。exec.Command
のデフォルトの動作では、Cmd.Env
が明示的に設定されていない場合、子プロセスは親プロセスの環境変数をそのまま継承します。
この変更により、GOMAXPROCS
環境変数が親プロセス(テストを実行しているプロセス)から子プロセス(テスト対象のGoプログラム)にそのまま引き継がれるようになりました。結果として、GOMAXPROCS > 1
の環境でテストを実行した場合でも、テスト対象のプログラムがそのGOMAXPROCS
設定を認識し、マルチプロセッサ環境下でのデッドロック検出が正しく機能するようになり、テストがハングアップする問題が解決されました。
関連リンク
- Go Issue 4826: https://github.com/golang/go/issues/4826
- Gerrit Change 7434046: https://golang.org/cl/7434046
参考にした情報源リンク
- Go Issue 4826の議論内容
- Go言語の
os/exec
パッケージのドキュメント - Go言語の
GOMAXPROCS
に関する公式ドキュメントやブログ記事 (一般的な知識として) - Go言語のデッドロック検出に関する情報 (一般的な知識として)