[インデックス 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言語の初期開発段階における標準ライブラリの成熟化と安定化を目的としています。
-
sync.Once
のデッドロック回避: 従来のonce
パッケージの実装は、内部でスレッド(goroutine)とチャネルを使用していました。しかし、初期化関数f()
の中で再度once.Do(f)
を呼び出すような再帰的なケースでデッドロックが発生する可能性がありました。これを解決するため、よりシンプルでデッドロック耐性のあるロックベースの実装への変更が必要とされました。これは、並行処理のプリミティブが正しく機能することの重要性を示しています。 -
os.Setenv
の実装: 環境変数をプログラムから設定する機能は、多くのアプリケーションで必要とされる基本的な機能です。Goのos
パッケージにSetenv
が存在しないことは、標準ライブラリの機能不足と見なされ、その追加が求められました。これにより、Goプログラムがより柔軟に環境設定を操作できるようになります。 -
スクリプトからの「export」削除: Goのビルドシステムやテストスクリプトにおいて、シェルスクリプトの
export
コマンドが不適切に使用されている箇所がありました。これは、Goのツールチェーンが環境変数を扱う方法や、スクリプトの移植性・堅牢性に関わる問題であり、修正が必要でした。 -
time
パッケージの命名規則修正: Go言語では、エクスポートされない(パッケージ外からアクセスできない)識別子には小文字で始まる名前を使用し、エクスポートされる識別子には大文字で始まる名前を使用するという命名規則があります。また、内部的なヘルパー関数や構造体には_
プレフィックスを使用しないのが一般的です。time
パッケージ内でこの規則に反する命名(例:_TimeTest
,_Data
)があったため、Goのコーディングスタイルガイドラインに準拠させるための修正が行われました。 -
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())
関数は、以下のロジックで動作する:joblock
をロックする。jobs
マップにf
に対応するjob
が存在するか確認する。- 存在しない場合(初回呼び出し):
- 新しい
job
を作成し、そのMutex
をロックする。 jobs
マップに登録する。joblock
をアンロックする。f()
を実行する。job.done
をtrue
に設定し、job
のMutex
をアンロックする。
- 新しい
- 存在する場合(2回目以降の呼び出し):
joblock
をアンロックする。job.done
がtrue
でない場合(つまり、他のgoroutineが初期化中である場合)、job
のMutex
をロックし、すぐにアンロックする。これにより、初期化が完了するまで待機する。
- このロックベースのアプローチにより、再帰的な呼び出しでもデッドロックが発生しなくなり、よりシンプルで堅牢な実装になった。
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.go
のExec
関数も更新され、envv
がnil
の場合に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
:- テスト構造体
_TimeTest
がTimeTest
に変更された(アンダースコアの削除)。 - テストデータ配列
utctests
とlocaltests
の要素の型が_TimeTest
からTimeTest
に変更された。 - 比較ヘルパー関数
_Same
がsame
に変更された(アンダースコアの削除)。 init()
関数が追加され、テスト実行前にos.Setenv("TZ", "US/Pacific")
を呼び出すことで、タイムゾーンを強制的にUS/Pacific
に設定するようになった。これにより、テストが実行される環境のタイムゾーン設定に依存せず、一貫した結果が得られるようになった。
- テスト構造体
src/lib/time/zoneinfo.go
:- 内部的な定数名
_MaxFileSize
,_HeaderSize
がmaxFileSize
,headerSize
に変更された。 - 内部的な構造体名
_Data
,_Zone
,_Zonetime
がdata
,zone
,zonetime
に変更された。 - 内部的なメソッド名
Read
,Big4
,Byte
がread
,big4
,byte
に変更された。 - 内部的なヘルパー関数
_ByteString
がbyteString
に変更された。 readinfofile
関数内でio.Readn
を使用してファイル読み込みを行うように変更され、エラーハンドリングが改善された。_SetupZone
関数がsetupZone
に変更され、TZ
環境変数の存在を確認し、それに応じてタイムゾーン情報ファイルを読み込むロジックが追加された。これにより、/etc/localtime
だけでなく、TZ
環境変数で指定されたタイムゾーンファイルも使用できるようになり、テストの柔軟性が向上した。
- 内部的な定数名
5. Makefile
の依存関係更新 (src/lib/Makefile
)
once.6
がsync.dirinstall
に依存するように追加された。これは、once
パッケージがsync
パッケージを使用するようになったため。os.dirinstall
がonce.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
の初期化処理に対する排他制御を提供します。jobs
はfunc()
をキーとし、*job
を値とするマップで、各初期化関数に対応するjob
インスタンスを管理します。joblock
はグローバルなsync.Mutex
で、jobs
マップへのアクセスを保護します。
Do(f func())
関数は、以下のロジックで動作します。
- まず
joblock
をロックし、jobs
マップへの安全なアクセスを確保します。 jobs
マップにf
に対応するjob
が既に存在するかを確認します。- 存在しない場合(初回呼び出し):
- 新しい
job
インスタンスj
を作成します。 j.Lock()
を呼び出し、この特定の初期化ジョブに対するロックを取得します。jobs
マップにf
とj
を関連付けて登録します。joblock.Unlock()
を呼び出し、他のgoroutineがjobs
マップにアクセスできるようにします。f()
を実行します。これが実際に一度だけ実行される初期化コードです。j.done = true
を設定し、初期化が完了したことをマークします。j.Unlock()
を呼び出し、この初期化ジョブに対するロックを解放します。
- 新しい
- 存在する場合(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
)が使用されていました。また、タイムゾーン関連のテストが実行環境のタイムゾーン設定に依存する可能性がありました。
変更後、以下の点が改善されました。
_TimeTest
はTimeTest
に、_Same
はsame
に変更され、Goの命名規則に準拠しました。- 最も重要な変更は、
init()
関数が追加されたことです。Goのinit()
関数は、パッケージがインポートされた際に自動的に実行されます。このinit()
関数内でos.Setenv("TZ", "US/Pacific")
が呼び出されています。os.Setenv
は、このコミットで新しく実装された機能です。"TZ", "US/Pacific"
は、テストが実行される前にシステムのタイムゾーンを強制的にUS/Pacific
に設定します。- これにより、テストが実行されるマシンの実際のタイムゾーン設定に関わらず、タイムゾーンに依存するテスト(特に夏時間に関連する
localtests
)が常に同じ、予測可能な環境で実行されるようになります。これは、テストの信頼性と再現性を大幅に向上させ、「non-MTV machines」でのテスト失敗問題を解決するのに役立ちます。
この変更は、テストの堅牢性を高め、異なる環境間でのテスト結果の一貫性を保証するために不可欠です。
関連リンク
- Go言語の
sync.Once
ドキュメント: https://pkg.go.dev/sync#Once - Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
time
パッケージドキュメント: https://pkg.go.dev/time - Go言語のコーディングスタイルガイドライン (Effective Go - Names): https://go.dev/doc/effective_go#names
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- TZif (Time Zone Information Format) について: https://en.wikipedia.org/wiki/Tz_database#TZif_format
Makefile
の基本: https://www.gnu.org/software/make/manual/make.html- Go言語の
init
関数について: https://go.dev/doc/effective_go#initialization - Go言語の環境変数について: https://go.dev/blog/os-package
- Go言語の
sync
パッケージの進化 (初期の議論など): Goのメーリングリストや初期の設計ドキュメントが参考になる可能性がありますが、特定のURLは特定できませんでした。