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

[インデックス 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·SysReservebool *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: 予約が成功したかどうかを示すブール値へのポインタ。
  • *reserved = true;:
    • 追加されたこの行は、reserved ポインタが指すメモリ位置に true を書き込みます。これにより、この runtime·SysReserve の呼び出しが成功したことを呼び出し元に通知します。
    • この行が追加されたことで、runtime·SysReserve を呼び出す側のコードは、reserved 引数をチェックすることで、メモリ予約が意図通りに行われたことを確認できるようになります。これは、特にWindows環境でのメモリ管理ロジックにおいて、より堅牢なエラーハンドリングや状態管理を可能にするために必要とされた変更であると推測されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go Runtime, Memory Management)
  • Goのソースコード (特に src/pkg/runtime/ ディレクトリ)
  • Gerrit Code Review (go-review.googlesource.com)
  • Windows API ドキュメント (VirtualAllocなど、メモリ管理関連)
  • Go言語のスタック成長に関する技術記事
  • 競合状態に関する一般的なプログラミングの概念