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

[インデックス 16687] ファイルの概要

このコミットは、Goランタイムのproc_test.goファイル内のプリエンプション(preemption)テストを一時的に無効化するものです。具体的には、TestPreemptionGCというテスト関数がビルドエラーを引き起こしていたため、そのテストをスキップするように変更されました。

コミット

  • コミットハッシュ: 1184407f2a2955fe2befd22896ee9bf49cd0defe
  • Author: Russ Cox rsc@golang.org
  • Date: Mon Jul 1 18:10:03 2013 -0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/1184407f2a2955fe2befd22896ee9bf49cd0defe

元コミット内容

runtime: disable preemption test (fix build)

TBR=golang-dev
CC=golang-dev
https://golang.org/cl/10849043

変更の背景

このコミットの主な目的は、Goランタイムのビルドプロセスで発生していた問題を修正することです。src/pkg/runtime/proc_test.go内のTestPreemptionGCテストが、当時のGoランタイムのプリエンプション機能の状態と整合性が取れておらず、ビルドエラーを引き起こしていました。

コミットメッセージにある「preemption is disabled」というコメントから、この時点ではプリエンプション機能が完全に有効になっていなかったか、あるいはテストが期待する動作をしていなかったことが示唆されます。テストがビルドを妨げる場合、開発の継続性を確保するために一時的にテストを無効化することは一般的なプラクティスです。この変更は、ビルドを修正し、開発フローをブロックしないようにするための緊急的な対応であったと考えられます。

前提知識の解説

Goランタイムのプリエンプション(Preemption)

Goのランタイムにおけるプリエンプションとは、Goスケジューラが実行中のゴルーチン(goroutine)を一時的に中断し、他のゴルーチンにCPUの実行権を渡すメカニズムです。これにより、特に計算集約的なアプリケーションにおいて、ゴルーチン間の公平性と応答性を確保し、特定のゴルーチンがCPUリソースを独占するのを防ぎます。

Goのプリエンプションは、その歴史の中で進化してきました。

  1. 協調的プリエンプション(Cooperative Preemption): 初期のGoスケジューラは、ゴルーチンが自発的に制御を譲る協調的プリエンプションに依存していました。これは通常、関数呼び出しの際に行われます。しかし、ゴルーチンが関数呼び出しを行わない「タイトなループ」に入ると、他のゴルーチンが実行されずにスターベーション(飢餓状態)に陥ったり、ガベージコレクション(GC)が遅延したりする問題がありました。

  2. 同期プリエンプション(Synchronous Preemption) - Go 1.2で導入: この問題を解決するため、Go 1.2では同期プリエンプションが導入されました。これは、コンパイラが関数プロローグ(関数の冒頭部分)にチェックを挿入するものです。プリエンプション要求が保留中の場合、このチェックによってランタイムはゴルーチンをプリエンプト(中断)し、スケジューラに制御を戻すことができます。

  3. 非同期プリエンプション(Asynchronous Preemption) - Go 1.14で導入: Go 1.14では、さらに進んだ非同期プリエンプションが導入されました。これにより、Goランタイムは、タイトなループ内であっても、ほぼ任意の時点でゴルーチンを中断できるようになりました。これは、OSスレッドにシグナル(SIGURGなど)を送信することで実現され、シグナルハンドラがゴルーチンを一時停止させ、スケジューラが他のタスクを実行できるようにします。

プリエンプションとガベージコレクション(GC)

プリエンプションは、Goのガベージコレクタ(GC)の効率的な動作にとって不可欠です。Goは、低レイテンシと高スループットのために設計された、並行かつ非世代型のガベージコレクタを使用しています。

GCの特定のフェーズ、特に「ストップ・ザ・ワールド(Stop-The-World: STW)」フェーズ(これは非常に短く保たれます)では、ランタイムはメモリを安全にスキャンし、未使用のオブジェクトを回収するために、すべてのゴルーチンを一時停止させる必要があります。

この目的を達成するために、ランタイムはプリエンプションを活用します。すべてのアクティブなゴルーチンに対してpreemptフラグを設定し、指定された「セーフポイント(safepoint)」で一時停止させます。これにより、ガベージコレクタは並行して実行されているゴルーチンからの干渉なしにその作業を実行でき、メモリの整合性が維持されます。効果的なプリエンプションがなければ、長時間実行されるゴルーチンがGCサイクルを遅延させ、メモリ使用量の増加やアプリケーションの不安定性につながる可能性があります。

TestPreemptionGCについて

TestPreemptionGCというテストは、ゴルーチンのプリエンプションとガベージコレクタの間の正しい相互作用を検証することを目的としていたと考えられます。このテストは、以下の点を保証しようとしていた可能性があります。

  • GCサイクル中にゴルーチンが正常にプリエンプトされること。
  • GCがメモリ管理タスクを実行するために、ゴルーチンを効果的に停止および再開できること。
  • 長時間実行される、またはCPUバウンドなゴルーチンがガベージコレクションを不必要に遅延させないこと。

このようなテストでは、プリエンプションをトリガーするために集中的な計算を実行するゴルーチンを含むシナリオが想定され、同時にGCの動作を監視して、プリエンプションがタイムリーで効率的なガベージコレクションを促進することを確認します。

技術的詳細

このコミットは、src/pkg/runtime/proc_test.goファイル内のTestPreemptionGC関数に、Goのテストフレームワークが提供するt.Skip()メソッドを追加することで、テストを無効化しています。

t.Skip()は、testingパッケージの一部であり、テスト関数内で呼び出されると、そのテストをスキップし、テストの実行を中断します。これにより、テストは失敗とはみなされず、単に実行されなかったものとして扱われます。これは、特定の環境で実行できないテストや、一時的に無効化する必要があるテストに対して非常に有用です。

この場合、「preemption is disabled」というメッセージと共にスキップされていることから、当時のGoランタイムのプリエンプション機能がまだ開発中であったり、安定していなかったり、あるいは特定のビルド構成で意図的に無効化されていたりした可能性が高いです。テストが期待通りに動作しない、またはビルドを妨げる状況で、開発者がその問題を修正するまでの間、一時的にテストを無効化する選択は合理的です。

コアとなるコードの変更箇所

変更はsrc/pkg/runtime/proc_test.goファイルの一箇所のみです。

--- a/src/pkg/runtime/proc_test.go
+++ b/src/pkg/runtime/proc_test.go
@@ -169,6 +169,7 @@ var preempt = func() int {
 }
 
 func TestPreemptionGC(t *testing.T) {
+	t.Skip("preemption is disabled")
 	// Test that pending GC preempts running goroutines.
 	const P = 5
 	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))

コアとなるコードの解説

追加された行は以下の通りです。

	t.Skip("preemption is disabled")

この行は、TestPreemptionGC関数の冒頭に追加されています。

  • tは、*testing.T型のインスタンスであり、Goのテストフレームワークが提供するテストヘルパーメソッドへのアクセスを提供します。
  • Skip()メソッドは、そのテストをスキップするようにテストランナーに指示します。引数として渡された文字列(この場合は"preemption is disabled")は、テストがスキップされた理由としてテスト結果に表示されます。

この変更により、TestPreemptionGCは実行時にすぐにスキップされ、その後のテストロジック(GCが実行中のゴルーチンをプリエンプトするかどうかをテストする部分)は実行されなくなります。これにより、このテストが原因で発生していたビルドエラーが解消されます。

関連リンク

参考にした情報源リンク