[インデックス 1747] ファイルの概要
このコミットは、Go言語の標準ライブラリである sync
パッケージ内の mutex.go
ファイルに対する変更です。具体的には、Mutex
および RWMutex
の構造体とメソッドに詳細なドキュメンテーションが追加されています。これにより、これらの同期プリミティブの利用方法、セマンティクス、および注意点が明確化されました。
コミット
commit 8ba287585a2486132c604981ed1c549d24b9feed
Author: Russ Cox <rsc@golang.org>
Date: Wed Mar 4 21:30:07 2009 -0800
sync: add documentation
R=r
DELTA=63 (57 added, 1 deleted, 5 changed)
OCL=25727
CL=25727
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8ba287585a2486132c604981ed1c549d24b9feed
元コミット内容
sync: add documentation
このコミットの目的は、Go言語の sync
パッケージ、特に Mutex
と RWMutex
に関するドキュメンテーションを追加することです。これにより、これらの同期プリミティブの利用方法や振る舞いがより明確になります。
変更の背景
Go言語は、並行処理を言語レベルでサポートするためにゴルーチンとチャネルを導入しました。しかし、低レベルのライブラリルーチンや特定のパフォーマンス要件を持つシナリオでは、伝統的なミューテックス(相互排他ロック)のような同期プリミティブが必要となります。sync
パッケージは、このような低レベルの同期機能を提供します。
このコミットが行われた2009年3月は、Go言語がまだ初期開発段階にあった時期です。言語の設計思想や標準ライブラリのAPIが固まりつつある中で、開発者がこれらの重要な同期プリミティブを正しく理解し、安全に利用できるようにするためのドキュメンテーションの整備は不可欠でした。特に、Mutex
や RWMutex
のようなプリミティブは、誤用するとデッドロックやデータ競合といった深刻な並行処理バグを引き起こす可能性があるため、そのセマンティクスを明確に記述することが求められました。
また、当時の RWMutex
はまだ完全な実装ではなく、リードロックが実際にはライトロックと同じように振る舞うという既知のバグ(BUG(rsc)
として明記されている)が存在していました。このような未完成な部分についても、ドキュメンテーションを通じてユーザーに正確な情報を提供する必要がありました。
前提知識の解説
1. 並行処理と同期プリミティブ
並行処理(Concurrency)とは、複数の計算が同時に進行しているように見える状態を指します。Go言語では、軽量なスレッドである「ゴルーチン(Goroutine)」と、ゴルーチン間の安全な通信を可能にする「チャネル(Channel)」によって並行処理をサポートしています。
しかし、共有リソースへのアクセスを制御するためには、同期メカニズムが必要です。同期プリミティブは、複数のゴルーチンが同時に共有データにアクセスする際に発生するデータ競合を防ぎ、処理の順序を保証するために使用されます。
2. sync
パッケージ
sync
パッケージは、Go言語で低レベルの同期プリミティブを提供する標準ライブラリです。これには、Mutex
(相互排他ロック)、RWMutex
(読み書きロック)、WaitGroup
、Once
などが含まれます。Goの設計思想では、可能な限りチャネルを用いた通信による同期("Don't communicate by sharing memory; share memory by communicating.")が推奨されますが、パフォーマンスが重視される場合や、より低レベルな制御が必要な場合には sync
パッケージが利用されます。
3. Mutex
(Mutual Exclusion Lock)
Mutex
は、相互排他ロックを提供します。これは、一度に一つのゴルーチンだけが特定のコードセクション(クリティカルセクション)を実行できるようにするメカニズムです。
Lock()
: ミューテックスをロックします。既にロックされている場合、呼び出し元のゴルーチンはロックが解放されるまでブロックされます。Unlock()
: ミューテックスをアンロックします。ロックされていないミューテックスをアンロックしようとすると、ランタイムエラーが発生します。Mutex
は、特定のゴルーチンに関連付けられていません。あるゴルーチンがロックし、別のゴルーチンがアンロックすることも可能です。
4. RWMutex
(Reader/Writer Mutual Exclusion Lock)
RWMutex
は、読み書きロックを提供します。これは、複数のリーダーが同時に共有リソースを読み取ることができる一方で、ライターは排他的にリソースにアクセスできるようにするロックです。
RLock()
: 読み取りのためにロックします。複数のゴルーチンが同時にRLock
を取得できます。RUnlock()
: 読み取りロックを解除します。Lock()
: 書き込みのためにロックします。このロックは排他的であり、他のリーダーやライターのアクセスをブロックします。Unlock()
: 書き込みロックを解除します。
5. アトミック操作とセマフォ
コミット内のコードには、cas
(Compare And Swap)、semacquire
、semrelease
、xadd
といった低レベルの関数が使われています。これらは、Goランタイム内部でミューテックスの実装に使われるアトミック操作やセマフォ操作のプリミティブです。
cas(val *int32, old, new int32) bool
:val
がold
と等しい場合にのみval
をnew
に更新し、成功したかどうかを返します。これはアトミックに行われます。xadd(val *int32, delta int32) (new int32)
:val
にdelta
をアトミックに加算し、加算後の新しい値を返します。semacquire(*int32)
: セマフォを取得します。セマフォが利用可能になるまでブロックします。semrelease(*int32)
: セマフォを解放します。ブロックされているゴルーチンがあれば、一つを再開させます。 これらは、OSレベルの同期プリミティブやCPUの命令セットを利用して実装されており、Goのsync
パッケージの基盤を形成しています。
技術的詳細
このコミットの主要な変更点は、src/lib/sync/mutex.go
ファイルにGoDoc形式のドキュメンテーションが大量に追加されたことです。これにより、sync
パッケージ全体、Mutex
構造体、Lock()
、Unlock()
メソッド、RWMutex
構造体、RLock()
、RUnlock()
、Lock()
、Unlock()
メソッドのそれぞれについて、その目的、振る舞い、および使用上の注意点が詳細に記述されました。
特筆すべきは、RWMutex
の実装に関するコメントです。
// Stub implementation of r/w locks.
// This satisfies the semantics but
// is not terribly efficient.
// The next comment goes in the BUGS section of the document,
// in its own paragraph, without the (rsc) tag.
// BUG(rsc): RWMutex does not (yet) allow multiple readers;
// instead it behaves as if RLock and RUnlock were Lock and Unlock.
このコメントは、当時の RWMutex
がまだ「スタブ実装」であり、効率的ではないこと、そして最も重要な点として、複数のリーダーを許可せず、RLock
と RUnlock
が実質的に Lock
と Unlock
と同じように振る舞うという既知のバグ(BUG(rsc)
)が存在することを明記しています。これは、Go言語の初期開発段階における透明性と、将来的な改善の意図を示すものです。
また、RWMutex
の構造体定義が type RWMutex struct { Mutex; }
から type RWMutex struct { m Mutex; }
に変更されています。これは、埋め込みフィールド(Mutex;
)から名前付きフィールド(m Mutex;
)への変更であり、これにより RWMutex
の内部で Mutex
がどのように利用されているかがより明確になります。機能的には大きな変更ではありませんが、コードの可読性と意図の明確化に寄与します。
全体として、このコミットは機能追加ではなく、既存の機能に対するドキュメンテーションの強化と、初期段階のGo言語における同期プリミティブの現状を正確に反映するためのものです。
コアとなるコードの変更箇所
変更は src/lib/sync/mutex.go
ファイルに集中しています。
-
パッケージレベルのドキュメンテーションの追加:
--- a/src/lib/sync/mutex.go +++ b/src/lib/sync/mutex.go @@ -2,12 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The sync package provides basic synchronization primitives +// such as mutual exclusion locks. These are intended for use +// by low-level library routines. Higher-level synchronization +// is better done via channels and communication. package sync
sync
パッケージの目的と、チャネルによる高レベルな同期が推奨される旨が記述されました。 -
Mutex
構造体とゼロ値に関するドキュメンテーションの追加:--- a/src/lib/sync/mutex.go +++ b/src/lib/sync/mutex.go @@ -2,12 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The sync package provides basic synchronization primitives // such as mutual exclusion locks. These are intended for use // by low-level library routines. Higher-level synchronization // is better done via channels and communication. package sync func cas(val *int32, old, new int32) bool func semacquire(*int32) func semrelease(*int32) +// A Mutex is a mutual exclusion lock. +// Mutexes can be created as part of other structures; +// the zero value for a Mutex is an unlocked mutex. type Mutex struct { key int32; sema int32;
Mutex
が相互排他ロックであること、およびゼロ値がアンロックされた状態のミューテックスであることを説明しています。 -
Lock()
メソッドに関するドキュメンテーションの追加:--- a/src/lib/sync/mutex.go +++ b/src/lib/sync/mutex.go @@ -23,6 +30,9 @@ func xadd(val *int32, delta int32) (new int32) { panic("unreached") } +// Lock locks m. +// If the lock is already in use, the calling goroutine +// blocks until the mutex is available. func (m *Mutex) Lock() { if xadd(&m.key, 1) == 1 { // changed from 0 to 1; we hold lock
Lock()
の振る舞い(ロック取得、ブロック)が記述されました。 -
Unlock()
メソッドに関するドキュメンテーションの追加:--- a/src/lib/sync/mutex.go +++ b/src/lib/sync/mutex.go @@ -31,6 +41,12 @@ func (m *Mutex) Lock() { semacquire(&m.sema); } +// Unlock unlocks m. +// It is a run-time error if m is not locked on entry to Unlock. +// +// A locked Mutex is not associated with a particular goroutine. +// It is allowed for one goroutine to lock a Mutex and then +// arrange for another goroutine to unlock it. func (m *Mutex) Unlock() { if xadd(&m.key, -1) == 0 { // changed from 1 to 0; no contention
Unlock()
の振る舞い(アンロック、ランタイムエラー条件、ゴルーチンとの関連付けがないこと)が記述されました。 -
RWMutex
のスタブ実装に関するコメントとBUG
コメントの追加、および構造体定義の変更:--- a/src/lib/sync/mutex.go +++ b/src/lib/sync/mutex.go @@ -42,17 +58,57 @@ func (m *Mutex) Unlock() { // Stub implementation of r/w locks. // This satisfies the semantics but // is not terribly efficient. -// TODO(rsc): Real r/w locks. +// The next comment goes in the BUGS section of the document,\n +// in its own paragraph, without the (rsc) tag.\n +\n +// BUG(rsc): RWMutex does not (yet) allow multiple readers;\n +// instead it behaves as if RLock and RUnlock were Lock and Unlock.\n +\n +// An RWMutex is a reader/writer mutual exclusion lock.\n +// The lock can be held by an arbitrary number of readers\n +// or a single writer.\n +// RWMutexes can be created as part of other\n +// structures; the zero value for a RWMutex is\n +// an unlocked mutex.\n type RWMutex struct { -\tMutex;\n +\tm Mutex;\n }\n +// RLock locks rw for reading.\n +// If the lock is already locked for writing or there is a writer already waiting\n +// to acquire the lock, RLock blocks until the writer has released the lock.\n func (rw *RWMutex) RLock() { -\tm.Lock();\n +\trw.m.Lock();\n }\n +// RUnlock undoes a single RLock call;\n +// it does not affect other simultaneous readers.\n +// It is a run-time error if rw is not locked for reading\n +// on entry to RUnlock.\n func (rw *RWMutex) RUnlock() { -\tm.Unlock();\n +\trw.m.Unlock();\n }\n +// Lock locks rw for writing.\n +// If the lock is already locked for reading or writing,\n +// Lock blocks until the lock is available.\n +// To ensure that the lock eventually becomes available,\n +// a blocked Lock call excludes new readers from acquiring\n +// the lock.\n +func (rw *RWMutex) Lock() {\n +\trw.m.Lock();\n +}\n +\n +// Unlock unlocks rw for writing.\n +// It is a run-time error if rw is not locked for writing\n +// on entry to Unlock.\n +//\n +// Like for Mutexes,\n +// a locked RWMutex is not associated with a particular goroutine.\n +// It is allowed for one goroutine to RLock (Lock) an RWMutex and then\n +// arrange for another goroutine to RUnlock (Unlock) it.\n +func (rw *RWMutex) Unlock() {\n +\trw.m.Unlock();\n }\n
RWMutex
の概要、読み書きロックのセマンティクス、ゼロ値の振る舞いが記述されました。特に、BUG(rsc)
コメントで当時のRWMutex
が複数のリーダーをサポートしていないことが明記されています。また、RWMutex
の内部フィールドがMutex;
からm Mutex;
に変更されています。
コアとなるコードの解説
このコミットは、Go言語の sync
パッケージにおける Mutex
と RWMutex
のドキュメンテーションを大幅に強化しています。
-
パッケージレベルのドキュメンテーション:
sync
パッケージが低レベルの同期プリミティブを提供し、高レベルな同期にはチャネルが推奨されるというGoの設計哲学が明確に示されています。これは、Goの並行処理モデルを理解する上で非常に重要な指針です。 -
Mutex
のドキュメンテーション:Mutex
が相互排他ロックであること、および他の構造体の一部として作成できること、そしてゼロ値がアンロックされた状態のミューテックスであることが明記されました。これは、sync.Mutex
を明示的に初期化する必要がないというGoの慣習を反映しています。Lock()
メソッドについては、ロックが既に使われている場合に呼び出し元のゴルーチンがブロックされることが説明されています。Unlock()
メソッドについては、ロックされていないミューテックスをアンロックしようとするとランタイムエラーになること、そしてロックされたミューテックスが特定のゴルーチンに関連付けられていないため、あるゴルーチンがロックし、別のゴルーチンがアンロックできるという重要なセマンティクスが強調されています。これは、C++のstd::mutex
などとは異なるGoの柔軟な設計を示しています。
-
RWMutex
のドキュメンテーション:RWMutex
が読み書きロックであり、複数のリーダーまたは単一のライターによって保持できることが説明されています。RLock()
とRUnlock()
、Lock()
とUnlock()
のそれぞれの振る舞いが詳細に記述されています。特に、RLock()
が書き込みロックによってブロックされる条件や、Lock()
が新しいリーダーのロック取得を排除して最終的にロックが利用可能になることを保証するメカニズムが説明されています。- 最も重要な点は、
BUG(rsc)
コメントです。これは、当時のRWMutex
の実装がまだ完全ではなく、RLock
とRUnlock
が実際にはLock
とUnlock
と同じように振る舞い、複数のリーダーを許可しないという既知の制限を正直に開示しています。これは、Go言語の初期開発における透明性と、ユーザーへの正確な情報提供の姿勢を示しています。 RWMutex
の構造体定義がtype RWMutex struct { Mutex; }
からtype RWMutex struct { m Mutex; }
に変更されたことで、RWMutex
が内部にMutex
を「埋め込む」のではなく、「持つ」という関係が明確になり、コードの意図がより分かりやすくなりました。
このコミットは、Go言語の同期プリミティブの利用に関する初期のベストプラクティスと、当時の実装の限界を明確にする上で非常に重要な役割を果たしています。
関連リンク
- Go言語の
sync
パッケージの公式ドキュメンテーション: https://pkg.go.dev/sync - Go言語の並行処理に関する公式ブログ記事(初期のものを含む): https://go.dev/blog/concurrency-is-not-parallelism
参考にした情報源リンク
- Go言語のソースコード(
src/sync/mutex.go
): https://github.com/golang/go/blob/master/src/sync/mutex.go - Go言語の初期の設計に関する議論(GoのメーリングリストやIssueトラッカーなど、当時の情報源)
- Go言語の
sync
パッケージに関する一般的な解説記事やチュートリアル - アトミック操作とセマフォに関するコンピュータサイエンスの基礎知識
- Go言語の
BUG
コメントに関する慣習についての情報