[インデックス 18942] ファイルの概要
このコミットは、GoランタイムにおけるWindowsビルドの問題を修正するものです。具体的には、以前のコミット(インデックス19543)によって導入されたバグを修正し、Windows環境でのメモリ予約関数のシグネチャを調整しています。
コミット
commit bee3848f4e252578c7439890a92e534b39c1e690
Author: Ian Lance Taylor <iant@golang.org>
Date: Tue Mar 25 14:17:00 2014 -0700
runtime: fix windows build (buggy commit in 19543:d68b79ccbfed)
TBR=rsc
CC=golang-codereviews
https://golang.org/cl/80090043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bee3848f4e252578c7439890a92e534b39c1e690
元コミット内容
このコミットは、src/pkg/runtime/mem_windows.c
ファイルの runtime·SysReserve
関数のシグネチャを変更しています。具体的には、bool *reserved
という新しい引数を追加し、その引数に true
を設定する行を追加しています。
--- a/src/pkg/runtime/mem_windows.c
+++ b/src/pkg/runtime/mem_windows.c
@@ -71,7 +71,7 @@ runtime·SysFault(void *v, uintptr n)
}
void*
-runtime·SysReserve(void *v, uintptr n)
+runtime·SysReserve(void *v, uintptr n, bool *reserved)
{
*reserved = true;
// v is just a hint.
変更の背景
このコミットは、コミットメッセージに明記されている通り、「buggy commit in 19543:d68b79ccbfed」によって引き起こされたWindowsビルドの問題を修正するために作成されました。
元の「バグのあるコミット」(CL 19543、ハッシュ d68b79ccbfed
)は、runtime: fix race in TestStackGrowth
というタイトルで、TestStackGrowth
テストにおける競合状態を修正することを目的としていました。この修正は、runtime.g
構造体の stackguard0
フィールドと g.stack.lo
フィールドに関連する競合状態に対処するため、g.stack.hi
という新しいフィールドを導入し、newstack
関数を修正してスタック境界チェックにこれを使用するように変更しました。
しかし、このスタック成長テストの修正が、Windows環境におけるメモリ予約関数 runtime·SysReserve
のシグネチャに意図しない変更をもたらしたか、あるいはその変更がWindowsビルドシステムと互換性がなかったために、ビルドエラーが発生したと考えられます。
本コミットは、このWindowsビルドの問題を解決するために、runtime·SysReserve
関数のシグネチャを、Windowsビルドが期待する形に再調整することで、以前のバグのあるコミットによって生じた不整合を解消しています。
前提知識の解説
- Goランタイム (Go Runtime): Goプログラムの実行を管理する低レベルのコンポーネントです。ガベージコレクション、スケジューリング、メモリ管理、システムコールなどが含まれます。Goプログラムは、OSと直接対話するのではなく、ランタイムを介してこれらの操作を行います。
- メモリ予約 (Memory Reservation): オペレーティングシステムに対して、特定のサイズのアドレス空間を確保する操作です。これは、実際にメモリをコミット(物理メモリを割り当てる)する前に行われることが多く、将来的に使用するメモリ領域を予約しておくことで、アドレス空間の断片化を防いだり、セキュリティを向上させたりする目的があります。Goランタイムは、ヒープ領域などを管理するためにこの機能を利用します。
SysReserve
関数: Goランタイム内部で使用される関数で、オペレーティングシステムに対してメモリ領域を予約する役割を担います。OSごとに実装が異なり、WindowsではVirtualAlloc
などのAPIが内部的に使用されます。uintptr
: Go言語におけるポインタの整数表現です。ポインタの値を整数として扱う際に使用されます。bool *reserved
: C言語のポインタ表現で、bool
型の変数へのポインタを意味します。このポインタを介して、呼び出し元にbool
値を返すことができます。この場合、メモリが正常に予約されたかどうかを示すために使用されます。- 競合状態 (Race Condition): 複数の並行プロセスやスレッドが共有リソースにアクセスする際に、そのアクセス順序によって結果が非決定的に変わってしまう状態を指します。Goランタイムのような並行性の高いシステムでは、このような問題が発生しやすく、慎重な設計と同期メカニズムが必要です。
- スタック成長 (Stack Growth): Goのgoroutineは、必要に応じてスタックサイズを動的に増減させることができます。スタックが不足しそうになると、ランタイムはより大きなスタックを割り当て、既存のスタックの内容を新しいスタックにコピーします。このプロセスは「スタック成長」と呼ばれます。
stackguard0
: Goランタイムがスタックオーバーフローを検出するために使用するガードページのアドレスです。スタックポインタがこのアドレスに近づくと、スタック成長が必要であると判断されます。g.stack.lo
/g.stack.hi
:g
はgoroutineの構造体を表し、g.stack
はそのgoroutineのスタックに関する情報を含みます。lo
はスタックの開始アドレス(低位アドレス)、hi
はスタックの終了アドレス(高位アドレス)を示します。
技術的詳細
このコミットの技術的な核心は、runtime·SysReserve
関数のシグネチャ変更にあります。
元の runtime·SysReserve(void *v, uintptr n)
は、指定されたアドレス v
から n
バイトのメモリを予約する関数でした。しかし、Goランタイムの進化、特にスタック成長のロジックやメモリ管理の改善に伴い、メモリ予約の振る舞いをより詳細に制御したり、予約が成功したかどうかを呼び出し元に明示的に伝える必要が生じたと考えられます。
以前の「バグのあるコミット」(CL 19543)は、スタック成長テストの競合状態を修正する過程で、runtime.g
構造体のスタック関連フィールド(stackguard0
, g.stack.lo
, g.stack.hi
)の扱いを変更しました。この変更が、runtime·SysReserve
の呼び出し規約や期待される動作に影響を与え、Windowsビルドで問題を引き起こした可能性があります。例えば、スタック成長の新しいロジックが、メモリ予約の成功/失敗をより厳密にチェックするようになった、あるいは、予約されたメモリ領域の特性に関する追加情報が必要になった、などが考えられます。
本コミットでは、runtime·SysReserve
に bool *reserved
という新しい引数を追加することで、この問題を解決しています。
bool *reserved
は、メモリ予約が成功したかどうかを呼び出し元に伝えるための出力パラメータとして機能します。- 関数内で
*reserved = true;
と設定することで、このruntime·SysReserve
の呼び出しが常に成功したとマークされることを示唆しています。これは、Windowsのメモリ予約APIが通常、指定された条件で予約に失敗しないことを前提としているか、あるいは、この特定のコンテキストでは予約が常に成功すると仮定できるためかもしれません。
この変更により、runtime·SysReserve
を呼び出す側のコード(おそらくスタック成長やヒープ管理に関連する部分)が、予約の成功状態を正しく受け取れるようになり、Windowsビルドにおける不整合が解消されたと考えられます。
コアとなるコードの変更箇所
変更は src/pkg/runtime/mem_windows.c
ファイルの runtime·SysReserve
関数に限定されています。
// 変更前
void*
runtime·SysReserve(void *v, uintptr n)
// 変更後
void*
runtime·SysReserve(void *v, uintptr n, bool *reserved)
{
*reserved = true; // 追加された行
// v is just a hint.
コアとなるコードの解説
void* runtime·SysReserve(void *v, uintptr n, bool *reserved)
:- 関数のシグネチャが変更され、3番目の引数として
bool *reserved
が追加されました。これは、メモリ予約が成功したかどうかを示すブール値へのポインタです。 void *v
: 予約するメモリ領域の推奨開始アドレス(ヒント)。OSは必ずしもこのアドレスを使用するとは限りません。uintptr n
: 予約するメモリ領域のサイズ(バイト単位)。bool *reserved
: 予約が成功したかどうかを示すブール値へのポインタ。
- 関数のシグネチャが変更され、3番目の引数として
*reserved = true;
:- 追加されたこの行は、
reserved
ポインタが指すメモリ位置にtrue
を書き込みます。これにより、このruntime·SysReserve
の呼び出しが成功したことを呼び出し元に通知します。 - この行が追加されたことで、
runtime·SysReserve
を呼び出す側のコードは、reserved
引数をチェックすることで、メモリ予約が意図通りに行われたことを確認できるようになります。これは、特にWindows環境でのメモリ管理ロジックにおいて、より堅牢なエラーハンドリングや状態管理を可能にするために必要とされた変更であると推測されます。
- 追加されたこの行は、
関連リンク
- Go Change-Id 80090043 (このコミットのCL): https://golang.org/cl/80090043
- Go Change-Id 19543 (バグのある元のコミットのCL): https://go-review.googlesource.com/c/go/+/19543
参考にした情報源リンク
- Go言語の公式ドキュメント (Go Runtime, Memory Management)
- Goのソースコード (特に
src/pkg/runtime/
ディレクトリ) - Gerrit Code Review (go-review.googlesource.com)
- Windows API ドキュメント (VirtualAllocなど、メモリ管理関連)
- Go言語のスタック成長に関する技術記事
- 競合状態に関する一般的なプログラミングの概念