[インデックス 13482] ファイルの概要
このコミットは、Go言語の標準ライブラリ sync
パッケージ内の WaitGroup
型のドキュメントとエラーメッセージの改善に関するものです。具体的には、WaitGroup.Add
メソッドに負の値を渡してカウンタが負になった場合に発生するパニックメッセージをより明確にし、その挙動をドキュメントに明記しています。
コミット
- コミットハッシュ:
55ff3f7076e9ef45f8c853eece8acf36e891d885
- 作者: Rob Pike r@golang.org
- コミット日時: 2012年7月19日 木曜日 11:55:03 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/55ff3f7076e9ef45f8c853eece8acf36e891d885
元コミット内容
sync: mention that WaitGroup.Add panics
Fixes #3839.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6428053
変更の背景
このコミットは、Go言語のIssue #3839を修正するために行われました。sync.WaitGroup
は、複数のゴルーチンが完了するのを待つための同期プリミティブです。Add
メソッドはカウンタを増減させますが、このカウンタが負の値になることは論理的に不正な状態です。以前のバージョンでは、カウンタが負になった場合にパニックが発生していましたが、そのパニックメッセージが「sync: negative WaitGroup count」と表示されていました。
この変更の背景には、以下の点が考えられます。
- 明確なエラーメッセージ: 「count」という単語よりも「counter」という単語の方が、
WaitGroup
の内部状態を指す上でより正確で分かりやすいと判断された可能性があります。これにより、開発者がパニックの原因を特定しやすくなります。 - ドキュメントの改善:
WaitGroup.Add
のドキュメントに「If the counter goes negative, Add panics.」という記述を追加することで、このメソッドの挙動に関する重要な制約を明示し、誤用を防ぐことを目的としています。これにより、ユーザーはWaitGroup
を使用する際に、カウンタが負にならないように注意する必要があることを事前に知ることができます。 - 堅牢性の向上: パニックメッセージの改善とドキュメントの追加は、ライブラリの堅牢性と使いやすさを向上させるための一般的なプラクティスです。
前提知識の解説
Go言語の sync.WaitGroup
sync.WaitGroup
は、Go言語で複数のゴルーチンが完了するのを待つための同期メカニズムです。主に以下の3つのメソッドで構成されます。
Add(delta int)
:WaitGroup
のカウンタにdelta
を加算します。通常、新しいゴルーチンを開始する前にAdd(1)
を呼び出してカウンタを増やします。delta
は負の値も取ることができ、これによりカウンタを減らすことも可能です。Done()
:Add(-1)
のショートカットです。通常、ゴルーチンがその処理を完了したときに呼び出され、カウンタを1減らします。Wait()
:WaitGroup
のカウンタがゼロになるまでブロックします。すべてのゴルーチンがDone()
を呼び出してカウンタがゼロになると、Wait()
はブロックを解除します。
Go言語の panic
Go言語における panic
は、プログラムの実行を停止させる回復不可能なエラーを示すメカニズムです。panic
が発生すると、現在のゴルーチンの通常の実行フローは停止し、遅延関数(defer
)が実行され、その後、コールスタックを遡ってパニックが伝播します。もし、スタックのどこにも recover
関数が呼び出されていない場合、プログラム全体がクラッシュします。
panic
は、通常、プログラマの論理的な誤りや、プログラムが継続できないような予期せぬ状態(例: 配列の範囲外アクセス、nilポインタのデリファレンス)が発生した場合に使用されます。WaitGroup
のカウンタが負になることは、WaitGroup
の設計上の意図に反する不正な状態であるため、panic
を発生させることでその誤用を開発者に知らせています。
atomic.AddInt32
atomic
パッケージは、低レベルのアトミック操作を提供します。アトミック操作は、複数のゴルーチンが同時にアクセスしても、その操作が中断されることなく完全に実行されることを保証します。atomic.AddInt32
は、int32
型の変数にアトミックに値を加算する関数です。WaitGroup
のカウンタは複数のゴルーチンから同時にアクセスされる可能性があるため、データ競合を防ぐためにアトミック操作が使用されています。
技術的詳細
このコミットの技術的な変更点は以下の2つです。
-
WaitGroup.Add
メソッドのドキュメント更新:src/pkg/sync/waitgroup.go
のWaitGroup.Add
メソッドのコメントに、以下の行が追加されました。// If the counter goes negative, Add panics.
これにより、
Add
メソッドに負の値を渡してカウンタが負になった場合にパニックが発生するという重要な挙動が明示的にドキュメント化されました。これは、ライブラリのAPI契約の一部となり、ユーザーがWaitGroup
を正しく使用するためのガイドラインを提供します。 -
パニックメッセージの変更:
src/pkg/sync/waitgroup.go
内のパニックメッセージが、panic("sync: negative WaitGroup count")
からpanic("sync: negative WaitGroup counter")
に変更されました。 この変更は、単語の選択に関するもので、WaitGroup
の内部状態を指す際に「count」よりも「counter」の方がより適切であるという判断に基づいています。これにより、エラーメッセージの正確性と一貫性が向上します。 -
テストの更新:
src/pkg/sync/waitgroup_test.go
内のTestWaitGroupMisuse
関数で、期待されるパニックメッセージが新しいメッセージに合わせて更新されました。// 変更前 // if err != "sync: negative WaitGroup count" { // 変更後 if err != "sync: negative WaitGroup counter" {
これは、コードの変更がテストによって適切に検証されることを保証するための標準的なプラクティスです。
コアとなるコードの変更箇所
diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go
index 0165b1ffb2..bc9e738e78 100644
--- a/src/pkg/sync/waitgroup.go
+++ b/src/pkg/sync/waitgroup.go
@@ -32,10 +32,11 @@ type WaitGroup struct {
// Add adds delta, which may be negative, to the WaitGroup counter.
// If the counter becomes zero, all goroutines blocked on Wait() are released.
+// If the counter goes negative, Add panics.
func (wg *WaitGroup) Add(delta int) {
v := atomic.AddInt32(&wg.counter, int32(delta))
if v < 0 {
- panic("sync: negative WaitGroup count")
+ panic("sync: negative WaitGroup counter")
}
if v > 0 || atomic.LoadInt32(&wg.waiters) == 0 {
return
diff --git a/src/pkg/sync/waitgroup_test.go b/src/pkg/sync/waitgroup_test.go
index 34430fc215..84c4cfc37a 100644
--- a/src/pkg/sync/waitgroup_test.go
+++ b/src/pkg/sync/waitgroup_test.go
@@ -50,7 +50,7 @@ func TestWaitGroup(t *testing.T) {
func TestWaitGroupMisuse(t *testing.T) {
defer func() {
err := recover()
- if err != "sync: negative WaitGroup count" {
+ if err != "sync: negative WaitGroup counter" {
t.Fatalf("Unexpected panic: %#v", err)
}
}()
コアとなるコードの解説
src/pkg/sync/waitgroup.go
// Add adds delta, which may be negative, to the WaitGroup counter.
// If the counter becomes zero, all goroutines blocked on Wait() are released.
// If the counter goes negative, Add panics. // <-- 追加された行
func (wg *WaitGroup) Add(delta int) {
v := atomic.AddInt32(&wg.counter, int32(delta))
if v < 0 {
panic("sync: negative WaitGroup counter") // <-- 変更された行
}
// ... (後続のコードは変更なし)
}
// If the counter goes negative, Add panics.
: この行は、WaitGroup.Add
メソッドのドキュメンテーションコメントに追加されました。これにより、WaitGroup
のカウンタが負の値になった場合にパニックが発生するという、このメソッドの重要な挙動が明示的に示されます。これは、APIの利用者が予期せぬ挙動に遭遇するのを防ぎ、より堅牢なコードを書くのに役立ちます。panic("sync: negative WaitGroup counter")
: この行は、WaitGroup
のカウンタが負の値(v < 0
)になった場合に実行されるパニックメッセージを変更しています。以前は「count」という単語が使われていましたが、より正確な「counter」に変更されました。この変更は、エラーメッセージの明確性を高め、デバッグ時に問題の原因を特定しやすくすることを目的としています。
src/pkg/sync/waitgroup_test.go
func TestWaitGroupMisuse(t *testing.T) {
defer func() {
err := recover()
if err != "sync: negative WaitGroup counter" { // <-- 変更された行
t.Fatalf("Unexpected panic: %#v", err)
}
}()
// ... (後続のコードは変更なし)
}
if err != "sync: negative WaitGroup counter"
: このテストコードは、WaitGroup.Add
メソッドの誤用(カウンタが負になるケース)をテストしています。defer
とrecover()
を使用してパニックを捕捉し、捕捉されたパニックメッセージが期待される文字列と一致するかどうかを検証しています。パニックメッセージが「sync: negative WaitGroup counter」に変更されたため、テストもそれに合わせて更新されました。これにより、コードの変更がテストによって正しく検証されていることが保証されます。
関連リンク
- Go言語のIssue #3839: https://github.com/golang/go/issues/3839
- Go言語の変更リスト (CL) 6428053: https://golang.org/cl/6428053
参考にした情報源リンク
- Go言語の
sync
パッケージドキュメント: https://pkg.go.dev/sync - Go言語の
atomic
パッケージドキュメント: https://pkg.go.dev/sync/atomic - A Tour of Go - Defer, Panic, and Recover: https://go.dev/tour/flowcontrol/12
- Go言語におけるエラーハンドリングとパニック: https://go.dev/blog/error-handling-and-go (一般的な情報源として)
- Go言語の
WaitGroup
の使い方 (例): https://gobyexample.com/waitgroups (一般的な情報源として)