Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 14062] ファイルの概要

このコミットは、Go言語の標準ライブラリsrc/pkg/sync/atomicパッケージに対する変更であり、Goのデータ競合検出機能(ThreadSanitizer)の統合の一環として行われました。具体的には、sync/atomicパッケージ内のアセンブリファイルおよびドキュメントファイルに+build !raceビルドタグを追加し、データ競合検出が有効な場合にアトミック操作をフックするための新しいrace.goファイルが導入されています。これにより、データ競合検出ツールがアトミック操作を監視し、潜在的な競合を報告できるようになります。

コミット

commit 59b87453285d952c5b1bc705ca5ffc87e7f15f61
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Sun Oct 7 22:07:32 2012 +0400

    race: sync/atomic changes
    This is a part of a bigger change that adds data race detection feature:
    https://golang.org/cl/6456044
    
    R=rsc, remyoudompheng
    CC=gobot, golang-dev
    https://golang.org/cl/6536059
---
 src/pkg/sync/atomic/asm_386.s       |   2 +
 src/pkg/sync/atomic/asm_amd64.s     |   2 +
 src/pkg/sync/atomic/asm_arm.s       |   2 +
 src/pkg/sync/atomic/asm_linux_arm.s |   2 +
 src/pkg/sync/atomic/doc.go          |   2 +
 src/pkg/sync/atomic/race.go         | 191 ++++++++++++++++++++++++++++++++++++
 6 files changed, 201 insertions(+)

diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s
index 70ace827a6..7a98a61d80 100644
--- a/src/pkg/sync/atomic/asm_386.s
+++ b/src/pkg/sync/atomic/asm_386.s
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build !race
+
 TEXT ·CompareAndSwapInt32(SB),7,$0
 	JMP	·CompareAndSwapUint32(SB)
 
diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s
index 5c0785d2df..58bda9e4f5 100644
--- a/src/pkg/sync/atomic/asm_amd64.s
+++ b/src/pkg/sync/atomic/asm_amd64.s
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build !race
+
 TEXT ·CompareAndSwapInt32(SB),7,$0
 	JMP\t·CompareAndSwapUint32(SB)
 
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index 63a6b7dba6..4faf5b5d97 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.\n
 
+// +build !race
+
 // ARM atomic operations, for use by asm_$(GOOS)_arm.s.
 
 TEXT ·armCompareAndSwapUint32(SB),7,$0
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index ba07d33803..098acf35bd 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.\n
 
+// +build !race
+
 // Linux/ARM atomic operations.
 
 // Because there is so much variation in ARM devices,\ndiff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go
index efe60f8522..33e1bcf0ae 100644
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.\n
 
+// +build !race
+
 // Package atomic provides low-level atomic memory primitives
 // useful for implementing synchronization algorithms.
 //
diff --git a/src/pkg/sync/atomic/race.go b/src/pkg/sync/atomic/race.go
new file mode 100644
index 0000000000..c3627654de
--- /dev/null
+++ b/src/pkg/sync/atomic/race.go
@@ -0,0 +1,191 @@
+// Copyright 2011 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 atomic
+
+import (
+\t"runtime"
+\t"unsafe"
+)
+
+var mtx uint32 = 1 // same for all
+
+func CompareAndSwapInt32(val *int32, old, new int32) bool {
+\treturn CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))\n}
+\n+func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {\n+\tswapped = false
+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\tif *val == old {\n+\t\t*val = new
+\t\tswapped = true
+\t\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\t}
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func CompareAndSwapInt64(val *int64, old, new int64) bool {\n+\treturn CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))\n}\n+\n+func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {\n+\tswapped = false
+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\tif *val == old {\n+\t\t*val = new
+\t\tswapped = true
+\t\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\t}
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {\n+\tswapped = false
+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\tif *val == old {\n+\t\t*val = new
+\t\tswapped = true
+\t\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\t}
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {\n+\tswapped = false
+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\tif *val == old {\n+\t\t*val = new
+\t\tswapped = true
+\t\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\t}
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func AddInt32(val *int32, delta int32) int32 {\n+\treturn int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))\n}\n+\n+func AddUint32(val *uint32, delta uint32) (new uint32) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\t*val = *val + delta
+\tnew = *val
+\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\truntime.RaceSemrelease(&mtx)\n+\n+\treturn
+}\n+\n+func AddInt64(val *int64, delta int64) int64 {\n+\treturn int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))\n}\n+\n+func AddUint64(val *uint64, delta uint64) (new uint64) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\t*val = *val + delta
+\tnew = *val
+\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\truntime.RaceSemrelease(&mtx)\n+\n+\treturn
+}\n+\n+func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(val))\n+\t*val = *val + delta
+\tnew = *val
+\truntime.RaceReleaseMerge(unsafe.Pointer(val))\n+\truntime.RaceSemrelease(&mtx)\n+\n+\treturn
+}\n+\n+func LoadInt32(addr *int32) int32 {\n+\treturn int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))\n}\n+\n+func LoadUint32(addr *uint32) (val uint32) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(addr))\n+\tval = *addr
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func LoadInt64(addr *int64) int64 {\n+\treturn int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))\n}\n+\n+func LoadUint64(addr *uint64) (val uint64) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(addr))\n+\tval = *addr
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(addr))\n+\tval = *addr
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func LoadUintptr(addr *uintptr) (val uintptr) {\n+\truntime.RaceSemacquire(&mtx)\n+\truntime.RaceAcquire(unsafe.Pointer(addr))\n+\tval = *addr
+\truntime.RaceSemrelease(&mtx)\n+\treturn
+}\n+\n+func StoreInt32(addr *int32, val int32) {\n+\tStoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))\n}\n+\n+func StoreUint32(addr *uint32, val uint32) {\n+\truntime.RaceSemacquire(&mtx)\n+\t*addr = val
+\truntime.RaceRelease(unsafe.Pointer(addr))\n+\truntime.RaceSemrelease(&mtx)\n+}\n+\n+func StoreInt64(addr *int64, val int64) {\n+\tStoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))\n}\n+\n+func StoreUint64(addr *uint64, val uint64) {\n+\truntime.RaceSemacquire(&mtx)\n+\t*addr = val
+\truntime.RaceRelease(unsafe.Pointer(addr))\n+\truntime.RaceSemrelease(&mtx)\n+}\n+\n+func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {\n+\truntime.RaceSemacquire(&mtx)\n+\t*addr = val
+\truntime.RaceRelease(unsafe.Pointer(addr))\n+\truntime.RaceSemrelease(&mtx)\n+}\n+\n+func StoreUintptr(addr *uintptr, val uintptr) {\n+\truntime.RaceSemacquire(&mtx)\n+\t*addr = val
+\truntime.RaceRelease(unsafe.Pointer(addr))\n+\truntime.RaceSemrelease(&mtx)\n+}\n```

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/59b87453285d952c5b1bc705ca5ffc87e7f15f61](https://github.com/golang/go/commit/59b87453285d952c5b1bc705ca5ffc87e7f15f61)

## 元コミット内容

このコミットは、Go言語の`sync/atomic`パッケージに対する変更であり、Goのデータ競合検出機能(ThreadSanitizer)の統合の一部です。具体的には、`sync/atomic`パッケージ内の既存のアセンブリファイル(`asm_386.s`, `asm_amd64.s`, `asm_arm.s`, `asm_linux_arm.s`)とドキュメントファイル(`doc.go`)に`+build !race`というビルドタグが追加されています。これにより、これらのファイルはデータ競合検出が有効になっていない場合にのみコンパイルされるようになります。

さらに重要な変更として、`src/pkg/sync/atomic/race.go`という新しいファイルが追加されました。このファイルは、`+build race`タグを持つため、データ競合検出が有効な場合にのみコンパイルされます。この`race.go`ファイルには、`sync/atomic`パッケージが提供するアトミック操作(`CompareAndSwap`、`Add`、`Load`、`Store`など)のラッパー関数が実装されており、これらのラッパー関数内で`runtime`パッケージの`RaceSemacquire`、`RaceAcquire`、`RaceReleaseMerge`、`RaceRelease`といった関数が呼び出されています。これらの関数は、ThreadSanitizerがアトミック操作を監視し、データ競合を検出するために使用されます。

このコミットは、データ競合検出機能のより大きな変更セット(`https://golang.org/cl/6456044`)の一部であり、Goプログラムの並行処理におけるバグの特定とデバッグを支援することを目的としています。

## 変更の背景

この変更の主な背景は、Go言語に堅牢なデータ競合検出機能(ThreadSanitizerの統合)を導入することです。並行プログラミングでは、複数のゴルーチン(Goの軽量スレッド)が共有データに同時にアクセスし、少なくとも1つのアクセスが書き込みである場合に、データ競合が発生する可能性があります。データ競合は、プログラムの予測不可能な動作、クラッシュ、または誤った結果を引き起こす可能性のある深刻なバグです。

Goの設計思想は並行処理を重視しているため、データ競合を早期に検出し、開発者が修正できるようにすることは非常に重要です。ThreadSanitizerは、このようなデータ競合をランタイムで検出するための強力なツールです。このコミットは、`sync/atomic`パッケージが提供するアトミック操作が、データ競合検出の対象となるようにするための基盤を構築しています。アトミック操作は、それ自体は競合フリーですが、それらが使用される文脈によっては、より大きなデータ競合の一部となる可能性があります。したがって、ThreadSanitizerがアトミック操作を「認識」し、それらを適切にモデル化できるようにすることが不可欠でした。

`+build !race`タグの追加は、データ競合検出が有効でない通常のビルドでは、既存の最適化されたアセンブリ実装が使用され、パフォーマンスへの影響を最小限に抑えることを保証します。一方、`+build race`タグを持つ`race.go`ファイルの導入は、データ競合検出が有効なビルドにおいて、アトミック操作の実行時にThreadSanitizerのフックが呼び出されるようにするためのメカニズムを提供します。これにより、開発者は`go run -race`や`go build -race`などのコマンドを使用して、データ競合の有無を効率的にチェックできるようになります。

## 前提知識の解説

### 1. データ競合 (Data Race)

データ競合とは、複数の並行実行されるスレッド(Goではゴルーチン)が、同期メカニズムなしに同じメモリ位置にアクセスし、そのうち少なくとも1つのアクセスが書き込みである場合に発生するプログラミング上のバグです。データ競合が発生すると、プログラムの実行結果が非決定論的になり、デバッグが非常に困難になります。これは、実行のタイミングによって結果が変わるため、再現性が低いことが多いためです。

### 2. アトミック操作 (Atomic Operations)

アトミック操作は、不可分(atomic)な操作であり、その操作が完全に実行されるか、全く実行されないかのどちらかであることを保証します。つまり、他のスレッドから見ると、アトミック操作は途中で中断されることなく、単一のステップとして完了するように見えます。これにより、複数のスレッドが同時に同じメモリ位置にアクセスしても、データ競合が発生しないようにすることができます。Go言語の`sync/atomic`パッケージは、`CompareAndSwap`(CAS)、`Add`、`Load`、`Store`などのアトミック操作を提供します。これらは通常、CPUの特殊な命令を利用して非常に高速に実行されます。

### 3. Goのビルドタグ (Build Tags)

Goのビルドタグ(またはビルド制約)は、Goソースファイルの先頭に記述される特別なコメント行です。これらは、特定の条件が満たされた場合にのみファイルがコンパイルされるように制御するために使用されます。例えば、`// +build linux,amd64`はLinuxかつAMD64アーキテクチャの場合にのみファイルをコンパイルし、`// +build !race`は`race`タグが有効でない場合にのみファイルをコンパイルします。これにより、特定の環境やビルド設定に特化したコードを条件付きで含めることができます。

### 4. ThreadSanitizer (TSan)

ThreadSanitizer(TSan)は、データ競合を検出するためのランタイム分析ツールです。プログラムの実行中にメモリアクセスを監視し、データ競合のパターンを特定します。TSanは、コンパイラ(Clang/GCC)やランタイムライブラリと連携して動作し、インストルメンテーション(コードの計測)を通じてメモリアクセスイベントを記録します。Go言語では、`go run -race`や`go build -race`コマンドを使用することで、ThreadSanitizerを有効にしてプログラムをビルド・実行できます。TSanは、データ競合が発生したメモリ位置、アクセスタイプ(読み込み/書き込み)、および関連するスタックトレースなどの詳細な情報を提供し、デバッグを支援します。

### 5. `runtime`パッケージの`Race`関数群

Goの`runtime`パッケージには、ThreadSanitizerとの連携を可能にする低レベルの関数群が含まれています。これらは通常、Goのユーザーコードから直接呼び出されることはなく、コンパイラや標準ライブラリの内部で、データ競合検出を有効にするために使用されます。このコミットで登場する`RaceSemacquire`、`RaceAcquire`、`RaceReleaseMerge`、`RaceRelease`などは、ThreadSanitizerにメモリアクセスや同期イベントを通知するためのフックとして機能します。

*   `RaceSemacquire(addr *uint32)`: セマフォの取得をThreadSanitizerに通知します。
*   `RaceAcquire(addr unsafe.Pointer)`: 特定のメモリ領域に対する「取得」操作(例:ロックの取得)をThreadSanitizerに通知します。これにより、そのメモリ領域に対する以前の書き込みが、現在のゴルーチンから「見える」ようになります。
*   `RaceRelease(addr unsafe.Pointer)`: 特定のメモリ領域に対する「解放」操作(例:ロックの解放)をThreadSanitizerに通知します。これにより、現在のゴルーチンによる書き込みが、そのメモリ領域を「取得」する他のゴルーチンから「見える」ようになります。
*   `RaceReleaseMerge(addr unsafe.Pointer)`: `RaceRelease`と同様ですが、複数のメモリ領域の変更をマージするような操作(例:アトミックなCAS操作)に使用されます。

これらの関数は、ThreadSanitizerがメモリイベントの順序を追跡し、並行アクセスが適切に同期されているかを判断するために不可欠です。

## 技術的詳細

このコミットの技術的な核心は、GoのビルドシステムとThreadSanitizerの連携方法にあります。

### ビルドタグによる条件付きコンパイル

既存の`sync/atomic`パッケージのアセンブリファイル(`asm_386.s`など)と`doc.go`ファイルに`// +build !race`というビルドタグが追加されました。このタグは、Goコンパイラに対して「`race`ビルドタグが指定されていない場合にのみ、このファイルをコンパイルせよ」と指示します。

Goプログラムを通常通りビルドする場合(例: `go build`)、`race`タグは指定されないため、これらのファイルがコンパイルされ、CPUのネイティブなアトミック命令を利用した高速な実装が使用されます。

一方、データ競合検出を有効にしてビルドする場合(例: `go build -race`)、`race`タグが指定されるため、`+build !race`タグを持つファイルはコンパイルされません。代わりに、新しく追加された`src/pkg/sync/atomic/race.go`ファイルがコンパイルされます。このファイルは`// +build race`タグを持っているため、「`race`ビルドタグが指定された場合にのみ、このファイルをコンパイルせよ」と指示されます。

このメカニズムにより、データ競合検出が不要な通常のビルドではパフォーマンスオーバーヘッドを回避し、データ競合検出が必要な場合にのみ、ThreadSanitizerのフックを含む特別な実装が使用されるようになります。

### `race.go`の役割とThreadSanitizerフック

`race.go`ファイルは、`sync/atomic`パッケージが提供するすべてのアトミック操作(`CompareAndSwap`、`Add`、`Load`、`Store`など)に対するGo言語でのラッパー実装を含んでいます。これらのラッパー関数は、実際のアトミック操作を実行する前後に、`runtime`パッケージの`RaceSemacquire`、`RaceAcquire`、`RaceReleaseMerge`、`RaceRelease`といった関数を呼び出します。

これらの`runtime.Race*`関数は、ThreadSanitizerの内部APIへのフックとして機能します。

*   **`RaceSemacquire(&mtx)` / `RaceSemrelease(&mtx)`**: `mtx`というグローバルな`uint32`変数(このコミットでは`mtx uint32 = 1`と定義されている)をセマフォとして使用し、アトミック操作の開始と終了をThreadSanitizerに通知します。これは、アトミック操作自体が不可分であることを保証しつつ、その操作がメモリにアクセスする「イベント」としてThreadSanitizerに認識されるようにするための一般的な同期メカニズムのシミュレーションです。
*   **`RaceAcquire(unsafe.Pointer(val))`**: アトミック操作が対象とするメモリ位置(`val`または`addr`)に対する「取得」イベントをThreadSanitizerに通知します。これは、そのメモリ位置に対する読み込みまたは書き込みが行われることを示唆し、ThreadSanitizerがメモリのアクセス履歴を更新するために使用します。
*   **`RaceReleaseMerge(unsafe.Pointer(val))` / `RaceRelease(unsafe.Pointer(addr))`**: アトミック操作が対象とするメモリ位置に対する「解放」イベントをThreadSanitizerに通知します。`RaceReleaseMerge`は特に、`CompareAndSwap`のように条件付きで書き込みが行われる操作や、複数のメモリ状態がマージされるような操作に適しています。`RaceRelease`は単純な書き込み操作に使用されます。これらの呼び出しにより、ThreadSanitizerは、アトミック操作によってメモリの状態が変更されたことを認識し、他のゴルーチンからのアクセスとの競合をチェックします。

`unsafe.Pointer`の使用は、Goの型システムをバイパスして任意のメモリアドレスを操作するために必要です。これは低レベルの操作であり、ThreadSanitizerがメモリの生のアドレスを監視する必要があるためです。

この二重の実装(アセンブリによる高速な実装と、`race.go`によるTSanフック付きの実装)は、Goのデータ競合検出機能が、パフォーマンスを犠牲にすることなく、必要な場合にのみ有効になるようにするための巧妙な設計パターンを示しています。

## コアとなるコードの変更箇所

このコミットで変更されたファイルは以下の通りです。

1.  **`src/pkg/sync/atomic/asm_386.s`**
    *   ファイルの先頭に`// +build !race`ビルドタグが追加されました。
2.  **`src/pkg/sync/atomic/asm_amd64.s`**
    *   ファイルの先頭に`// +build !race`ビルドタグが追加されました。
3.  **`src/pkg/sync/atomic/asm_arm.s`**
    *   ファイルの先頭に`// +build !race`ビルドタグが追加されました。
4.  **`src/pkg/sync/atomic/asm_linux_arm.s`**
    *   ファイルの先頭に`// +build !race`ビルドタグが追加されました。
5.  **`src/pkg/sync/atomic/doc.go`**
    *   ファイルの先頭に`// +build !race`ビルドタグが追加されました。
6.  **`src/pkg/sync/atomic/race.go`**
    *   新規ファイルとして追加されました。
    *   ファイルの先頭に`// +build race`ビルドタグがあります。
    *   `package atomic`として定義されています。
    *   `runtime`と`unsafe`パッケージをインポートしています。
    *   `var mtx uint32 = 1`というグローバル変数が定義されています。
    *   `CompareAndSwapInt32`, `CompareAndSwapUint32`, `CompareAndSwapInt64`, `CompareAndSwapUint64`, `CompareAndSwapPointer`, `CompareAndSwapUintptr`といった`CompareAndSwap`系の関数が実装されています。これらの関数は、実際の比較と交換のロジックの前後で`runtime.RaceSemacquire`, `runtime.RaceAcquire`, `runtime.RaceReleaseMerge`, `runtime.RaceSemrelease`を呼び出しています。
    *   `AddInt32`, `AddUint32`, `AddInt64`, `AddUint64`, `AddUintptr`といった`Add`系の関数が実装されています。これらの関数も同様に`runtime.RaceSemacquire`, `runtime.RaceAcquire`, `runtime.RaceReleaseMerge`, `runtime.RaceSemrelease`を呼び出しています。
    *   `LoadInt32`, `LoadUint32`, `LoadInt64`, `LoadUint64`, `LoadPointer`, `LoadUintptr`といった`Load`系の関数が実装されています。これらの関数は、読み込みの前後で`runtime.RaceSemacquire`, `runtime.RaceAcquire`, `runtime.RaceSemrelease`を呼び出しています。
    *   `StoreInt32`, `StoreUint32`, `StoreInt64`, `StoreUint64`, `StorePointer`, `StoreUintptr`といった`Store`系の関数が実装されています。これらの関数は、書き込みの前後で`runtime.RaceSemacquire`, `runtime.RaceRelease`, `runtime.RaceSemrelease`を呼び出しています。

## コアとなるコードの解説

新しく追加された`src/pkg/sync/atomic/race.go`ファイルがこのコミットの核心です。このファイルは、Goのデータ競合検出機能(ThreadSanitizer)が有効な場合にのみコンパイルされるように設計されています(`// +build race`タグ)。

このファイル内の各アトミック操作の関数(例: `CompareAndSwapUint32`, `AddUint32`, `LoadUint32`, `StoreUint32`など)は、以下のような共通のパターンに従って実装されています。

1.  **`runtime.RaceSemacquire(&mtx)`**:
    *   アトミック操作の開始時に呼び出されます。
    *   これは、ThreadSanitizerに対して、このアトミック操作が「セマフォ」のような同期プリミティブによって保護されているかのように振る舞うことを通知します。`mtx`は、この目的のために定義されたグローバルな`uint32`変数です。
    *   これにより、ThreadSanitizerは、この操作が不可分であり、他のメモリ操作との順序関係を正しく追跡できるようになります。

2.  **`runtime.RaceAcquire(unsafe.Pointer(val))` または `runtime.RaceAcquire(unsafe.Pointer(addr))`**:
    *   アトミック操作がアクセスするメモリ位置(`val`または`addr`)に対して「取得」イベントをThreadSanitizerに通知します。
    *   「取得」イベントは、そのメモリ位置に対する以前の書き込みが、現在のゴルーチンから「見える」ようになることを意味します。これは、メモリバリアのような効果をThreadSanitizerにシミュレートさせ、競合検出の精度を高めます。

3.  **実際のアトミック操作のロジック**:
    *   例えば、`CompareAndSwapUint32`では`if *val == old { *val = new; swapped = true }`という比較と交換のロジックが実行されます。
    *   `AddUint32`では`*val = *val + delta`という加算ロジックが実行されます。
    *   `LoadUint32`では`val = *addr`という読み込みロジックが実行されます。
    *   `StoreUint32`では`*addr = val`という書き込みロジックが実行されます。

4.  **`runtime.RaceReleaseMerge(unsafe.Pointer(val))` または `runtime.RaceRelease(unsafe.Pointer(addr))`**:
    *   アトミック操作の完了時に呼び出されます。
    *   `RaceReleaseMerge`は、`CompareAndSwap`のように、条件付きでメモリが変更される場合や、複数のメモリ状態がマージされるような操作に適しています。
    *   `RaceRelease`は、単純な書き込み操作(`Store`や`Add`の結果としての書き込み)に使用されます。
    *   これらの「解放」イベントは、現在のゴルーチンによるメモリへの変更が、そのメモリ位置を「取得」する他のゴルーチンから「見える」ようになることをThreadSanitizerに通知します。これにより、ThreadSanitizerは、並行アクセスが適切に同期されているかを判断し、データ競合を検出します。

5.  **`runtime.RaceSemrelease(&mtx)`**:
    *   アトミック操作の終了時に呼び出されます。
    *   `RaceSemacquire`と対になり、セマフォの解放をThreadSanitizerに通知します。

これらのフックをアトミック操作に組み込むことで、ThreadSanitizerは、アトミック操作自体が競合フリーであるにもかかわらず、それらがプログラム全体のデータフローとどのように相互作用するかを正確に追跡できるようになります。これにより、アトミック操作が関与するより複雑なデータ競合パターンも検出可能になります。

## 関連リンク

*   GitHubコミットページ: [https://github.com/golang/go/commit/59b87453285d952c5b1bc705ca5ffc87e7f15f61](https://github.com/golang/go/commit/59b87453285d952c5b1bc705ca5ffc87e7f15f61)
*   関連するGo Change List (CL): [https://golang.org/cl/6456044](https://golang.org/cl/6456044)
*   関連するGo Change List (CL): [https://golang.org/cl/6536059](https://golang.org/cl/6536059)

## 参考にした情報源リンク

*   Go言語の`sync/atomic`パッケージ公式ドキュメント: [https://pkg.go.dev/sync/atomic](https://pkg.go.dev/sync/atomic)
*   Go言語のデータ競合検出器 (The Go Race Detector): [https://go.dev/doc/articles/race_detector](https://go.dev/doc/articles/race_detector)
*   ThreadSanitizerの概要 (Wikipedia): [https://en.wikipedia.org/wiki/ThreadSanitizer](https://en.wikipedia.org/wiki/ThreadSanitizer)
*   Goのビルド制約 (Build Constraints): [https://go.dev/cmd/go/#hdr-Build_constraints](https://go.dev/cmd/go/#hdr-Build_constraints)
*   Goの`unsafe`パッケージ: [https://pkg.go.dev/unsafe](https://pkg.go.dev/unsafe)
*   Goの`runtime`パッケージ (特に内部の`race`関連関数): [https://pkg.go.dev/runtime](https://pkg.go.dev/runtime) (ただし、`Race*`関数は通常、公開APIではありません)
*   Dmitriy VyukovによるThreadSanitizerに関する発表やブログ記事 (例: "Go Concurrency Primitives: Data Races and the Race Detector" など、具体的なURLは時期によって変動する可能性がありますが、検索で関連情報が見つかります)