[インデックス 14061] ファイルの概要
このコミットは、Go言語の標準ライブラリであるsync
パッケージ内の複数のファイルにデータ競合検出のための変更を加えるものです。具体的には、以下のファイルが影響を受けています。
src/pkg/go/build/deps_test.go
: ビルド依存関係のテストファイル。sync
パッケージの依存関係にunsafe
が追加されています。src/pkg/sync/cond.go
:sync.Cond
(条件変数)の実装ファイル。src/pkg/sync/mutex.go
:sync.Mutex
(ミューテックス)の実装ファイル。src/pkg/sync/race.go
: 新規追加されたデータ競合検出関連の関数を定義するファイル。race
ビルドタグが有効な場合にコンパイルされます。src/pkg/sync/race0.go
: 新規追加されたデータ競合検出関連のスタブ関数を定義するファイル。race
ビルドタグが無効な場合にコンパイルされます。src/pkg/sync/rwmutex.go
:sync.RWMutex
(読み書きミューテックス)の実装ファイル。src/pkg/sync/waitgroup.go
:sync.WaitGroup
の実装ファイル。
これらの変更は、Goランタイムにデータ競合検出機能を追加する大規模な変更の一部です。
コミット
commit 53390c8fc7b27bf1a14c709feae802c410ea2ae2
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Sun Oct 7 22:07:03 2012 +0400
race: sync changes
This is a part of a bigger change that adds data race detection feature:
https://golang.org/cl/6456044
R=rsc, minux.ma
CC=gobot, golang-dev
https://golang.org/cl/6529053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/53390c8fc7b27bf1a14c709feae802c410ea2ae2
元コミット内容
race: sync changes
これは、データ競合検出機能を追加する大規模な変更の一部です。
https://golang.org/cl/6456044
変更の背景
このコミットは、Go言語にデータ競合検出機能(Go Race Detector)を導入する一連の変更の一部です。データ競合は並行プログラミングにおける深刻なバグの一種であり、複数のゴルーチンが同時に同じメモリ位置にアクセスし、少なくとも1つのアクセスが書き込みであり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に発生します。データ競合は予測不能な動作、プログラムのクラッシュ、データの破損などを引き起こす可能性があります。
Go Race Detectorは、実行時にデータ競合を検出するためのツールであり、開発者が並行プログラムのバグを特定し、修正するのに役立ちます。このコミットは、sync
パッケージ内の同期プリミティブ(ミューテックス、条件変数、読み書きミューテックス、WaitGroup)が、データ競合検出器と適切に連携できるようにするための変更を導入しています。具体的には、これらのプリミティブがロックの取得/解放、待機/通知などの操作を行う際に、競合検出器にそのイベントを通知するメカニズムが追加されています。
コミットメッセージに記載されているhttps://golang.org/cl/6456044
は、このデータ競合検出機能の導入に関する主要な変更リスト(Change List)の一つであり、システムコールに関連する競合状態に対処することを目的としていました。
前提知識の解説
データ競合 (Data Race)
データ競合は、並行プログラムにおけるバグの一種で、以下の3つの条件がすべて満たされたときに発生します。
- 複数のゴルーチンが同じメモリ位置にアクセスする: 2つ以上のゴルーチンが同じ変数やデータ構造を操作しようとします。
- 少なくとも1つのアクセスが書き込みである: 読み取り専用のアクセスだけでは競合は発生しません。少なくとも1つのゴルーチンがデータを変更しようとします。
- アクセスが同期メカニズムによって保護されていない: ミューテックス、セマフォ、チャネルなどの同期プリミティブが適切に使用されていないため、アクセス順序が保証されません。
データ競合が発生すると、プログラムの実行結果がアクセス順序に依存するようになり、非決定的な動作を引き起こします。これはデバッグが非常に困難な種類のバグです。
Go Race Detector
Go言語には、ビルド時に-race
フラグを付けることで有効になる組み込みのデータ競合検出器があります。この検出器は、プログラムの実行中にメモリアクセスを監視し、データ競合のパターンを検出すると警告を出力します。検出器は、メモリの読み書き、ゴルーチンの開始/終了、同期プリミティブの操作などのイベントを追跡することで機能します。
sync
パッケージの同期プリミティブ
Goのsync
パッケージは、並行処理を安全に行うための基本的な同期プリミティブを提供します。
sync.Mutex
: 排他ロック。共有リソースへのアクセスを一度に1つのゴルーチンに制限します。Lock()
でロックを取得し、Unlock()
でロックを解放します。sync.Cond
: 条件変数。ミューテックスと組み合わせて使用され、特定の条件が満たされるまでゴルーチンを待機させたり、条件が満たされたときに待機中のゴルーチンを通知したりするために使用されます。Wait()
で待機し、Signal()
またはBroadcast()
で通知します。sync.RWMutex
: 読み書きミューテックス。複数の読み取り操作は同時に許可しますが、書き込み操作は排他的に行われます。RLock()
/RUnlock()
で読み取りロック、Lock()
/Unlock()
で書き込みロックを取得/解放します。sync.WaitGroup
: 複数のゴルーチンの完了を待機するために使用されます。Add()
でカウンタを増やし、Done()
で減らし、Wait()
でカウンタがゼロになるまでブロックします。
sync/atomic
パッケージ
sync/atomic
パッケージは、アトミック(不可分)な操作を提供します。これにより、複数のゴルーチンが同時にアクセスしても、操作が中断されることなく完了することが保証されます。例えば、atomic.AddInt32
は、32ビット整数にアトミックに値を加算します。
unsafe.Pointer
unsafe.Pointer
は、任意の型のポインタを保持できる特殊なポインタ型です。Goの型システムをバイパスして、任意のメモリ位置にアクセスすることを可能にします。データ競合検出器は、メモリアドレスを追跡する必要があるため、unsafe.Pointer
を使用して同期プリミティブの内部状態や関連するメモリ領域のアドレスを検出器に渡します。
ビルドタグ (+build
)
Goのソースファイルには、+build
ディレクティブを使用してビルドタグを指定できます。これにより、特定の環境や条件でのみファイルをコンパイルするように制御できます。このコミットでは、sync/race.go
には+build race
が、sync/race0.go
には+build !race
が指定されており、-race
フラグが有効な場合にsync/race.go
が、無効な場合にsync/race0.go
がコンパイルされるようになっています。
技術的詳細
このコミットの主要な目的は、Goのデータ競合検出器がsync
パッケージの同期プリミティブの操作を監視し、データ競合を正確に報告できるようにすることです。これを実現するために、以下の技術的アプローチが取られています。
-
raceenabled
フラグの導入:sync/race.go
とsync/race0.go
という2つの新しいファイルが導入されました。sync/race.go
は+build race
タグを持ち、raceenabled
定数をtrue
に設定します。また、runtime
パッケージのRaceAcquire
,RaceRelease
,RaceReleaseMerge
,RaceDisable
,RaceEnable
といった関数をラップする関数(raceAcquire
,raceRelease
など)を定義します。これらの関数は、データ競合検出器に同期イベントを通知するためのエントリポイントとなります。sync/race0.go
は+build !race
タグを持ち、raceenabled
定数をfalse
に設定します。また、sync/race.go
で定義された関数と同じ名前のスタブ関数を定義します。これにより、-race
フラグなしでビルドされた場合、競合検出関連のコードはコンパイルされず、パフォーマンスへの影響を最小限に抑えます。
-
同期プリミティブへの競合検出フックの追加:
sync.Cond
,sync.Mutex
,sync.RWMutex
,sync.WaitGroup
の各メソッド(Lock
,Unlock
,Wait
,Signal
,Broadcast
,Add
,Done
など)に、raceenabled
がtrue
の場合にのみ実行される条件付きのコードブロックが追加されました。- これらのコードブロック内では、
raceAcquire
,raceRelease
,raceReleaseMerge
,raceDisable
,raceEnable
といった関数が呼び出されます。 raceAcquire(unsafe.Pointer(m))
のような呼び出しは、ミューテックスm
がロックされた(取得された)ことを競合検出器に通知します。unsafe.Pointer
を使用することで、ミューテックスオブジェクト自体のメモリアドレスを検出器に渡します。raceRelease(unsafe.Pointer(m))
は、ミューテックスm
が解放されたことを通知します。raceReleaseMerge
は、複数の競合領域がマージされるような特定の同期パターン(例:RWMutex
のRUnlock
)で使用されます。raceDisable()
とraceEnable()
は、一時的に競合検出を無効化/有効化するために使用されます。これは、同期プリミティブの内部処理中に、検出器が誤って競合を報告しないようにするために重要です。例えば、Cond.Wait()
やRWMutex.RLock()
/Lock()
の内部で、ロックの取得/解放の前後で競合検出を一時的に無効化し、内部のセマフォ操作などが競合として報告されないようにしています。
-
go/build
パッケージの依存関係の更新:src/pkg/go/build/deps_test.go
において、sync
パッケージの依存関係にunsafe
が追加されました。これは、sync
パッケージがunsafe.Pointer
を使用するようになったため、ビルドシステムがその依存関係を正しく認識するために必要です。
これらの変更により、Goの同期プリミティブは、データ競合検出器と協調して動作し、並行プログラムにおける潜在的なデータ競合をより効果的に特定できるようになります。
コアとなるコードの変更箇所
src/pkg/go/build/deps_test.go
--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -30,7 +30,7 @@ var pkgDeps = map[string][]string{
"errors": {},
"io": {"errors", "sync"},
"runtime": {"unsafe"},
- "sync": {"sync/atomic"},
+ "sync": {"sync/atomic", "unsafe"},
"sync/atomic": {"unsafe"},
"unsafe": {},
src/pkg/sync/cond.go
Wait
, Signal
, Broadcast
メソッドにraceenabled
チェックとraceDisable
/raceEnable
呼び出しが追加されています。
--- a/src/pkg/sync/cond.go
+++ b/src/pkg/sync/cond.go
@@ -56,6 +56,9 @@ func NewCond(l Locker) *Cond {
// c.L.Unlock()\n //\n func (c *Cond) Wait() {\n+\tif raceenabled {\n+\t\traceDisable()\n+\t}\n \tc.m.Lock()\n \tif c.newSema == nil {\n \t\tc.newSema = new(uint32)\n@@ -63,6 +66,9 @@ func (c *Cond) Wait() {\n \ts := c.newSema\n \tc.newWaiters++\n \tc.m.Unlock()\n+\tif raceenabled {\n+\t\traceEnable()\n+\t}\n \tc.L.Unlock()\n \truntime_Semacquire(s)\n \tc.L.Lock()\
@@ -73,6 +79,9 @@ func (c *Cond) Wait() {\n // It is allowed but not required for the caller to hold c.L\n // during the call.\n func (c *Cond) Signal() {\n+\tif raceenabled {\n+\t\traceDisable()\n+\t}\n \tc.m.Lock()\n \tif c.oldWaiters == 0 && c.newWaiters > 0 {\n \t\t// Retire old generation; rename new to old.\n@@ -86,6 +95,9 @@ func (c *Cond) Signal() {\n \t\truntime_Semrelease(c.oldSema)\n \t}\n \tc.m.Unlock()\n+\tif raceenabled {\n+\t\traceEnable()\n+\t}\n }\n \n // Broadcast wakes all goroutines waiting on c.\n@@ -93,6 +105,9 @@ func (c *Cond) Signal() {\n // It is allowed but not required for the caller to hold c.L\n // during the call.\n func (c *Cond) Broadcast() {\n+\tif raceenabled {\n+\t\traceDisable()\n+\t}\n \tc.m.Lock()\n \t// Wake both generations.\n \tif c.oldWaiters > 0 {\n@@ -109,4 +124,7 @@ func (c *Cond) Broadcast() {\n \t\tc.newSema = nil\n \t}\n \tc.m.Unlock()\n+\tif raceenabled {\n+\t\traceEnable()\n+\t}\n }\n```
### `src/pkg/sync/mutex.go`
`Lock`と`Unlock`メソッドに`raceenabled`チェックと`raceAcquire`/`raceRelease`呼び出しが追加されています。`unsafe`パッケージがインポートされています。
```diff
--- a/src/pkg/sync/mutex.go
+++ b/src/pkg/sync/mutex.go
@@ -10,7 +10,10 @@
// Values containing the types defined in this package should not be copied.\n package sync\n \n-import "sync/atomic"\n+import (\n+\t"sync/atomic"\n+\t"unsafe"\n+)\n \n // A Mutex is a mutual exclusion lock.\n // Mutexes can be created as part of other structures;\n@@ -38,6 +41,9 @@ const (\n func (m *Mutex) Lock() {\n \t// Fast path: grab unlocked mutex.\n \tif atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {\n+\t\tif raceenabled {\n+\t\t\traceAcquire(unsafe.Pointer(m))\n+\t\t}\n \t\treturn\n \t}\n \n@@ -61,6 +67,10 @@ func (m *Mutex) Lock() {\n \t\t\tawoke = true\n \t\t}\n \t}\n+\n+\tif raceenabled {\n+\t\t\traceAcquire(unsafe.Pointer(m))\n+\t}\n }\n \n // Unlock unlocks m.\n@@ -70,6 +80,10 @@ func (m *Mutex) Lock() {\n // It is allowed for one goroutine to lock a Mutex and then\n // arrange for another goroutine to unlock it.\n func (m *Mutex) Unlock() {\n+\tif raceenabled {\n+\t\t\traceRelease(unsafe.Pointer(m))\n+\t}\n+\n \t// Fast path: drop lock bit.\n \tnew := atomic.AddInt32(&m.state, -mutexLocked)\n \tif (new+mutexLocked)&mutexLocked == 0 {\
src/pkg/sync/race.go
(新規ファイル)
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build race
package sync
import (
"runtime"
"unsafe"
)
const raceenabled = true
func raceAcquire(addr unsafe.Pointer) {
runtime.RaceAcquire(addr)
}
func raceRelease(addr unsafe.Pointer) {
runtime.RaceRelease(addr)
}
func raceReleaseMerge(addr unsafe.Pointer) {
runtime.RaceReleaseMerge(addr)
}
func raceDisable() {
runtime.RaceDisable()
}
func raceEnable() {
runtime.RaceEnable()
}
src/pkg/sync/race0.go
(新規ファイル)
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !race
package sync
import (
"unsafe"
)
const raceenabled = false
func raceAcquire(addr unsafe.Pointer) {
}
func raceRelease(addr unsafe.Pointer) {
}
func raceReleaseMerge(addr unsafe.Pointer) {
}
func raceDisable() {
}
func raceEnable() {
}
src/pkg/sync/rwmutex.go
RLock
, RUnlock
, Lock
, Unlock
メソッドにraceenabled
チェックとraceAcquire
/raceRelease
/raceReleaseMerge
/raceDisable
/raceEnable
呼び出しが追加されています。unsafe
パッケージがインポートされています。
--- a/src/pkg/sync/rwmutex.go
+++ b/src/pkg/sync/rwmutex.go
@@ -4,7 +4,10 @@
package sync
-import "sync/atomic"\n+import (\n+\t"sync/atomic"\n+\t"unsafe"\n+)\n \n // An RWMutex is a reader/writer mutual exclusion lock.\n // The lock can be held by an arbitrary number of readers\n@@ -24,10 +27,17 @@ const rwmutexMaxReaders = 1 << 30\n \n // RLock locks rw for reading.\n func (rw *RWMutex) RLock() {\n+\tif raceenabled {\n+\t\traceDisable()\n+\t}\n \tif atomic.AddInt32(&rw.readerCount, 1) < 0 {\n \t\t// A writer is pending, wait for it.\n \t\truntime_Semacquire(&rw.readerSem)\n \t}\n+\tif raceenabled {\n+\t\traceEnable()\n+\t\traceAcquire(unsafe.Pointer(&rw.readerSem))\n+\t}\n }\n \n // RUnlock undoes a single RLock call;\n@@ -35,6 +45,10 @@ func (rw *RWMutex) RLock() {\n // It is a run-time error if rw is not locked for reading\n // on entry to RUnlock.\n func (rw *RWMutex) RUnlock() {\n+\tif raceenabled {\n+\t\traceReleaseMerge(unsafe.Pointer(&rw.writerSem))\n+\t\traceDisable()\n+\t}\n \tif atomic.AddInt32(&rw.readerCount, -1) < 0 {\n \t\t// A writer is pending.\n \t\tif atomic.AddInt32(&rw.readerWait, -1) == 0 {\n@@ -42,6 +56,9 @@ func (rw *RWMutex) RUnlock() {\n \t\t\truntime_Semrelease(&rw.writerSem)\n \t\t}\n \t}\n+\tif raceenabled {\n+\t\traceEnable()\n+\t}\n }\n \n // Lock locks rw for writing.\n@@ -51,6 +68,9 @@ func (rw *RWMutex) RUnlock() {\n // a blocked Lock call excludes new readers from acquiring\n // the lock.\n func (rw *RWMutex) Lock() {\n+\tif raceenabled {\n+\t\traceDisable()\n+\t}\n \t// First, resolve competition with other writers.\n \trw.w.Lock()\n \t// Announce to readers there is a pending writer.\n@@ -59,6 +79,11 @@ func (rw *RWMutex) Lock() {\n \tif r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {\n \t\truntime_Semacquire(&rw.writerSem)\n \t}\n+\tif raceenabled {\n+\t\traceEnable()\n+\t\traceAcquire(unsafe.Pointer(&rw.readerSem))\n+\t\traceAcquire(unsafe.Pointer(&rw.writerSem))\n+\t}\n }\n \n // Unlock unlocks rw for writing. It is a run-time error if rw is\n@@ -68,6 +93,12 @@ func (rw *RWMutex) Lock() {\n // goroutine. One goroutine may RLock (Lock) an RWMutex and then\n // arrange for another goroutine to RUnlock (Unlock) it.\n func (rw *RWMutex) Unlock() {\n+\tif raceenabled {\n+\t\traceRelease(unsafe.Pointer(&rw.readerSem))\n+\t\traceRelease(unsafe.Pointer(&rw.writerSem))\n+\t\traceDisable()\n+\t}\n+\n \t// Announce to readers there is no active writer.\n \tr := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)\n \t// Unblock blocked readers, if any.\n@@ -76,6 +107,9 @@ func (rw *RWMutex) Unlock() {\n \t}\n \t// Allow other writers to proceed.\n \trw.w.Unlock()\n+\tif raceenabled {\n+\t\traceEnable()\n+\t}\n }\n \n // RLocker returns a Locker interface that implements\n```
### `src/pkg/sync/waitgroup.go`
`Add`と`Wait`メソッドに`raceenabled`チェックと`raceAcquire`/`raceReleaseMerge`/`raceDisable`/`raceEnable`呼び出しが追加されています。`unsafe`パッケージがインポートされています。
```diff
--- a/src/pkg/sync/waitgroup.go
+++ b/src/pkg/sync/waitgroup.go
@@ -4,7 +4,10 @@
package sync
-import "sync/atomic"\n+import (\n+\t"sync/atomic"\n+\t"unsafe"\n+)\n \n // A WaitGroup waits for a collection of goroutines to finish.\n // The main goroutine calls Add to set the number of\n@@ -34,6 +37,11 @@ type WaitGroup struct {\n // If the counter becomes zero, all goroutines blocked on Wait() are released.\n // If the counter goes negative, Add panics.\n func (wg *WaitGroup) Add(delta int) {\n+\tif raceenabled {\n+\t\traceReleaseMerge(unsafe.Pointer(wg))\n+\t\traceDisable()\n+\t\tdefer raceEnable()\n+\t}\n \tv := atomic.AddInt32(&wg.counter, int32(delta))\n \tif v < 0 {\n \t\tpanic(\"sync: negative WaitGroup counter\")\n@@ -57,7 +65,14 @@ func (wg *WaitGroup) Done() {\n \n // Wait blocks until the WaitGroup counter is zero.\n func (wg *WaitGroup) Wait() {\n+\tif raceenabled {\n+\t\traceDisable()\n+\t}\n \tif atomic.LoadInt32(&wg.counter) == 0 {\n+\t\tif raceenabled {\n+\t\t\t\traceEnable()\n+\t\t\t\traceAcquire(unsafe.Pointer(wg))\n+\t\t}\n \t\treturn\n \t}\n \twg.m.Lock()\n@@ -68,7 +83,15 @@ func (wg *WaitGroup) Wait() {\n \t// to avoid missing an Add.\n \tif atomic.LoadInt32(&wg.counter) == 0 {\n \t\tatomic.AddInt32(&wg.waiters, -1)\n+\t\tif raceenabled {\n+\t\t\t\traceEnable()\n+\t\t\t\traceAcquire(unsafe.Pointer(wg))\n+\t\t\t\traceDisable()\n+\t\t}\n \t\twg.m.Unlock()\n+\t\tif raceenabled {\n+\t\t\t\traceEnable()\n+\t\t}\n \t\treturn\n \t}\n \tif wg.sema == nil {\n@@ -77,4 +100,8 @@ func (wg *WaitGroup) Wait() {\n \ts := wg.sema\n \twg.m.Unlock()\n \truntime_Semacquire(s)\n+\tif raceenabled {\n+\t\traceEnable()\n+\t\traceAcquire(unsafe.Pointer(wg))\n+\t}\n }\n```
## コアとなるコードの解説
### `src/pkg/go/build/deps_test.go`の変更
`sync`パッケージが`unsafe`パッケージに依存するようになったため、ビルドシステムの依存関係定義を更新しています。これは、`sync`パッケージ内のコードが`unsafe.Pointer`を使用するようになったことによる必然的な変更です。
### `src/pkg/sync/race.go`と`src/pkg/sync/race0.go`の追加
これらのファイルは、Goのビルドタグ機能を利用して、データ競合検出が有効かどうかに応じて異なる実装を提供します。
* **`race.go` (`-race`ビルド時)**:
* `const raceenabled = true`: 競合検出が有効であることを示すフラグ。
* `raceAcquire(addr unsafe.Pointer)`: `runtime.RaceAcquire`を呼び出します。これは、指定されたメモリアドレス`addr`がゴルーチンによって「取得」された(例: ロックが取得された)ことを競合検出器に通知します。検出器は、このアドレスに対するその後のアクセスを監視し、競合がないかチェックします。
* `raceRelease(addr unsafe.Pointer)`: `runtime.RaceRelease`を呼び出します。これは、指定されたメモリアドレス`addr`がゴルーチンによって「解放」された(例: ロックが解放された)ことを競合検出器に通知します。
* `raceReleaseMerge(addr unsafe.Pointer)`: `runtime.RaceReleaseMerge`を呼び出します。これは、複数の競合領域がマージされるような特定の同期パターン(例: `RWMutex`の`RUnlock`)で使用され、検出器にその情報を伝えます。
* `raceDisable()`: `runtime.RaceDisable()`を呼び出します。これは、一時的に競合検出を無効にします。同期プリミティブの内部処理中に、検出器が誤って競合を報告しないようにするために使用されます。
* `raceEnable()`: `runtime.RaceEnable()`を呼び出します。これは、無効化された競合検出を再度有効にします。
* **`race0.go` (通常ビルド時)**:
* `const raceenabled = false`: 競合検出が無効であることを示すフラグ。
* すべての`race*`関数は空のスタブとして実装されています。これにより、`-race`フラグなしでビルドされた場合、これらの関数呼び出しはコンパイル時に最適化され、実行時のオーバーヘッドがなくなります。
### `sync.Cond`の変更
`Wait()`, `Signal()`, `Broadcast()`メソッドの冒頭と末尾に`raceDisable()`と`raceEnable()`の呼び出しが追加されています。
`Cond.Wait()`は、ゴルーチンが条件変数で待機する際に、内部的にセマフォ操作を行います。このセマフォ操作自体は同期メカニズムの一部ですが、競合検出器がその内部実装を誤ってデータ競合と解釈する可能性があります。そのため、`Wait()`の開始時に競合検出を一時的に無効化し、セマフォ操作が完了した後に再度有効化することで、誤検出を防ぎます。`Signal()`や`Broadcast()`も同様に、内部のセマフォ解放操作の前後で競合検出を一時的に無効化しています。
### `sync.Mutex`の変更
`Lock()`メソッドでは、ミューテックスが正常に取得された後(高速パスと低速パスの両方で)、`raceAcquire(unsafe.Pointer(m))`が呼び出されます。これにより、ミューテックス`m`がロックされたことを競合検出器に通知します。
`Unlock()`メソッドでは、ミューテックスが解放される前に`raceRelease(unsafe.Pointer(m))`が呼び出されます。これにより、ミューテックス`m`がアンロックされたことを競合検出器に通知します。
これらの通知により、競合検出器はミューテックスによって保護されているメモリ領域へのアクセスを正しく追跡し、保護されていないアクセスを検出できます。
### `sync.RWMutex`の変更
`RWMutex`は読み取りロックと書き込みロックの両方を持つため、より複雑な競合検出フックが必要です。
* **`RLock()`**: 読み取りロックが取得される前後に`raceDisable()`と`raceEnable()`が呼び出され、`raceAcquire(unsafe.Pointer(&rw.readerSem))`が呼び出されます。これは、読み取りセマフォが取得されたことを検出器に通知します。
* **`RUnlock()`**: 読み取りロックが解放される前後に`raceDisable()`と`raceEnable()`が呼び出され、`raceReleaseMerge(unsafe.Pointer(&rw.writerSem))`が呼び出されます。`raceReleaseMerge`は、読み取りロックの解放が書き込みロックの待機状態に影響を与える可能性があるため、検出器にその関係を伝えます。
* **`Lock()`**: 書き込みロックが取得される前後に`raceDisable()`と`raceEnable()`が呼び出され、`raceAcquire(unsafe.Pointer(&rw.readerSem))`と`raceAcquire(unsafe.Pointer(&rw.writerSem))`が呼び出されます。これは、読み取りセマフォと書き込みセマフォの両方が取得されたことを検出器に通知します。
* **`Unlock()`**: 書き込みロックが解放される前後に`raceDisable()`と`raceEnable()`が呼び出され、`raceRelease(unsafe.Pointer(&rw.readerSem))`と`raceRelease(unsafe.Pointer(&rw.writerSem))`が呼び出されます。これは、両方のセマフォが解放されたことを検出器に通知します。
これらのフックにより、`RWMutex`の複雑なロック状態遷移が競合検出器に正確に伝達されます。
### `sync.WaitGroup`の変更
* **`Add(delta int)`**: `WaitGroup`のカウンタが変更される前に`raceReleaseMerge(unsafe.Pointer(wg))`と`raceDisable()`が呼び出され、関数終了時に`defer raceEnable()`で競合検出が再度有効化されます。`raceReleaseMerge`は、`WaitGroup`のカウンタ変更が待機中のゴルーチンに影響を与える可能性があるため、検出器にその関係を伝えます。
* **`Wait()`**: `WaitGroup`がゼロになるまで待機する際に、`raceDisable()`と`raceEnable()`が適切に呼び出されます。また、`WaitGroup`がゼロになったときに`raceAcquire(unsafe.Pointer(wg))`が呼び出され、`WaitGroup`が「取得」された(待機が完了した)ことを検出器に通知します。
これらの変更により、`WaitGroup`の`Add`/`Done`/`Wait`操作が競合検出器によって正しく監視され、`WaitGroup`に関連するデータ競合が検出できるようになります。
## 関連リンク
* GitHubコミットページ: [https://github.com/golang/go/commit/53390c8fc7b27bf1a14c709feae802c410ea2ae2](https://github.com/golang/go/commit/53390c8fc7b27bf1a14c709feae802c410ea2ae2)
* 関連するGo Change List: [https://golang.org/cl/6456044](https://golang.org/cl/6456044)
* Go Race Detectorの公式ドキュメント (Go 1.1以降で利用可能): [https://go.dev/doc/articles/race_detector](https://go.dev/doc/articles/race_detector)
## 参考にした情報源リンク
* appspot.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHLWKbSe--g67qi8mogu5ZF0XpbX4Oya4ZVyagX_8-Lp_5IPLEjLPS0AT1otSKxE38eoMrjPKXJtTdv-Uza2OuaOdKwN21JHSCzqmH1XtUK7IinKBh-GJE6TzK5qZXLlyBu)
* Go Race Detectorの概念と使用方法に関する一般的な情報源 (例: Go公式ドキュメント、ブログ記事など)