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

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

このコミットは、Go言語の標準ライブラリにおける複数の改善とバグ修正をまとめたものです。主な変更点としては、sync.Once の実装がスレッドベースからロックベースに変更され、再帰的な once 呼び出しにおけるデッドロックが回避されるようになりました。また、os.Setenv が実装され、環境変数の設定機能が追加されました。その他、スクリプトからの「export」キーワードの削除、time パッケージ内の命名規則の修正、および特定の環境(非MTVマシン)での time テストの修正が含まれています。

コミット

  • コミットハッシュ: ff3173849e844471c769b7e8f8e789769f1327b8
  • 作者: Russ Cox rsc@golang.org
  • コミット日時: Sun Feb 15 22:12:35 2009 -0800

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

https://github.com/golang/go/commit/ff3173849e844471c769b7e8f8e789769f1327b8

元コミット内容

assorted changes:
        - use a lock instead of a thread in once
                avoids deadlock in recursive once calls
        - implement os.Setenv
        - remove "export" from some scripts
        - remove _ from names in time package
        - fix time test for non-MTV machines

R=r
DELTA=265  (87 added, 58 deleted, 120 changed)
OCL=25057
CL=25057

変更の背景

このコミットは、Go言語の初期開発段階における標準ライブラリの成熟化と安定化を目的としています。

  1. sync.Once のデッドロック回避: 従来の once パッケージの実装は、内部でスレッド(goroutine)とチャネルを使用していました。しかし、初期化関数 f() の中で再度 once.Do(f) を呼び出すような再帰的なケースでデッドロックが発生する可能性がありました。これを解決するため、よりシンプルでデッドロック耐性のあるロックベースの実装への変更が必要とされました。これは、並行処理のプリミティブが正しく機能することの重要性を示しています。

  2. os.Setenv の実装: 環境変数をプログラムから設定する機能は、多くのアプリケーションで必要とされる基本的な機能です。Goの os パッケージに Setenv が存在しないことは、標準ライブラリの機能不足と見なされ、その追加が求められました。これにより、Goプログラムがより柔軟に環境設定を操作できるようになります。

  3. スクリプトからの「export」削除: Goのビルドシステムやテストスクリプトにおいて、シェルスクリプトの export コマンドが不適切に使用されている箇所がありました。これは、Goのツールチェーンが環境変数を扱う方法や、スクリプトの移植性・堅牢性に関わる問題であり、修正が必要でした。

  4. time パッケージの命名規則修正: Go言語では、エクスポートされない(パッケージ外からアクセスできない)識別子には小文字で始まる名前を使用し、エクスポートされる識別子には大文字で始まる名前を使用するという命名規則があります。また、内部的なヘルパー関数や構造体には _ プレフィックスを使用しないのが一般的です。time パッケージ内でこの規則に反する命名(例: _TimeTest, _Data)があったため、Goのコーディングスタイルガイドラインに準拠させるための修正が行われました。

  5. time テストの修正 (非MTVマシン): time パッケージのテストが特定の環境(おそらく開発者の環境や特定のテストインフラ)に依存しており、それ以外の環境("non-MTV machines")で失敗する問題がありました。これは、テストの移植性と信頼性を確保するために修正されるべき重要なバグでした。特にタイムゾーンの扱いは環境に依存しやすいため、テストが堅牢である必要があります。

これらの変更は、Go言語の標準ライブラリの堅牢性、機能性、およびコーディングスタイルの統一性を向上させるための継続的な取り組みの一環として行われました。

前提知識の解説

1. sync.Once (Go言語の初期化メカニズム)

sync.Once は、Go言語の sync パッケージで提供されるプリミティブで、特定のコードブロックがプログラムの実行中に一度だけ実行されることを保証します。これは、遅延初期化(lazy initialization)や、複数のgoroutineから同時にアクセスされる可能性のある共有リソースの初期化に特に有用です。

従来の(このコミット以前の)実装の課題: このコミット以前の once パッケージ(src/lib/once.go)は、内部でgoroutineとチャネルを使用して、初期化関数の実行を調整していました。具体的には、server() というgoroutineがバックグラウンドで動作し、Do() 関数からのリクエストを処理していました。 この設計は、初期化関数 f() の中で再び once.Do(f) を呼び出すような再帰的なシナリオでデッドロックを引き起こす可能性がありました。これは、f() が実行を完了するまで Do() がブロックし、同時に f()Do() を呼び出すことで、無限の待機状態に陥るためです。

2. 環境変数と os.Setenv

環境変数(Environment Variables)は、オペレーティングシステムがプロセスに提供する動的な名前付き値のセットです。プログラムはこれらの変数にアクセスして、実行時の設定や動作を変更できます。 os.Setenv は、プログラムの実行中に新しい環境変数を設定したり、既存の環境変数の値を変更したりするための関数です。これは、テスト、デバッグ、または特定の実行環境のシミュレーションなど、さまざまなシナリオで役立ちます。

3. Go言語の命名規則

Go言語には、識別子(変数、関数、型など)の命名に関する明確な規則があります。

  • エクスポートされる識別子: パッケージ外からアクセス可能にするには、識別子の最初の文字を大文字にする必要があります。
  • エクスポートされない識別子: パッケージ内でのみ使用される識別子は、最初の文字を小文字にする必要があります。
  • アンダースコア (_) プレフィックス: Goの慣習では、内部的な変数や関数に _ プレフィックスを使用することは一般的ではありません。これは、他の言語(例: Python)とは異なります。このコミットでは、_TimeTest_Data のような命名が修正されています。

4. タイムゾーン情報ファイル (TZif)

time パッケージは、タイムゾーン情報を扱うために、通常 /usr/share/zoneinfo ディレクトリに存在するTZif (Time Zone Information Format) ファイルを読み込みます。これらのファイルは、特定のタイムゾーンにおける夏時間(Daylight Saving Time, DST)の開始・終了時刻やUTCからのオフセットなどの情報を含んでいます。 TZ 環境変数は、システムが使用するタイムゾーンを指定するために使用されます。TZ が設定されていない場合、通常は /etc/localtime がデフォルトのタイムゾーン情報として使用されます。

5. Makefile とビルドシステム

Goの初期のビルドシステムでは、Makefile が使用されていました。Makefile は、プログラムのコンパイル、リンク、テストなどのビルドプロセスを自動化するためのツールです。このコミットでは、Makefile の依存関係が更新され、新しいパッケージの追加や既存パッケージの依存関係の変更が反映されています。

技術的詳細

1. sync.Once の実装変更 (src/lib/once.go)

  • 変更前:

    • _Job 構造体と _Request 構造体を使用して、初期化ジョブの状態とリクエストを管理。
    • service チャネルと jobmap を使用して、server() goroutineが初期化の実行をシリアライズ。
    • Do() 関数は、server() goroutineとのチャネル通信を通じて初期化を調整。
    • 再帰的な once.Do 呼び出しでデッドロックの可能性があった。
  • 変更後:

    • sync パッケージをインポートし、sync.Mutex を使用。
    • job 構造体は done フラグと sync.Mutex を持つ。
    • jobs マップとグローバルな joblock (sync.Mutex) を使用して、初期化ジョブの管理と排他制御を行う。
    • Do(f func()) 関数は、以下のロジックで動作する:
      1. joblock をロックする。
      2. jobs マップに f に対応する job が存在するか確認する。
      3. 存在しない場合(初回呼び出し):
        • 新しい job を作成し、その Mutex をロックする。
        • jobs マップに登録する。
        • joblock をアンロックする。
        • f() を実行する。
        • job.donetrue に設定し、jobMutex をアンロックする。
      4. 存在する場合(2回目以降の呼び出し):
        • joblock をアンロックする。
        • job.donetrue でない場合(つまり、他のgoroutineが初期化中である場合)、jobMutex をロックし、すぐにアンロックする。これにより、初期化が完了するまで待機する。
    • このロックベースのアプローチにより、再帰的な呼び出しでもデッドロックが発生しなくなり、よりシンプルで堅牢な実装になった。

2. os.Setenv の実装 (src/lib/os/env.go)

  • 変更前: Setenv は存在せず、コメントで「Setenv doesn't exist yet: don't have the run-time hooks yet」と記載されていた。環境変数は sys.Envs から直接読み込まれていた。
  • 変更後:
    • env という map[string]string 型のグローバル変数が追加され、環境変数をメモリ上で管理するようになった。
    • copyenv() 関数が追加され、sys.Envs(システムから取得した環境変数)を初回アクセス時に env マップにコピーする。この処理は once.Do(copyenv) を使用して一度だけ実行される。
    • Getenv(key string): copyenv() を呼び出して環境変数をロードした後、env マップから指定されたキーの値を返す。
    • Setenv(key, value string): copyenv() を呼び出して環境変数をロードした後、env マップにキーと値を設定する。
    • Clearenv(): copyenv() を呼び出した後、env マップを空にする。
    • Environ(): copyenv() を呼び出した後、env マップの内容を []string 形式("KEY=VALUE")で返す。
    • src/lib/os/exec.goExec 関数も更新され、envvnil の場合に Environ() を呼び出して現在の環境変数を引き継ぐようになった。

3. スクリプトからの「export」削除 (src/lib/syscall/mkdarwin, src/lib/syscall/mklinux, src/lib/syscall/mksignal)

  • これらのスクリプトは、Goのシステムコール定数を生成するために使用されるPerlスクリプトです。
  • 変更前は、生成されるGoコードの定数宣言で export const のように export キーワードが誤って使用されていました。
  • 変更後、export キーワードが削除され、単に const となるように修正されました。これは、Go言語には export というキーワードが存在しないため、生成されるコードが不正になるのを防ぐための修正です。

4. time パッケージの命名規則修正 (src/lib/time/time.go, src/lib/time/time_test.go, src/lib/time/zoneinfo.go)

  • src/lib/time/time.go:
    • SecondsToUTC 関数内で設定されるタイムゾーン名が "GMT" から "UTC" に変更された。これは、協定世界時(Coordinated Universal Time)をより正確に表現するため。
    • SecondsToLocalTime 関数内でタイムゾーン名を設定する際に、time.LookupTimezone から返される z を直接使用するように変更された。
  • src/lib/time/time_test.go:
    • テスト構造体 _TimeTestTimeTest に変更された(アンダースコアの削除)。
    • テストデータ配列 utctestslocaltests の要素の型が _TimeTest から TimeTest に変更された。
    • 比較ヘルパー関数 _Samesame に変更された(アンダースコアの削除)。
    • init() 関数が追加され、テスト実行前に os.Setenv("TZ", "US/Pacific") を呼び出すことで、タイムゾーンを強制的に US/Pacific に設定するようになった。これにより、テストが実行される環境のタイムゾーン設定に依存せず、一貫した結果が得られるようになった。
  • src/lib/time/zoneinfo.go:
    • 内部的な定数名 _MaxFileSize, _HeaderSizemaxFileSize, headerSize に変更された。
    • 内部的な構造体名 _Data, _Zone, _Zonetimedata, zone, zonetime に変更された。
    • 内部的なメソッド名 Read, Big4, Byteread, big4, byte に変更された。
    • 内部的なヘルパー関数 _ByteStringbyteString に変更された。
    • readinfofile 関数内で io.Readn を使用してファイル読み込みを行うように変更され、エラーハンドリングが改善された。
    • _SetupZone 関数が setupZone に変更され、TZ 環境変数の存在を確認し、それに応じてタイムゾーン情報ファイルを読み込むロジックが追加された。これにより、/etc/localtime だけでなく、TZ 環境変数で指定されたタイムゾーンファイルも使用できるようになり、テストの柔軟性が向上した。

5. Makefile の依存関係更新 (src/lib/Makefile)

  • once.6sync.dirinstall に依存するように追加された。これは、once パッケージが sync パッケージを使用するようになったため。
  • os.dirinstallonce.install に依存するように追加された。これは、os パッケージが once パッケージを使用するようになったため(os.Getenv などで once.Do を使用するため)。
  • exec.6 の依存関係が移動された(実質的な変更なし)。

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

src/lib/once.go (sync.Once の実装変更)

--- a/src/lib/once.go
+++ b/src/lib/once.go
@@ -4,74 +4,41 @@
 // For one-time initialization that is not done during init.
 // Wrap the initialization in a niladic function f() and call
-//	once.Do(&f)
-// If multiple processes call once.Do(&f) simultaneously
+//	once.Do(f)
+// If multiple processes call once.Do(f) simultaneously
 // with the same f argument, only one will call f, and the
 // others will block until f finishes running.
 
 package once
 
-type _Job struct {
-	done bool;
-	doit chan bool;	// buffer of 1
-}
+import "sync"
 
-type _Request struct {
-	f func();
-	reply chan *_Job
-}
+type job struct {
+	done bool;
+	sync.Mutex;	// should probably be sync.Notification or some such
+}
 
-var service = make(chan _Request)
-var jobmap = make(map[func()]*_Job)
+var jobs = make(map[func()]*job)
+var joblock sync.Mutex;
 
-// Moderate access to the jobmap.
-// Even if accesses were thread-safe (they should be but are not)
-// something needs to serialize creation of new jobs.
-// That's what the Server does.
-func server() {
-	for {
-		req := <-service;
-		job, present := jobmap[req.f];
-		if !present {
-			job = new(_Job);
-			job.doit = make(chan bool, 1);
-			job.doit <- true;
-			jobmap[req.f] = job
-		}
-		req.reply <- job
-	}
-}
- 
 func Do(f func()) {
-	// Look for job in map (avoids channel communication).
-	// If not there, ask map server to make one.
-	// TODO: Uncomment use of jobmap[f] once
-	// maps are thread-safe.
-	var job *_Job;
-	var present bool;
-	// job, present = jobmap[f]
+	joblock.Lock();
+	j, present := jobs[f];
 	if !present {
-		c := make(chan *_Job);
-		service <- _Request(f, c);
-		job = <-c
-	}
-
-	// Optimization
-	if job.done {
-		return
-	}
-
-	// If we're the first one, job.doit has a true waiting.
-	if <-job.doit {
+		// run it
+		j = new(job);
+		j.Lock();
+		jobs[f] = j;
+		joblock.Unlock();
 		f();
-		job.done = true
+		j.done = true;
+		j.Unlock();
 	} else {
-		// Leave a false waiting for the next guy.
-		job.doit <- false
+		// wait for it
+		joblock.Unlock();
+		if j.done != true {
+			j.Lock();
+			j.Unlock();
+		}
 	}
-
-func init() {
-	go server()
 }

src/lib/os/env.go (os.Setenv の実装)

--- a/src/lib/os/env.go
+++ b/src/lib/os/env.go
@@ -3,25 +3,71 @@
 // license that can be found in the LICENSE file.
 
 // Environment variables.
-// Setenv doesn't exist yet: don't have the run-time hooks yet
 
 package os
 
-import os "os"
+import (
+	"once";
+	"os";
+)
 
 var (
 	ENOENV = NewError("no such environment variable");
++
+	env map[string] string;
+)
+
+func copyenv() {
+	env = make(map[string] string);
+	for i, s := range sys.Envs {
+		for j := 0; j < len(s); j++ {
+			if s[j] == '=' {
+				env[s[0:j]] = s[j+1:len(s)];
+				break;
+			}
+		}
+	}
+}
+
+func Getenv(key string) (value string, err *Error) {
+	once.Do(copyenv);
+
+	if len(key) == 0 {
+		return "", EINVAL;
+	}
+	v, ok := env[key];
+	if !ok {
+		return "", ENOENV;
+	}
+	return v, nil;
+}
+
+func Setenv(key, value string) *Error {
+	once.Do(copyenv);
+
+	if len(key) == 0 {
+		return EINVAL;
+	}
+	env[key] = value;
+	return nil;
+}
+
+func Clearenv() {
+	once.Do(copyenv);	// prevent copyenv in Getenv/Setenv
+	env = make(map[string] string);
+}
+
+func Environ() []string {
+	once.Do(copyenv);
+	a := make([]string, len(env));
+	i := 0;
+	for k, v := range(env) {
+		// check i < len(a) for safety,
+		// in case env is changing underfoot.
+		if i < len(a) {
+			a[i] = k + "=" + v;
+			i++;
+		}
+	}
+	return a[0:i];
+}

src/lib/time/time_test.go (テストのタイムゾーン設定)

--- a/src/lib/time/time_test.go
+++ b/src/lib/time/time_test.go
@@ -5,31 +5,39 @@
 package time
 
 import (
+	"os";
 	"testing";
 	"time";
 )
 
-type _TimeTest struct {
+func init() {
+	// Force US Pacific time for daylight-savings
+	// tests below (localtests).  Needs to be set
+	// before the first call into the time library.
+	os.Setenv("TZ", "US/Pacific");
+}
+
+type TimeTest struct {
 	seconds int64;
 	golden Time;
 }
 
-var utctests = []_TimeTest (
-	_TimeTest(0, Time(1970, 1, 1, 0, 0, 0, Thursday, 0, "GMT")),
-	_TimeTest(1221681866, Time(2008, 9, 17, 20, 4, 26, Wednesday, 0, "GMT")),
-	_TimeTest(-1221681866, Time(1931, 4, 16, 3, 55, 34, Thursday, 0, "GMT")),
-	_TimeTest(1e18, Time(31688740476, 10, 23, 1, 46, 40, Friday, 0, "GMT")),
-	_TimeTest(-1e18, Time(-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "GMT")),
-	_TimeTest(0x7fffffffffffffff, Time(292277026596, 12, 4, 15, 30, 7, Sunday, 0, "GMT")),
-	_TimeTest(-0x8000000000000000, Time(-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "GMT"))
+var utctests = []TimeTest (
+	TimeTest(0, Time(1970, 1, 1, 0, 0, 0, Thursday, 0, "UTC")),
+	TimeTest(1221681866, Time(2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC")),
+	TimeTest(-1221681866, Time(1931, 4, 16, 3, 55, 34, Thursday, 0, "UTC")),
+	TimeTest(1e18, Time(31688740476, 10, 23, 1, 46, 40, Friday, 0, "UTC")),
+	TimeTest(-1e18, Time(-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "UTC")),
+	TimeTest(0x7fffffffffffffff, Time(292277026596, 12, 4, 15, 30, 7, Sunday, 0, "UTC")),
+	TimeTest(-0x8000000000000000, Time(-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "UTC"))
 )
 
-var localtests = []_TimeTest (
-	_TimeTest(0, Time(1969, 12, 31, 16, 0, 0, Wednesday, -8*60*60, "PST")),
-	_TimeTest(1221681866, Time(2008, 9, 17, 13, 4, 26, Wednesday, -7*60*60, "PDT"))
+var localtests = []TimeTest (
+	TimeTest(0, Time(1969, 12, 31, 16, 0, 0, Wednesday, -8*60*60, "PST")),
+	TimeTest(1221681866, Time(2008, 9, 17, 13, 4, 26, Wednesday, -7*60*60, "PDT"))
 )
 
-func _Same(t, u *Time) bool {
+func same(t, u *Time) bool {
 	return t.Year == u.Year
 		&& t.Month == u.Month
 		&& t.Day == u.Day
@@ -50,7 +58,7 @@ func TestSecondsToUTC(t *testing.T) {
 	\tif newsec != sec {
 	\t\tt.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec);
 	\t}
-\t\tif !_Same(tm, golden) {
+\t\tif !same(tm, golden) {
 	\t\tt.Errorf("SecondsToUTC(%d):", sec);
 	\t\tt.Errorf("  want=%v", *golden);
 	\t\tt.Errorf("  have=%v", *tm);
@@ -67,7 +75,7 @@ func TestSecondsToLocalTime(t *testing.T) {
 	\tif newsec != sec {
 	\t\tt.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec);
 	\t}
-\t\tif !_Same(tm, golden) {
+\t\tif !same(tm, golden) {
 	\t\tt.Errorf("SecondsToLocalTime(%d):", sec);
 	\t\tt.Errorf("  want=%v", *golden);
 	\t\tt.Errorf("  have=%v", *tm);

コアとなるコードの解説

src/lib/once.go

このファイルは、Goの sync.Once の初期の実装を示しています。変更前は、_Job_Request という内部構造体、service チャネル、jobmap を用いて、server() というバックグラウンドgoroutineが初期化処理を管理していました。この設計は、初期化関数 f() が再帰的に once.Do(f) を呼び出す場合にデッドロックを引き起こす可能性がありました。

変更後、sync パッケージがインポートされ、sync.Mutex が導入されました。

  • job 構造体は、done というブール値と sync.Mutex を持ちます。done は初期化が完了したかどうかを示し、sync.Mutex はその job の初期化処理に対する排他制御を提供します。
  • jobsfunc() をキーとし、*job を値とするマップで、各初期化関数に対応する job インスタンスを管理します。
  • joblock はグローバルな sync.Mutex で、jobs マップへのアクセスを保護します。

Do(f func()) 関数は、以下のロジックで動作します。

  1. まず joblock をロックし、jobs マップへの安全なアクセスを確保します。
  2. jobs マップに f に対応する job が既に存在するかを確認します。
  3. 存在しない場合(初回呼び出し):
    • 新しい job インスタンス j を作成します。
    • j.Lock() を呼び出し、この特定の初期化ジョブに対するロックを取得します。
    • jobs マップに fj を関連付けて登録します。
    • joblock.Unlock() を呼び出し、他のgoroutineが jobs マップにアクセスできるようにします。
    • f() を実行します。これが実際に一度だけ実行される初期化コードです。
    • j.done = true を設定し、初期化が完了したことをマークします。
    • j.Unlock() を呼び出し、この初期化ジョブに対するロックを解放します。
  4. 存在する場合(2回目以降の呼び出し):
    • joblock.Unlock() を呼び出し、jobs マップへのロックを解放します。
    • if j.done != true の条件で、もし初期化がまだ完了していない場合(つまり、他のgoroutineが現在 f() を実行中である場合)、j.Lock() を呼び出し、すぐに j.Unlock() を呼び出します。これにより、f() の実行が完了して j.Unlock() が呼ばれるまで、現在のgoroutineはブロックされます。初期化が既に完了している場合は、このブロックはスキップされます。

この新しい実装は、チャネルベースの複雑なロジックを sync.Mutex を用いたより直接的な排他制御に置き換えることで、再帰的な呼び出しによるデッドロックの問題を解決し、コードの可読性と堅牢性を向上させています。

src/lib/os/env.go

このファイルは、Goプログラムが環境変数と対話するための機能を提供します。 変更前は、Setenv のような環境変数を設定する機能は存在せず、Getenv はシステムから直接環境変数を読み込む sys.Envs に依存していました。

変更後、once パッケージがインポートされ、環境変数をメモリ上で管理するための env マップが導入されました。

  • env map[string]string は、環境変数のキーと値を保持するマップです。
  • copyenv() 関数は、システムから取得した環境変数(sys.Envs)を解析し、env マップにコピーする役割を担います。この関数は、once.Do(copyenv) を通じて、環境変数への最初のアクセス時に一度だけ実行されることが保証されます。これにより、環境変数の初期ロードが効率的に行われます。
  • Getenv(key string) は、まず copyenv() を呼び出して env マップが初期化されていることを確認し、その後 env マップから指定された key の値を検索して返します。
  • Setenv(key, value string) は、同様に copyenv() を呼び出して env マップを初期化した後、env マップに新しい環境変数 key とその value を追加または更新します。
  • Clearenv() は、env マップを空にすることで、すべての環境変数をクリアします。
  • Environ() は、env マップの内容を []string のスライスとして返します。各要素は "KEY=VALUE" の形式です。

この変更により、Goプログラムは実行時に環境変数を動的に操作できるようになり、より柔軟なアプリケーション開発が可能になりました。once.Do の使用は、環境変数の初期ロードが一度だけ行われることを保証し、パフォーマンスと正確性を両立させています。

src/lib/time/time_test.go

このファイルは、time パッケージのテストケースを含んでいます。 変更前は、テスト構造体やヘルパー関数にGoの命名規則に反するアンダースコアプレフィックス(例: _TimeTest, _Same)が使用されていました。また、タイムゾーン関連のテストが実行環境のタイムゾーン設定に依存する可能性がありました。

変更後、以下の点が改善されました。

  • _TimeTestTimeTest に、_Samesame に変更され、Goの命名規則に準拠しました。
  • 最も重要な変更は、init() 関数が追加されたことです。Goの init() 関数は、パッケージがインポートされた際に自動的に実行されます。この init() 関数内で os.Setenv("TZ", "US/Pacific") が呼び出されています。
    • os.Setenv は、このコミットで新しく実装された機能です。
    • "TZ", "US/Pacific" は、テストが実行される前にシステムのタイムゾーンを強制的に US/Pacific に設定します。
    • これにより、テストが実行されるマシンの実際のタイムゾーン設定に関わらず、タイムゾーンに依存するテスト(特に夏時間に関連する localtests)が常に同じ、予測可能な環境で実行されるようになります。これは、テストの信頼性と再現性を大幅に向上させ、「non-MTV machines」でのテスト失敗問題を解決するのに役立ちます。

この変更は、テストの堅牢性を高め、異なる環境間でのテスト結果の一貫性を保証するために不可欠です。

関連リンク

参考にした情報源リンク