[インデックス 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は特定できませんでした。