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

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

このコミットは、Go言語のランタイム、特にガベージコレクタのコア部分である src/pkg/runtime/mgc0.c ファイルに対する変更です。このファイルは、Goプログラムのメモリ管理において重要な役割を担っています。

コミット

commit 89ec208ee8e2a9de6dc4f716f37eb41da4105a6d
Author: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
Date:   Fri Jan 4 10:20:50 2013 -0500

    runtime: introduce typedefs and delete struct keywords in mgc0.c
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/7029055

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

https://github.com/golang/go/commit/89ec208ee8e2a9de6dc4f716f37eb41da4105a6d

元コミット内容

mgc0.c ファイルにおいて、struct キーワードを伴う構造体宣言を typedef を用いたエイリアスに置き換え、そのエイリアスを使用するように変更しています。これにより、コードの可読性と簡潔性が向上しています。

変更の背景

Go言語のランタイムは、パフォーマンスが非常に重要であるため、一部の低レベルな処理にはC言語が使用されています。mgc0.c は、Goのガベージコレクタのマークフェーズに関連する重要なC言語ファイルです。

C言語では、構造体を宣言する際に struct TagName { ... }; のように struct キーワードを使用し、その構造体型の変数を宣言する際にも struct TagName variable_name; のように struct キーワードを繰り返し記述する必要があります。これは冗長であり、コードの可読性を損なう可能性があります。

このコミットの背景には、C言語の慣用的な記述方法である typedef を用いて構造体エイリアスを定義し、コードをより簡潔に、そしてGo言語のコードスタイルに近い形で記述するという意図があります。これにより、GoランタイムのC言語部分のメンテナンス性が向上し、開発者がコードを理解しやすくなることが期待されます。

前提知識の解説

C言語の structtypedef

C言語における struct は、異なるデータ型の変数を一つにまとめるためのユーザー定義型です。例えば、以下のように宣言します。

struct Point {
    int x;
    int y;
};

この Point 型の変数を宣言する際には、常に struct キーワードを前置する必要があります。

struct Point p1;

これは、C言語のコンパイラが Point を「構造体タグ」として認識するためです。

一方、typedef は既存のデータ型に新しい名前(エイリアス)を付けるためのキーワードです。これを利用して、struct キーワードを省略して構造体変数を宣言できるようにするのが一般的な慣習です。

typedef struct Point {
    int x;
    int y;
} Point; // ここで struct Point に Point という新しい名前を付けている

または、先に構造体を宣言し、後から typedef でエイリアスを付けることもできます。

struct Point {
    int x;
    int y;
};
typedef struct Point Point; // struct Point に Point というエイリアスを付ける

この typedef を使用すると、変数の宣言がより簡潔になります。

Point p1; // struct キーワードが不要になる

このコミットは、まさにこの typedef を用いた慣用的な記述方法を mgc0.c に適用しています。

Go言語のガベージコレクタ (mgc0.c の役割)

Go言語は自動メモリ管理(ガベージコレクション)を採用しており、開発者が手動でメモリを解放する必要がありません。Goのガベージコレクタは、主に「マーク&スイープ」アルゴリズムをベースにしています。

mgc0.c ファイルは、Goランタイムのガベージコレクタの初期の実装、特にオブジェクトのマーク(到達可能なオブジェクトを識別する)フェーズに関連する低レベルな処理を担っていました。このファイルには、ガベージコレクション中に一時的にポインタやビットマップ情報を保持するための構造体や、それらを処理する関数が定義されています。

  • PtrTarget: ポインタとそのターゲットに関する情報を保持する構造体。
  • BitTarget: ビットマップ情報(オブジェクトがマークされたかどうかなど)を保持する構造体。
  • BufferList: PtrTargetBitTarget の配列を保持し、ガベージコレクション中の作業バッファとして機能する構造体。

これらの構造体は、ガベージコレクタがメモリ上のオブジェクトを走査し、到達可能なオブジェクトをマークしていく過程で頻繁に使用されます。

技術的詳細

このコミットの技術的詳細は、C言語の typedef の適用と、それに伴うコードベース全体での struct キーワードの削除に集約されます。

具体的には、以下の3つの構造体に対して typedef が導入されました。

  1. struct PtrTarget -> PtrTarget
  2. struct BitTarget -> BitTarget
  3. struct BufferList -> BufferList

変更前は、これらの構造体を宣言するたびに struct キーワードを記述する必要がありました。例えば、struct PtrTarget ptrtarget[IntermediateBufferCapacity]; のように記述されていました。

変更後は、typedef によって定義されたエイリアス名(例: PtrTarget)を直接使用できるようになり、ptrtarget[IntermediateBufferCapacity]; のように記述が簡潔になりました。これは、関数引数、構造体メンバーの型、ローカル変数の宣言など、mgc0.c 内の複数の箇所にわたって適用されています。

この変更は、コードの機能的な振る舞いには一切影響を与えません。純粋にコードのスタイルと可読性を改善するためのリファクタリングです。しかし、低レベルなシステムプログラミングにおいて、このような細かな改善がコードベース全体の品質とメンテナンス性に寄与します。特に、Goランタイムのように多くの開発者が関わるプロジェクトでは、一貫性のある簡潔なコーディングスタイルが重要となります。

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

src/pkg/runtime/mgc0.c ファイルにおける変更は以下の通りです。

  1. typedef の追加:

    --- a/src/pkg/runtime/mgc0.c
    +++ b/src/pkg/runtime/mgc0.c
    @@ -163,12 +163,14 @@ enum {
     // is moved/flushed to the work buffer (Workbuf).
     // The size of an intermediate buffer is very small,
     // such as 32 or 64 elements.
    +typedef struct PtrTarget PtrTarget;
     struct PtrTarget
     {
     	void *p;
     	uintptr ti;
     };
     
    +typedef struct BitTarget BitTarget;
     struct BitTarget
     {
     	void *p;
    @@ -176,13 +178,14 @@ struct BitTarget
     	uintptr *bitp, shift;
     };
     
    +typedef struct BufferList BufferList;
     struct BufferList
     {
    -\tstruct PtrTarget ptrtarget[IntermediateBufferCapacity];
    -\tstruct BitTarget bittarget[IntermediateBufferCapacity];
    -\tstruct BufferList *next;
    +\tPtrTarget ptrtarget[IntermediateBufferCapacity];
    +\tBitTarget bittarget[IntermediateBufferCapacity];
    +\tBufferList *next;
     };
    -static struct BufferList *bufferList;
    +static BufferList *bufferList;
    
  2. struct キーワードの削除とエイリアスへの置き換え: 関数シグネチャ、変数宣言、構造体初期化など、PtrTarget, BitTarget, BufferList が使用されている全ての箇所で struct キーワードが削除され、typedef で定義されたエイリアスに置き換えられています。

    --- a/src/pkg/runtime/mgc0.c
    +++ b/src/pkg/runtime/mgc0.c
    @@ -207,7 +210,7 @@ static Lock lock;
     //  flushptrbuf
     //  (2nd part, mark and enqueue)
     static void
    -flushptrbuf(struct PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, struct BitTarget *bitbuf)
    +flushptrbuf(PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, BitTarget *bitbuf)
     {
     	byte *p, *arena_start, *obj;
     	uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti;
    @@ -215,8 +218,8 @@ flushptrbuf(struct PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uin
     	PageID k;
     	Obj *wp;
     	Workbuf *wbuf;
    -\tstruct PtrTarget *ptrbuf_end;
    -\tstruct BitTarget *bitbufpos, *bt;
    +\tPtrTarget *ptrbuf_end;
    +\tBitTarget *bitbufpos, *bt;
     
     	arena_start = runtime·mheap.arena_start;
     
    @@ -323,7 +326,7 @@ flushptrbuf(struct PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uin
     		\tif((bits & (bitAllocated|bitMarked)) != bitAllocated)\
     		\t\t\t\tcontinue;\
     
    -\t\t\t*bitbufpos = (struct BitTarget){obj, ti, bitp, shift};\
    +\t\t\t*bitbufpos = (BitTarget){obj, ti, bitp, shift};\
     \t\t\tbitbufpos++;\
     \t\t}\
     
    @@ -398,11 +401,11 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\
     
     	uintptr *pc;\
     
    -\tstruct BufferList *scanbuffers;\
    -\tstruct PtrTarget *ptrbuf, *ptrbuf_end;\
    -\tstruct BitTarget *bitbuf;\
    +\tBufferList *scanbuffers;\
    +\tPtrTarget *ptrbuf, *ptrbuf_end;\
    +\tBitTarget *bitbuf;\
     
    -\tstruct PtrTarget *ptrbufpos;\
    +\tPtrTarget *ptrbufpos;\
     
     	// End of local variable declarations.
     
    @@ -462,7 +465,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\
     
     			\t\t\t\tobj = *(byte**)i;\
     			\t\t\t\tif(obj >= arena_start && obj < arena_used) {\
    -\t\t\t\t\t*ptrbufpos = (struct PtrTarget){obj, 0};\
    +\t\t\t\t\t*ptrbufpos = (PtrTarget){obj, 0};\
     \t\t\t\t\tptrbufpos++;\
     \t\t\t\t\tif(ptrbufpos == ptrbuf_end)\
     \t\t\t\t\t\tgoto flush_buffers;\
    

コアとなるコードの解説

このコミットのコアとなる変更は、C言語の typedef を用いて構造体のエイリアスを定義し、そのエイリアスをコード全体で一貫して使用するようにした点です。

例えば、変更前は struct PtrTarget ptrtarget[IntermediateBufferCapacity]; のように、構造体型の変数を宣言する際に毎回 struct キーワードを記述する必要がありました。これは、C言語の文法上の要請ですが、コードの冗長性を生み出します。

変更後、typedef struct PtrTarget PtrTarget; のように typedef を導入することで、PtrTarget という新しい型名が struct PtrTarget のエイリアスとして定義されます。これにより、以降のコードでは PtrTarget ptrtarget[IntermediateBufferCapacity]; のように struct キーワードを省略して変数を宣言できるようになります。

同様に、関数シグネチャにおいても、static void flushptrbuf(struct PtrTarget *ptrbuf, ...)static void flushptrbuf(PtrTarget *ptrbuf, ...) に変更されています。これにより、関数定義や呼び出しの際にも struct キーワードが不要となり、コードがより簡潔になります。

この変更は、GoランタイムのC言語部分のコードベースにおいて、C言語の慣用的なスタイルを取り入れ、可読性を向上させることを目的としています。特に、ガベージコレクタのような複雑で低レベルなコードでは、このような細かな改善がデバッグや将来の機能追加の際に大きな助けとなります。

関連リンク

参考にした情報源リンク

  • C言語の structtypedef に関する一般的なドキュメントやチュートリアル (例: C言語の入門書、オンラインリファレンス)
  • Go言語のガベージコレクションに関するドキュメントや解説記事 (Goのメモリ管理の仕組みを理解するため)
  • Go言語のソースコード (特に src/pkg/runtime/ ディレクトリ内のC言語ファイル)
  • GitHubのコミット履歴と差分表示機能 (変更内容を詳細に確認するため)
  • Goのコードレビューシステム (Gerrit) のCLページ (関連する議論や背景情報を確認するため)