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

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

このコミットは、Go言語のランタイムにおけるローカル変数名の命名規則の変更に関するものです。具体的には、グローバル変数 m (Machine) および g (Goroutine) との混同を避けるため、ローカル変数名として mp および gp を使用するように修正しています。この変更は、コードの可読性と保守性を向上させ、将来的なバグの発生を防ぐことを目的としています。

コミット

commit 70e967b7bc6583735c098a91395b9f42b017e006
Author: Jingcheng Zhang <diogin@gmail.com>
Date:   Wed Dec 19 00:30:29 2012 +0800

    runtime: use "mp" and "gp" instead of "m" and "g" for local variable name to avoid confusion with the global "m" and "g".
    
    R=golang-dev, minux.ma, rsc
    CC=bradfitz, golang-dev
    https://golang.org/cl/6939064
---
 src/pkg/runtime/cgocall.c             |  8 +++---
 src/pkg/runtime/malloc.goc            | 22 ++++++++---------\
 src/pkg/runtime/mgc0.c                | 26 ++++++++++----------
 src/pkg/runtime/mprof.goc             | 12 ++++-----\
 src/pkg/runtime/os_plan9.h            |  2 +-\
 src/pkg/runtime/proc.c                | 46 +++++++++++++++++------------------
 src/pkg/runtime/runtime.h             |  2 +-\
 src/pkg/runtime/signal_netbsd_386.c   |  6 ++---\
 src/pkg/runtime/signal_netbsd_amd64.c |  6 ++---\
 src/pkg/runtime/sys_darwin_386.s      |  2 +-\
 src/pkg/runtime/sys_darwin_amd64.s     |  2 +-\
 src/pkg/runtime/sys_linux_386.s       |  4 +--
 src/pkg/runtime/sys_linux_amd64.s     |  4 +--
 src/pkg/runtime/sys_linux_arm.s       |  4 +--
 src/pkg/runtime/sys_openbsd_386.s     |  4 +--
 src/pkg/runtime/sys_openbsd_amd64.s   |  4 +--
 src/pkg/runtime/thread_darwin.c       |  8 +++---
 src/pkg/runtime/thread_freebsd.c      | 20 +++++++--------
 src/pkg/runtime/thread_linux.c        |  8 +++---
 src/pkg/runtime/thread_netbsd.c       | 12 ++++-----\
 src/pkg/runtime/thread_openbsd.c      | 14 +++++------
 src/pkg/runtime/thread_plan9.c        | 16 ++++++------
 src/pkg/runtime/thread_windows.c      |  8 +++---
 src/pkg/runtime/traceback_arm.c       | 24 +++++++++---------\
 src/pkg/runtime/traceback_x86.c       | 26 ++++++++++----------
 25 files changed, 145 insertions(+), 145 deletions(-)

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

https://github.com/golang/go/commit/70e967b7bc6583735c098a91395b9f42b017e006

元コミット内容

このコミットの目的は、Goランタイム内のローカル変数名 mg を、それぞれ mpgp に変更することです。これは、Goランタイムが内部的に使用するグローバルな M (Machine) および G (Goroutine) 構造体へのポインタと、関数内で宣言されるローカル変数名が衝突し、混同を招く可能性があったためです。

変更の背景

Go言語のランタイムは、並行処理を効率的に管理するために、M (Machine)、G (Goroutine)、P (Processor) という3つの主要な抽象化を使用しています。

  • M (Machine): OSのスレッドを表します。Goランタイムは、OSスレッド上でGoルーチンを実行します。
  • G (Goroutine): Go言語の軽量な並行処理単位です。GoルーチンはOSスレッドよりもはるかに軽量で、数百万個作成することも可能です。
  • P (Processor): Goルーチンを実行するための論理プロセッサを表します。PはMとGの間の仲介役となり、GoルーチンをMにディスパッチします。

これらのMとGは、ランタイムの内部で頻繁に参照される重要なグローバル変数として存在します。しかし、一部の関数内で、ローカル変数として M 型の変数に mG 型の変数に g という名前が付けられていました。これにより、コードを読む際に、その mg がグローバルなランタイムのM/Gを指しているのか、それとも関数スコープのローカル変数を指しているのかが不明瞭になり、混乱を招く可能性がありました。

このコミットは、このような命名の曖昧さを解消し、コードの可読性と保守性を向上させることを目的としています。

前提知識の解説

Goランタイムのスケジューラ (M, G, P)

Goランタイムのスケジューラは、M, G, Pという3つの要素から構成される協調的マルチタスクシステムです。

  • G (Goroutine): Go言語における並行実行の単位です。非常に軽量で、数MBのスタックサイズから始まり、必要に応じて動的に拡張・縮小されます。Goプログラムのすべての関数呼び出しは、Goルーチン内で実行されます。
  • M (Machine): OSのスレッドに相当します。Goランタイムは、OSスレッドを抽象化したMを管理し、その上でGoルーチンを実行します。Mは、Goルーチンの実行、システムコール、CGO呼び出しなどを担当します。
  • P (Processor): 論理プロセッサを表します。Pは、Goルーチンを実行するためのコンテキストを提供し、MとGの間の仲介役となります。Pは、実行可能なGoルーチンのキューを保持し、MがGoルーチンを実行する際にPをアタッチします。これにより、複数のMが同時にGoルーチンを実行できるようになります。

このM, G, Pモデルにより、Goランタイムは効率的な並行処理を実現しています。

C言語とGo言語の相互運用 (CGO)

Go言語は、C言語のコードを呼び出すためのCGOというメカニズムを提供しています。CGOを使用すると、Goプログラムから既存のCライブラリを利用したり、C言語で記述されたパフォーマンスクリティカルな部分をGoプログラムに組み込んだりすることができます。

CGO呼び出しでは、GoランタイムとCランタイムの間でスタックやレジスタの状態を適切に切り替える必要があります。このコミットで変更されている src/pkg/runtime/cgocall.c は、CGO呼び出しに関連するランタイムの内部処理を扱っています。

スタック管理とトレースバック

Goルーチンは、それぞれ独自のスタックを持っています。Goランタイムは、Goルーチンのスタックを動的に管理し、必要に応じて拡張・縮小します。また、プログラムのクラッシュ時やデバッグ時に、現在のGoルーチンの呼び出し履歴(スタックトレース)を生成する機能も持っています。このコミットで変更されている src/pkg/runtime/proc.csrc/pkg/runtime/traceback_*.c は、これらのスタック管理やトレースバックに関連する処理を扱っています。

技術的詳細

このコミットの技術的な詳細は、主にGoランタイムの内部実装における変数名の変更に集約されます。

GoランタイムのC言語で書かれた部分では、M 構造体へのポインタを mG 構造体へのポインタを g という変数名で扱うことが一般的でした。しかし、ランタイムのグローバルスコープにも m (現在のM) や g (現在のG) といった変数が存在するため、関数内でローカル変数として mg を宣言すると、グローバル変数とローカル変数のどちらを参照しているのかがコードを読む上で分かりにくくなるという問題がありました。

このコミットでは、この問題を解決するために、関数内で M 型のローカル変数を宣言する際には mp (Machine Pointer)、G 型のローカル変数を宣言する際には gp (Goroutine Pointer) という命名規則を採用しています。これにより、コードを見ただけで、その変数がローカルなポインタ変数なのか、それともグローバルなランタイムのM/Gを指しているのかが明確になります。

この変更は、Goランタイムのコードベース全体にわたって行われており、src/pkg/runtime ディレクトリ以下の多数のファイルに影響を与えています。これには、メモリ管理 (malloc.goc, mgc0.c), プロファイリング (mprof.goc), プロセス管理 (proc.c), シグナルハンドリング (signal_*.c), スレッド管理 (thread_*.c), トレースバック (traceback_*.c) など、ランタイムの様々なサブシステムが含まれます。

特に、アセンブリ言語で書かれたファイル (sys_darwin_386.s, sys_linux_386.s など) においても、MG を引数として受け取る関数のコメントや引数名が mpgp に変更されています。これは、C言語のコードとアセンブリ言語のコードの間で、変数名の整合性を保つための重要な変更です。

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

このコミットは、GoランタイムのC言語およびアセンブリ言語のコード全体にわたる変数名の変更が主な内容です。具体的な変更は、M *mM *mp に、G *gG *gp に置き換えるというパターンがほとんどです。

例として、src/pkg/runtime/cgocall.c の変更を見てみましょう。

変更前:

void
runtime·NumCgoCall(int64 ret)
{
	M *m;

	ret = 0;
	for(m=runtime·atomicloadp(&runtime·allm); m; m=m->alllink)
		ret += m->ncgocall;
	FLUSH(&ret);
}

変更後:

void
runtime·NumCgoCall(int64 ret)
{
	M *mp; // ローカル変数名が mp に変更

	ret = 0;
	for(mp=runtime·atomicloadp(&runtime·allm); mp; mp=mp->alllink) // mp を使用
		ret += mp->ncgocall; // mp を使用
	FLUSH(&ret);
}

同様に、src/pkg/runtime/proc.cruntime·newstack 関数では、G *g1;G *gp; に変更され、それに伴い関数内の g1 の参照がすべて gp に変更されています。

アセンブリ言語のファイルでは、コメント内の引数名が変更されています。例えば、src/pkg/runtime/sys_darwin_386.s では、bsdthread_create 関数のコメントが以下のように変更されています。

変更前:

// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void))

変更後:

// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void))

これらの変更は、コードの機能には影響を与えませんが、コードの意図をより明確にし、将来的な開発者がコードを理解しやすくするための重要な改善です。

コアとなるコードの解説

このコミットにおけるコアとなるコードの変更は、Goランタイムの内部で M (Machine) および G (Goroutine) 構造体へのポインタを扱う際のローカル変数名の命名規則の統一です。

Goランタイムは、C言語で記述された部分が多く、その中で MG といった重要なランタイム構造体が頻繁に利用されます。これらの構造体は、Goルーチンのスケジューリング、メモリ管理、システムコール処理など、ランタイムの根幹をなす機能において中心的な役割を果たします。

変更前は、関数内で M 型のローカル変数を mG 型のローカル変数を g と命名することがありました。しかし、Goランタイムには、現在のOSスレッドに紐付けられた M 構造体へのグローバルポインタ m や、現在のGoルーチンに紐付けられた G 構造体へのグローバルポインタ g が存在します。このため、ローカル変数とグローバル変数の名前が衝突し、コードの読解時にどちらの mg を指しているのかが曖昧になるという問題がありました。

このコミットでは、この曖昧さを解消するために、ローカル変数として M 型のポインタを宣言する際には mp (Machine Pointer)、G 型のポインタを宣言する際には gp (Goroutine Pointer) という命名規則を導入し、既存のコードを修正しています。

この変更により、コードの可読性が大幅に向上します。例えば、mp->ncgocall と書かれていれば、それがローカルな M ポインタが指す ncgocall メンバーであることを明確に理解できます。もし m->ncgocall と書かれていた場合、それがグローバルな m を指すのか、それともローカルな m を指すのか、文脈を注意深く読まなければ判断できませんでした。

この命名規則の統一は、特に大規模なコードベースや、複数の開発者が関わるプロジェクトにおいて、コードの保守性を高める上で非常に重要です。命名規則が明確であれば、新しい開発者がコードベースに慣れるまでの時間を短縮し、既存の開発者もより迅速かつ正確にコードを理解できるようになります。結果として、バグの混入を防ぎ、開発効率を向上させる効果が期待できます。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に src/pkg/runtime ディレクトリ)
  • Go言語のM, G, Pスケジューラに関する一般的な技術解説記事 (Web検索を通じて得られた情報)
  • Go言語のCGOに関するドキュメント (Web検索を通じて得られた情報)
  • コミットメッセージと変更されたファイルの内容
  • Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6939064