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

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

このコミットは、Go言語のランタイムにおけるデータ競合検出ツール(race detector)の更新に関するものです。具体的には、ランタイムのバージョンをリビジョン188542に更新し、データ競合検出時の出力改善と、最初の競合検出時にプログラムを中断するオプションの追加を行っています。

コミット

commit fbf5fd5f1e47e8b4fa971ac45d1b3893f85ea523
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Fri Aug 16 17:51:09 2013 +0400

    runtime/race: update runtime to rev 188542
    
    Fixes #6107.
    race: output goroutine 1 as main goroutine
    
    Fixes #6130.
    race: option to abort program on first detected error
    
    R=golang-dev, mikioh.mikioh
    CC=golang-dev
    https://golang.org/cl/12968044

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

https://github.com/golang/go/commit/fbf5fd5f1e47e8b4fa971ac45d1b3893f85ea523

元コミット内容

このコミットは、Goランタイムのデータ競合検出機能(runtime/raceパッケージ)を更新するものです。主な変更点は以下の通りです。

  1. ランタイムの更新: runtime/raceパッケージが、Goランタイムのリビジョン188542に基づいて更新されました。これは、データ競合検出器が依存する内部ランタイムの変更を取り込むことを意味します。
  2. Issue #6107の修正: データ競合検出時の出力において、メインゴルーチン(プログラムのエントリポイントであるmain関数を実行するゴルーチン)を「goroutine 1」としてではなく、「main goroutine」として明確に表示するように変更されました。これにより、競合レポートの可読性が向上し、ユーザーが問題の発生源を特定しやすくなります。
  3. Issue #6130の修正: 最初のデータ競合が検出された際に、プログラムを即座に中断(abort)するオプションが追加されました。これは、デバッグ時に最初の競合発生箇所で実行を停止させたい場合に有用です。

変更の背景

Go言語は並行処理を強力にサポートしていますが、並行処理はデータ競合(data race)というデバッグが困難なバグを引き起こす可能性があります。データ競合は、複数のゴルーチンが同時に同じメモリ位置にアクセスし、そのうち少なくとも1つが書き込み操作である場合に発生します。このような競合は、プログラムの非決定的な動作やクラッシュの原因となります。

Goのデータ競合検出器は、このような問題を特定するための重要なツールです。このコミットは、その検出器の機能と使いやすさを向上させることを目的としています。

  • 出力の改善(Issue #6107): 以前のバージョンでは、メインゴルーチンが他のゴルーチンと同様に番号で識別されていたため、競合レポートを解析する際に、どのゴルーチンがメインゴルーチンであるかを判別しにくい場合がありました。これを「main goroutine」と明示することで、レポートの理解が容易になり、デバッグ効率が向上します。
  • 早期中断オプション(Issue #6130): 大規模な並行プログラムでは、データ競合が多数発生する可能性があります。デバッグの初期段階では、最初の競合が発生した時点でプログラムの実行を停止し、その時点での状態を詳細に調査したいというニーズがあります。このオプションは、そのようなシナリオに対応するために追加されました。これにより、不要な競合レポートの生成を避け、問題の根本原因に迅速に到達できるようになります。

これらの変更は、Goのデータ競合検出器の成熟度を高め、開発者がより堅牢な並行プログラムを構築するのを支援することを目的としています。

前提知識の解説

Go言語の並行処理とゴルーチン

Go言語は、軽量なスレッドである「ゴルーチン(goroutine)」と、ゴルーチン間の安全な通信を可能にする「チャネル(channel)」を通じて、並行処理を言語レベルで強力にサポートしています。

  • ゴルーチン: goキーワードを使って関数呼び出しの前に記述することで、その関数を新しいゴルーチンとして並行に実行します。ゴルーチンはOSのスレッドよりもはるかに軽量であり、数千、数万のゴルーチンを同時に実行することが可能です。
  • チャネル: ゴルーチン間で値を送受信するための通信メカニズムです。チャネルを使用することで、共有メモリを直接操作することなく、安全にデータをやり取りできます。Goの並行処理の哲学は「共有メモリによる通信ではなく、通信による共有」です。

データ競合 (Data Race)

データ競合は、並行プログラミングにおける最も一般的なバグの一つです。以下の3つの条件がすべて満たされたときに発生します。

  1. 複数のゴルーチンが同じメモリ位置にアクセスする: 2つ以上のゴルーチンが、同じ変数やデータ構造を読み書きしようとします。
  2. 少なくとも1つのアクセスが書き込みである: アクセスのうち少なくとも1つが、メモリの内容を変更する操作(書き込み)です。
  3. アクセスが同期されていない: アクセスが、ミューテックス(sync.Mutex)やチャネルなどの同期メカニズムによって適切に保護されていない場合です。

データ競合が発生すると、プログラムの動作が非決定的になり、予期せぬ結果(例: 間違った値の読み取り、プログラムのクラッシュ)を引き起こす可能性があります。

Go Race Detector

Go言語には、データ競合を検出するための組み込みツールである「Go Race Detector」があります。これは、プログラムの実行時にメモリアクセスを監視し、データ競合のパターンを検出します。

  • 使用方法: go test -racego run -racego build -racego install -raceなどのコマンドに-raceフラグを追加することで有効にできます。
  • 検出メカニズム: Go Race Detectorは、Googleが開発したThreadSanitizer(TSan)という動的解析ツールをベースにしています。TSanは、メモリへのアクセスをインストルメント(計測コードを挿入)し、各メモリ位置へのアクセス履歴を追跡することで、競合を検出します。
  • オーバーヘッド: Race Detectorを有効にすると、メモリ使用量が5〜10倍、実行時間が2〜20倍増加する可能性があります。これは、詳細な監視と履歴の記録にリソースが必要なためです。
  • レポート: データ競合が検出されると、Race Detectorは詳細なレポートを出力します。このレポートには、競合が発生したメモリ位置、競合するアクセスを行ったゴルーチンのスタックトレース、およびゴルーチンが作成された場所のスタックトレースが含まれます。

runtime/raceパッケージ

runtime/raceパッケージは、Go Race Detectorの内部実装を担うパッケージです。このパッケージはGoの標準ライブラリの一部ですが、通常、開発者が直接インポートして使用するものではありません。Goコンパイラとランタイムが、-raceフラグが指定された場合にこのパッケージの機能を利用してデータ競合検出を行います。このパッケージには、競合検出ロジック、レポート生成、および関連するランタイムサポートが含まれています。

技術的詳細

このコミットは、主にGo Race Detectorの内部動作と出力形式に影響を与えます。

ランタイムリビジョンの更新 (rev 188542)

runtime/raceパッケージは、Goランタイムの特定の内部構造や関数に依存しています。Goランタイム自体は継続的に開発されており、その内部APIやデータ構造が変更されることがあります。このコミットでruntime/raceがリビジョン188542に更新されたということは、Race Detectorが最新のランタイムの変更に対応するために、その内部コードが同期されたことを意味します。これは、Race DetectorがGoの最新バージョンで正しく機能し続けるために必要なメンテナンス作業です。バイナリファイルのサイズが増加しているのは、このランタイムの更新に伴う変更が反映された結果と考えられます。

メインゴルーチンの出力変更 (output_test.goの変更)

src/pkg/runtime/race/output_test.goの変更は、データ競合レポートの出力形式がどのように変更されたかを示しています。

変更前:

Previous write by goroutine 1:
  main.store()
      .*/main.go:11 +0x[0-9,a-f]+
  main.main()
      .*/main.go:7 +0x[0-9,a-f]+

Goroutine 1 (running) created at:
  _rt0_go()
      .*/src/pkg/runtime/asm_amd64.s:[0-9]+ +0x[0-9,a-f]+

変更後:

Previous write by main goroutine:
  main.store()
      .*/main.go:12 +0x[0-9,a-f]+
  main.main()
      .*/main.go:8 +0x[0-9,a-f]+

Goroutine [0-9] (running) created at:
  main.startRacer()
      .*/main.go:15 +0x[0-9,a-f]+
  main.main()
      .*/main.go:7 +0x[0-9,a-f]+

この変更により、Goroutine 1という一般的な識別子ではなく、main goroutineというより意味のあるラベルが使用されるようになりました。これは、ユーザーがレポートを読んだときに、どのゴルーチンがプログラムのメイン実行パスを表しているのかを直感的に理解できるようにするための改善です。また、行番号のオフセットが変更されているのは、テストコード自体にimport "time"time.Sleepが追加されたことによるものです。

最初の競合検出でのプログラム中断オプション

この機能は、Race Detectorの内部で、競合が検出された際に特定のフラグや設定に基づいてプログラムの実行を終了させるロジックが追加されたことを示唆しています。通常、Race Detectorはすべての競合を報告し、プログラムの実行を継続しますが、このオプションが有効になると、最初の競合が検出された時点でos.Exitのようなメカニズムを通じてプログラムが終了します。これにより、デバッガでアタッチしている場合などに、問題発生直後の状態をキャプチャしやすくなります。このオプションは、おそらく環境変数やビルドフラグを通じて制御されるものと推測されます。

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

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

  • src/pkg/runtime/race/README: Race DetectorのREADMEファイル。
  • src/pkg/runtime/race/output_test.go: Race Detectorの出力形式をテストするためのGoテストファイル。
  • src/pkg/runtime/race/race_darwin_amd64.syso: macOS (AMD64) 用のRace Detectorのバイナリライブラリ。
  • src/pkg/runtime/race/race_linux_amd64.syso: Linux (AMD64) 用のRace Detectorのバイナリライブラリ。
  • src/pkg/runtime/race/race_windows_amd64.syso: Windows (AMD64) 用のRace Detectorのバイナリライブラリ。

コアとなるコードの解説

src/pkg/runtime/race/README

このファイルには、Race Detectorのビルドとテストに関する簡単な説明が含まれています。今回のコミットでは、以下の行が追加されました。

--- a/src/pkg/runtime/race/README
+++ b/src/pkg/runtime/race/README
@@ -9,3 +9,4 @@ $ ./buildgo.sh
 
 Tested with gcc 4.6.1 and 4.7.0.  On Windows it's built with 64-bit MinGW.
 
+Current runtime is built on rev 188542.

この変更は、Race DetectorがGoランタイムのリビジョン188542に基づいてビルドされていることを明記するためのものです。これは、Race Detectorのバージョン管理と、それが依存するランタイムのバージョンを示す重要な情報です。

src/pkg/runtime/race/output_test.go

このファイルは、Race Detectorが生成する出力の形式が期待通りであることを検証するためのテストコードです。このコミットでは、テスト対象のGoプログラムと、それに対応する期待されるRace Detectorの出力パターンが変更されています。

主な変更点:

  1. import "time"の追加: テスト対象のGoプログラムにimport "time"が追加されました。
  2. time.Sleep(10*time.Millisecond)の追加: racer関数にtime.Sleepが追加されました。これは、ゴルーチンのスケジューリングにわずかな遅延を導入し、競合の発生タイミングをより確実に再現するため、またはテストの安定性を向上させるための変更である可能性があります。
  3. 正規表現パターンの変更: 期待される出力の正規表現パターンが変更されました。
    • Previous write by goroutine 1:Previous write by main goroutine: に変更されました。これは、メインゴルーチンの識別子がより分かりやすいものになったことを反映しています。
    • 行番号のオフセットが調整されました(例: .*/main.go:11.*/main.go:12 に)。これは、テスト対象のGoプログラムにimport "time"time.Sleepが追加されたことで、ソースコードの行番号がずれたためです。
    • Goroutine 1 (running) created at: のブロックが削除され、Goroutine [0-9] (running) created at: のパターンが調整されました。これは、メインゴルーチンの作成に関する詳細なランタイム内部のスタックトレースが、一般的なゴルーチン作成のスタックトレースに置き換えられたことを示唆しています。これにより、レポートがより簡潔になり、ユーザーにとって重要な情報(アプリケーションコード内のスタックトレース)に焦点が当てられるようになります。

これらの変更は、Race Detectorの出力がよりユーザーフレンドリーになり、デバッグプロセスを効率化するためのものです。

バイナリファイル (.sysoファイル)

  • src/pkg/runtime/race/race_darwin_amd64.syso
  • src/pkg/runtime/race/race_linux_amd64.syso
  • src/pkg/runtime/race/race_windows_amd64.syso

これらのファイルは、それぞれmacOS、Linux、WindowsのAMD64アーキテクチャ向けのRace Detectorの静的ライブラリ(System Object)です。これらのファイルがバイナリレベルで変更されていることは、Goランタイムのリビジョン更新(rev 188542)に伴い、Race Detectorのコアロジックが再コンパイルされ、新しいランタイムの内部APIやデータ構造に対応するように更新されたことを意味します。また、「最初の競合検出時にプログラムを中断するオプション」の実装も、これらのバイナリファイル内のロジック変更として含まれている可能性が高いです。

関連リンク

参考にした情報源リンク