[インデックス 15589] ファイルの概要
このコミットは、Goランタイムにおけるタイマー管理関数 addtimer
および deltimer
の宣言を src/pkg/runtime/runtime.h
ヘッダーファイルに移動し、これらの関数をGoランタイムの外部から呼び出し可能にする変更です。これにより、Goランタイムの内部実装であったタイマー関数が、より広範なシステムコンポーネント、特にネットワークポーラーから利用できるようになります。
コミット
commit d0c11d20b8eeebcdf2ab597c3b494e40287f9c9b
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Tue Mar 5 09:38:15 2013 +0200
runtime: declare addtimer/deltimer in runtime.h
In preparation for integrated network poller
(https://golang.org/cl/7326051),
this is required to handle deadlines.
R=golang-dev, remyoudompheng, rsc
CC=golang-dev
https://golang.org/cl/7446047
---
src/pkg/runtime/runtime.h | 2 ++\
src/pkg/runtime/time.goc | 19 ++++++++++++-------\
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 9b43f29a59..585d6a536e 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -761,6 +761,8 @@ int64 runtime·cputicks(void);\
int64 runtime·tickspersecond(void);\
void runtime·blockevent(int64, int32);\
extern int64 runtime·blockprofilerate;\
+void runtime·addtimer(Timer*);\
+bool runtime·deltimer(Timer*);\
#pragma varargck argpos runtime·printf 1
#pragma varargck ttype "d" int32
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index 2babb173df..6de989f515 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -15,7 +15,6 @@ package time
static Timers timers;\
static void addtimer(Timer*);\
-static bool deltimer(Timer*);\
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -31,15 +30,13 @@ func Sleep(ns int64) {\
func startTimer(t *Timer) {\
if(raceenabled)\
runtime·racerelease(t);\
-\truntime·lock(&timers);\
-\taddtimer(t);\
-\truntime·unlock(&timers);\
+\truntime·addtimer(t);\
}\
// stopTimer removes t from the timer heap if it is there.
// It returns true if t was removed, false if t wasn't even there.
func stopTimer(t *Timer) (stopped bool) {\
-\tstopped = deltimer(t);\
+\tstopped = runtime·deltimer(t);\
}\
// C runtime.
@@ -79,6 +76,14 @@ runtime·tsleep(int64 ns, int8 *reason)\
static FuncVal timerprocv = {timerproc};\
+void
+runtime·addtimer(Timer *t)\
+{\
+\truntime·lock(&timers);\
+\taddtimer(t);\
+\truntime·unlock(&timers);\
+}\
+\
// Add a timer to the heap and start or kick the timer proc
// if the new timer is earlier than any of the others.
static void
@@ -121,8 +126,8 @@ addtimer(Timer *t)\
// Delete timer t from the heap.
// Do not need to update the timerproc:
// if it wakes up early, no big deal.\
-static bool
-deltimer(Timer *t)\
+bool
+runtime·deltimer(Timer *t)\
{\
\tint32 i;\
\
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d0c11d20b8eeebcdf2ab597c3b494e40287f9c9b
元コミット内容
Goランタイムにおいて、addtimer
および deltimer
関数を runtime.h
で宣言する。これは、統合されたネットワークポーラー(https://golang.org/cl/7326051)の準備のためであり、デッドラインを処理するために必要となる。
変更の背景
このコミットの主な背景は、GoランタイムにおけるネットワークI/Oの効率化と、それに伴うデッドライン(タイムアウト)処理の改善です。Goのランタイムは、goroutineのスケジューリング、メモリ管理、ガベージコレクション、そしてネットワークI/Oの処理など、Goプログラムの実行を支える多くの低レベルな機能を提供します。
コミットメッセージに記載されている「integrated network poller (https://golang.org/cl/7326051)」は、GoのネットワークI/O処理をより効率的にするための重要な変更を指しています。従来のGoのネットワークI/Oは、システムコールをブロックする可能性があり、多数のコネクションを扱う際にパフォーマンスのボトルネックとなることがありました。統合されたネットワークポーラーは、epoll
(Linux), kqueue
(FreeBSD/macOS), IOCP
(Windows) などのOSネイティブな非同期I/Oメカニズムを活用し、単一のスレッドで多数のネットワークイベントを効率的に監視・処理できるようにするものです。これにより、Goのネットワークアプリケーションのスケーラビリティとパフォーマンスが大幅に向上します。
この新しいネットワークポーラーは、ネットワーク操作にデッドライン(タイムアウト)を設定する機能も必要とします。例えば、ネットワーク接続の確立やデータの送受信が一定時間内に完了しない場合に、操作を中断してエラーを返すといった処理です。このようなデッドライン処理を実現するためには、Goランタイムのタイマー機能とネットワークポーラーが密接に連携する必要があります。
addtimer
と deltimer
は、Goランタイム内部でタイマーを管理するための関数です。これらは、time.Sleep
や time.After
などのGoのタイマー機能の基盤となっています。統合されたネットワークポーラーがデッドラインを処理するためには、これらのタイマー管理関数を直接呼び出す必要が生じました。しかし、これらは元々 static
関数として time.goc
内でのみ利用可能でした。このコミットは、これらの関数を runtime.h
で宣言し、runtime·
プレフィックスを付けて外部からアクセス可能にすることで、ネットワークポーラーがタイマーを操作できるようにするための準備作業です。
前提知識の解説
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理するC言語で書かれた部分です。Goのプログラムは、コンパイルされると、Goランタイムとリンクされ、その上で動作します。ランタイムは、以下のような重要な役割を担っています。
- Goroutineスケジューリング: 多数の軽量スレッドであるgoroutineを効率的にOSスレッドにマッピングし、実行を切り替えます。
- メモリ管理: ヒープメモリの割り当てと解放、ガベージコレクション(GC)を行います。
- チャネル通信: goroutine間の安全な通信メカニズムであるチャネルの実装を提供します。
- ネットワークI/O: ネットワーク操作を効率的に処理します。
- システムコールインターフェース: OSの機能にアクセスするためのインターフェースを提供します。
Goランタイムのソースコードは、Go言語のソースツリーの src/runtime
ディレクトリにあります。多くの部分はC言語(またはGoの特殊なC言語方言であるGoC
)で書かれています。
runtime.h
src/pkg/runtime/runtime.h
は、GoランタイムのC言語部分で定義された関数や変数、型などを、他のランタイムのCファイルやGoのコードから利用できるようにするためのヘッダーファイルです。C言語におけるヘッダーファイルと同様に、関数のプロトタイプ宣言や構造体の定義などが含まれています。GoのコードからC言語のランタイム関数を呼び出す際には、このヘッダーファイルに宣言されている必要があります。
time.goc
src/pkg/runtime/time.goc
は、Goランタイムにおける時間管理とタイマー機能の実装を含むファイルです。.goc
拡張子は、Goのランタイムで使われるC言語の特殊な方言(GoC)を示します。このファイルには、タイマーのヒープ管理、time.Sleep
や time.After
のようなGoの標準ライブラリのタイマー機能の低レベルな実装が含まれています。
static
キーワード (C言語)
C言語における static
キーワードは、関数や変数に対して異なる意味を持ちます。このコミットの文脈では、関数に対する static
キーワードが重要です。
- 関数に対する
static
: 関数にstatic
を付けると、その関数のスコープは、その関数が定義されているファイル内に限定されます。つまり、他のファイルからはその関数を直接呼び出すことができません。これは、内部的なヘルパー関数や、特定のファイル内でのみ使用されるべき関数を隠蔽するために使用されます。
このコミットでは、addtimer
と deltimer
から static
キーワードが削除されています。これは、これらの関数が time.goc
の外部からもアクセス可能になることを意味します。
runtime·
プレフィックス
GoランタイムのC言語部分では、Goのコードから呼び出されるC関数や、GoのコードとCのコード間で共有されるシンボルには、慣習的に runtime·
というプレフィックスが付けられます。これは、GoのリンカーがGoの関数名とCの関数名を区別し、正しくリンクするために使用されるGoの内部的な命名規則の一部です。このプレフィックスが付いた関数は、Goのコードから runtime.FunctionName
のように呼び出すことができます。
ネットワークポーラー (Network Poller)
ネットワークポーラーは、複数のネットワークI/O操作を効率的に監視し、準備ができたイベント(データが読み取り可能になった、書き込み可能になったなど)を通知するメカニズムです。OSが提供する非同期I/O API(Linuxのepoll
、macOS/FreeBSDのkqueue
、WindowsのIOCP
など)を利用して実装されます。Goのランタイムは、このポーラーを使って、多数のネットワークコネクションを扱う際に、各コネクションがブロックするのを待つのではなく、イベントドリブンな方法で処理することで、高い並行性とスケーラビリティを実現しています。
技術的詳細
このコミットの技術的な核心は、Goランタイムのタイマー管理関数 addtimer
と deltimer
の可視性(visibility)を変更することにあります。
元々、time.goc
内で static void addtimer(Timer*);
および static bool deltimer(Timer*);
と宣言されていたこれらの関数は、time.goc
ファイル内からのみ呼び出すことができました。これは、これらの関数が time.goc
の内部実装の詳細であり、外部から直接操作されることを意図していなかったためです。
しかし、統合されたネットワークポーラーがデッドラインを処理するためには、ネットワークI/O操作のタイムアウトを設定し、そのタイムアウトが発生した際に適切な処理を行う必要があります。この処理には、Goランタイムのタイマー機能、具体的には addtimer
と deltimer
を利用するのが最も効率的です。ネットワークポーラーはランタイムの別の部分に存在するため、これらのタイマー関数にアクセスできる必要がありました。
このコミットでは、以下の変更が行われています。
-
src/pkg/runtime/runtime.h
への宣言追加:void runtime·addtimer(Timer*);
bool runtime·deltimer(Timer*);
これらの行がruntime.h
に追加されました。これにより、Goランタイムの他のCファイルや、GoのコードからCgoを介してこれらの関数を呼び出すことが可能になります。runtime·
プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのものです。 -
src/pkg/runtime/time.goc
からstatic
キーワードの削除:static void addtimer(Timer*);
からstatic
が削除され、void addtimer(Timer*);
となりました。static bool deltimer(Timer*);
からstatic
が削除され、bool deltimer(Timer*);
となりました。 これにより、これらの関数はファイルスコープの制限がなくなり、runtime.h
で宣言されたグローバルなシンボルとして扱われるようになります。 -
time.goc
内での関数の再定義と呼び出し箇所の変更:addtimer
とdeltimer
の実装自体はtime.goc
内に残りますが、それらの関数定義にはruntime·
プレフィックスが付けられ、runtime·addtimer
およびruntime·deltimer
として外部に公開されます。startTimer
関数内でのaddtimer(t);
の呼び出しがruntime·addtimer(t);
に変更されました。stopTimer
関数内でのdeltimer(t);
の呼び出しがruntime·deltimer(t);
に変更されました。 これは、time.goc
内部からも、外部に公開されたruntime·
プレフィックス付きの関数を呼び出すように統一するためです。また、runtime·addtimer
の実装では、タイマーヒープへのアクセスを保護するためにruntime·lock
とruntime·unlock
が追加されています。これは、複数のgoroutineから同時にタイマーが追加・削除される可能性があるため、スレッドセーフティを確保するためです。
この変更により、ネットワークポーラーは runtime·addtimer
と runtime·deltimer
を呼び出すことで、ネットワークI/O操作に関連するデッドラインを効率的に設定・解除できるようになります。例えば、ネットワーク読み込み操作が開始される際に runtime·addtimer
を呼び出してタイムアウトを設定し、読み込みが完了した際には runtime·deltimer
を呼び出してタイマーを解除するといった連携が可能になります。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -761,6 +761,8 @@ int64 runtime·cputicks(void);\
int64 runtime·tickspersecond(void);\
void runtime·blockevent(int64, int32);\
extern int64 runtime·blockprofilerate;\
+void runtime·addtimer(Timer*);\
+bool runtime·deltimer(Timer*);\
#pragma varargck argpos runtime·printf 1
#pragma varargck ttype "d" int32
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index 2babb173df..6de989f515 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -15,7 +15,6 @@ package time
static Timers timers;\
static void addtimer(Timer*);\
-static bool deltimer(Timer*);\
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -31,15 +30,13 @@ func Sleep(ns int64) {\
func startTimer(t *Timer) {\
if(raceenabled)\
runtime·racerelease(t);\
-\truntime·lock(&timers);\
-\taddtimer(t);\
-\truntime·unlock(&timers);\
+\truntime·addtimer(t);\
}\
// stopTimer removes t from the timer heap if it is there.
// It returns true if t was removed, false if t wasn't even there.
func stopTimer(t *Timer) (stopped bool) {\
-\tstopped = deltimer(t);\
+\tstopped = runtime·deltimer(t);\
}\
// C runtime.
@@ -79,6 +76,14 @@ runtime·tsleep(int64 ns, int8 *reason)\
static FuncVal timerprocv = {timerproc};\
+void
+runtime·addtimer(Timer *t)\
+{\
+\truntime·lock(&timers);\
+\taddtimer(t);\
+\truntime·unlock(&timers);\
+}\
+\
// Add a timer to the heap and start or kick the timer proc
// if the new timer is earlier than any of the others.
static void
@@ -121,8 +126,8 @@ addtimer(Timer *t)\
// Delete timer t from the heap.
// Do not need to update the timerproc:
// if it wakes up early, no big deal.\
-static bool
-deltimer(Timer *t)\
+bool
+runtime·deltimer(Timer *t)\
{\
\tint32 i;\
\
コアとなるコードの解説
src/pkg/runtime/runtime.h
の変更
- 追加:
Goランタイムのグローバルヘッダーファイルであるvoid runtime·addtimer(Timer*); bool runtime·deltimer(Timer*);
runtime.h
に、runtime·addtimer
とruntime·deltimer
の関数プロトタイプが追加されました。これにより、これらの関数がGoランタイムの他のCファイルや、GoのコードからCgoを介して呼び出し可能になります。runtime·
プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのGoの内部的な命名規則です。
src/pkg/runtime/time.goc
の変更
-
static
キーワードの削除:-static bool deltimer(Timer*);
deltimer
関数の前方宣言からstatic
キーワードが削除されました。これにより、deltimer
はファイルスコープに限定されず、外部からアクセス可能なグローバルシンボルとなります。addtimer
も同様に、その定義からstatic
が削除されています。 -
startTimer
およびstopTimer
内の呼び出し変更:- runtime·lock(&timers); - addtimer(t); - runtime·unlock(&timers); + runtime·addtimer(t);
- stopped = deltimer(t); + stopped = runtime·deltimer(t);
Goの
time
パッケージのAPIであるstartTimer
とstopTimer
の内部で、タイマー管理関数を呼び出す箇所が変更されました。以前はaddtimer
やdeltimer
を直接呼び出していましたが、この変更後はruntime·addtimer
とruntime·deltimer
という、runtime.h
で宣言され外部に公開された関数を呼び出すように統一されました。これにより、time.goc
内部からも、外部に公開されたインターフェースを通じてタイマー操作を行うことになります。 -
runtime·addtimer
の追加とdeltimer
の変更:+void +runtime·addtimer(Timer *t) +{ + runtime·lock(&timers); + addtimer(t); + runtime·unlock(&timers); +}
runtime·addtimer
という新しい関数が追加されました。この関数は、内部的なaddtimer
関数を呼び出す前にtimers
ヒープをロックし、呼び出し後にアンロックすることで、複数のgoroutineからの同時アクセスに対するスレッドセーフティを確保しています。-static bool -deltimer(Timer *t) +bool +runtime·deltimer(Timer *t)
deltimer
関数の定義からstatic
キーワードが削除され、関数名がruntime·deltimer
に変更されました。これにより、この関数も外部から直接呼び出し可能になります。
これらの変更により、Goランタイムのタイマー管理機能がモジュール化され、ネットワークポーラーのような他のランタイムコンポーネントから、安全かつ効率的にタイマーを操作できるようになりました。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/d0c11d20b8eeebcdf2ab597c3b494e40287f9c9b
- 関連するGo CL (Change List) - 統合ネットワークポーラーの準備: https://golang.org/cl/7326051
- このコミットのGo CL: https://golang.org/cl/7446047
参考にした情報源リンク
- Goのランタイムに関する一般的な情報:
- The Go Programming Language Specification: https://go.dev/ref/spec
- Go's runtime package documentation: https://pkg.go.dev/runtime
- C言語の
static
キーワードについて:- C static keyword: https://www.geeksforgeeks.org/static-keyword-in-c/
- Goのネットワークポーラーに関する情報 (より詳細な理解のため):
- Go's netpoller: https://go.dev/src/runtime/netpoll.go (現在の実装)
- Go issue for integrated network poller (関連する議論): https://github.com/golang/go/issues/4316 (古い情報を含む可能性あり)
- GoのCgoに関する情報 (GoとCの連携について):
- Go and Cgo: https://go.dev/blog/cgo
- Go's cgo documentation: https://pkg.go.dev/cmd/cgo
- Goのタイマーに関する情報:
- Go's time package documentation: https://pkg.go.dev/time
- Go's runtime/time.goc (現在の実装): https://go.dev/src/runtime/time.go (Go 1.4以降はGo言語で書かれているため、当時のGoCとは異なる)
- Go's runtime/runtime.h (現在の実装): https://go.dev/src/runtime/runtime.h# [インデックス 15589] ファイルの概要
このコミットは、Goランタイムにおけるタイマー管理関数 addtimer
および deltimer
の宣言を src/pkg/runtime/runtime.h
ヘッダーファイルに移動し、これらの関数をGoランタイムの外部から呼び出し可能にする変更です。これにより、Goランタイムの内部実装であったタイマー関数が、より広範なシステムコンポーネント、特にネットワークポーラーから利用できるようになります。
コミット
commit d0c11d20b8eeebcdf2ab597c3b494e40287f9c9b
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Tue Mar 5 09:38:15 2013 +0200
runtime: declare addtimer/deltimer in runtime.h
In preparation for integrated network poller
(https://golang.org/cl/7326051),
this is required to handle deadlines.
R=golang-dev, remyoudompheng, rsc
CC=golang-dev
https://golang.org/cl/7446047
---
src/pkg/runtime/runtime.h | 2 ++\
src/pkg/runtime/time.goc | 19 ++++++++++++-------\
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 9b43f29a59..585d6a536e 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -761,6 +761,8 @@ int64 runtime·cputicks(void);\
int64 runtime·tickspersecond(void);\
void runtime·blockevent(int64, int32);\
extern int64 runtime·blockprofilerate;\
+void runtime·addtimer(Timer*);\
+bool runtime·deltimer(Timer*);\
#pragma varargck argpos runtime·printf 1
#pragma varargck ttype "d" int32
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index 2babb173df..6de989f515 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -15,7 +15,6 @@ package time
static Timers timers;\
static void addtimer(Timer*);\
-static bool deltimer(Timer*);\
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -31,15 +30,13 @@ func Sleep(ns int64) {\
func startTimer(t *Timer) {\
if(raceenabled)\
runtime·racerelease(t);\
-\truntime·lock(&timers);\
-\taddtimer(t);\
-\truntime·unlock(&timers);\
+\truntime·addtimer(t);\
}\
// stopTimer removes t from the timer heap if it is there.
// It returns true if t was removed, false if t wasn't even there.
func stopTimer(t *Timer) (stopped bool) {\
-\tstopped = deltimer(t);\
+\tstopped = runtime·deltimer(t);\
}\
// C runtime.
@@ -79,6 +76,14 @@ runtime·tsleep(int64 ns, int8 *reason)\
static FuncVal timerprocv = {timerproc};\
+void
+runtime·addtimer(Timer *t)\
+{\
+\truntime·lock(&timers);\
+\taddtimer(t);\
+\truntime·unlock(&timers);\
+}\
+\
// Add a timer to the heap and start or kick the timer proc
// if the new timer is earlier than any of the others.
static void
@@ -121,8 +126,8 @@ addtimer(Timer *t)\
// Delete timer t from the heap.
// Do not need to update the timerproc:
// if it wakes up early, no big deal.\
-static bool
-deltimer(Timer *t)\
+bool
+runtime·deltimer(Timer *t)\
{\
\tint32 i;\
\
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d0c11d20b8eeebcdf2ab597c3b494e40287f9c9b
元コミット内容
Goランタイムにおいて、addtimer
および deltimer
関数を runtime.h
で宣言する。これは、統合されたネットワークポーラー(https://golang.org/cl/7326051)の準備のためであり、デッドラインを処理するために必要となる。
変更の背景
このコミットの主な背景は、GoランタイムにおけるネットワークI/Oの効率化と、それに伴うデッドライン(タイムアウト)処理の改善です。Goのランタイムは、goroutineのスケジューリング、メモリ管理、ガベージコレクション、そしてネットワークI/Oの処理など、Goプログラムの実行を支える多くの低レベルな機能を提供します。
コミットメッセージに記載されている「integrated network poller (https://golang.org/cl/7326051)」は、GoのネットワークI/O処理をより効率的にするための重要な変更を指しています。従来のGoのネットワークI/Oは、システムコールをブロックする可能性があり、多数のコネクションを扱う際にパフォーマンスのボトルネックとなることがありました。統合されたネットワークポーラーは、epoll
(Linux), kqueue
(FreeBSD/macOS), IOCP
(Windows) などのOSネイティブな非同期I/Oメカニズムを活用し、単一のスレッドで多数のネットワークイベントを効率的に監視・処理できるようにするものです。これにより、Goのネットワークアプリケーションのスケーラビリティとパフォーマンスが大幅に向上します。
この新しいネットワークポーラーは、ネットワーク操作にデッドライン(タイムアウト)を設定する機能も必要とします。例えば、ネットワーク接続の確立やデータの送受信が一定時間内に完了しない場合に、操作を中断してエラーを返すといった処理です。このようなデッドライン処理を実現するためには、Goランタイムのタイマー機能とネットワークポーラーが密接に連携する必要があります。
addtimer
と deltimer
は、Goランタイム内部でタイマーを管理するための関数です。これらは、time.Sleep
や time.After
などのGoのタイマー機能の基盤となっています。統合されたネットワークポーラーがデッドラインを処理するためには、これらのタイマー管理関数を直接呼び出す必要が生じました。しかし、これらは元々 static
関数として time.goc
内でのみ利用可能でした。このコミットは、これらの関数を runtime.h
で宣言し、runtime·
プレフィックスを付けて外部からアクセス可能にすることで、ネットワークポーラーがタイマーを操作できるようにするための準備作業です。
前提知識の解説
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理するC言語で書かれた部分です。Goのプログラムは、コンパイルされると、Goランタイムとリンクされ、その上で動作します。ランタイムは、以下のような重要な役割を担っています。
- Goroutineスケジューリング: 多数の軽量スレッドであるgoroutineを効率的にOSスレッドにマッピングし、実行を切り替えます。
- メモリ管理: ヒープメモリの割り当てと解放、ガベージコレクション(GC)を行います。
- チャネル通信: goroutine間の安全な通信メカニズムであるチャネルの実装を提供します。
- ネットワークI/O: ネットワーク操作を効率的に処理します。
- システムコールインターフェース: OSの機能にアクセスするためのインターフェースを提供します。
Goランタイムのソースコードは、Go言語のソースツリーの src/runtime
ディレクトリにあります。多くの部分はC言語(またはGoの特殊なC言語方言であるGoC
)で書かれています。
runtime.h
src/pkg/runtime/runtime.h
は、GoランタイムのC言語部分で定義された関数や変数、型などを、他のランタイムのCファイルやGoのコードから利用できるようにするためのヘッダーファイルです。C言語におけるヘッダーファイルと同様に、関数のプロトタイプ宣言や構造体の定義などが含まれています。GoのコードからC言語のランタイム関数を呼び出す際には、このヘッダーファイルに宣言されている必要があります。
time.goc
src/pkg/runtime/time.goc
は、Goランタイムにおける時間管理とタイマー機能の実装を含むファイルです。.goc
拡張子は、Goのランタイムで使われるC言語の特殊な方言(GoC)を示します。このファイルには、タイマーのヒープ管理、time.Sleep
や time.After
のようなGoの標準ライブラリのタイマー機能の低レベルな実装が含まれています。
static
キーワード (C言語)
C言語における static
キーワードは、関数や変数に対して異なる意味を持ちます。このコミットの文脈では、関数に対する static
キーワードが重要です。
- 関数に対する
static
: 関数にstatic
を付けると、その関数のスコープは、その関数が定義されているファイル内に限定されます。つまり、他のファイルからはその関数を直接呼び出すことができません。これは、内部的なヘルパー関数や、特定のファイル内でのみ使用されるべき関数を隠蔽するために使用されます。
このコミットでは、addtimer
と deltimer
から static
キーワードが削除されています。これは、これらの関数が time.goc
の外部からもアクセス可能になることを意味します。
runtime·
プレフィックス
GoランタイムのC言語部分では、Goのコードから呼び出されるC関数や、GoのコードとCのコード間で共有されるシンボルには、慣習的に runtime·
というプレフィックスが付けられます。これは、GoのリンカーがGoの関数名とCの関数名を区別し、正しくリンクするために使用されるGoの内部的な命名規則の一部です。このプレフィックスが付いた関数は、Goのコードから runtime.FunctionName
のように呼び出すことができます。
ネットワークポーラー (Network Poller)
ネットワークポーラーは、複数のネットワークI/O操作を効率的に監視し、準備ができたイベント(データが読み取り可能になった、書き込み可能になったなど)を通知するメカニズムです。OSが提供する非同期I/O API(Linuxのepoll
、macOS/FreeBSDのkqueue
、WindowsのIOCP
など)を利用して実装されます。Goのランタイムは、このポーラーを使って、多数のネットワークコネクションを扱う際に、各コネクションがブロックするのを待つのではなく、イベントドリブンな方法で処理することで、高い並行性とスケーラビリティを実現しています。
技術的詳細
このコミットの技術的な核心は、Goランタイムのタイマー管理関数 addtimer
と deltimer
の可視性(visibility)を変更することにあります。
元々、time.goc
内で static void addtimer(Timer*);
および static bool deltimer(Timer*);
と宣言されていたこれらの関数は、time.goc
ファイル内からのみ呼び出すことができました。これは、これらの関数が time.goc
の内部実装の詳細であり、外部から直接操作されることを意図していなかったためです。
しかし、統合されたネットワークポーラーがデッドラインを処理するためには、ネットワークI/O操作のタイムアウトを設定し、そのタイムアウトが発生した際に適切な処理を行う必要があります。この処理には、Goランタイムのタイマー機能、具体的には addtimer
と deltimer
を利用するのが最も効率的です。ネットワークポーラーはランタイムの別の部分に存在するため、これらのタイマー関数にアクセスできる必要がありました。
このコミットでは、以下の変更が行われています。
-
src/pkg/runtime/runtime.h
への宣言追加:void runtime·addtimer(Timer*);
bool runtime·deltimer(Timer*);
これらの行がruntime.h
に追加されました。これにより、Goランタイムの他のCファイルや、GoのコードからCgoを介してこれらの関数を呼び出すことが可能になります。runtime·
プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのものです。 -
src/pkg/runtime/time.goc
からstatic
キーワードの削除:static void addtimer(Timer*);
からstatic
が削除され、void addtimer(Timer*);
となりました。static bool deltimer(Timer*);
からstatic
が削除され、bool deltimer(Timer*);
となりました。 これにより、これらの関数はファイルスコープの制限がなくなり、runtime.h
で宣言されたグローバルなシンボルとして扱われるようになります。 -
time.goc
内での関数の再定義と呼び出し箇所の変更:addtimer
とdeltimer
の実装自体はtime.goc
内に残りますが、それらの関数定義にはruntime·
プレフィックスが付けられ、runtime·addtimer
およびruntime·deltimer
として外部に公開されます。startTimer
関数内でのaddtimer(t);
の呼び出しがruntime·addtimer(t);
に変更されました。stopTimer
関数内でのdeltimer(t);
の呼び出しがruntime·deltimer(t);
に変更されました。 これは、time.goc
内部からも、外部に公開されたruntime·
プレフィックス付きの関数を呼び出すように統一するためです。また、runtime·addtimer
の実装では、タイマーヒープへのアクセスを保護するためにruntime·lock
とruntime·unlock
が追加されています。これは、複数のgoroutineから同時にタイマーが追加・削除される可能性があるため、スレッドセーフティを確保するためです。
この変更により、ネットワークポーラーは runtime·addtimer
と runtime·deltimer
を呼び出すことで、ネットワークI/O操作に関連するデッドラインを効率的に設定・解除できるようになります。例えば、ネットワーク読み込み操作が開始される際に runtime·addtimer
を呼び出してタイムアウトを設定し、読み込みが完了した際には runtime·deltimer
を呼び出してタイマーを解除するといった連携が可能になります。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -761,6 +761,8 @@ int64 runtime·cputicks(void);\
int64 runtime·tickspersecond(void);\
void runtime·blockevent(int64, int32);\
extern int64 runtime·blockprofilerate;\
+void runtime·addtimer(Timer*);\
+bool runtime·deltimer(Timer*);\
#pragma varargck argpos runtime·printf 1
#pragma varargck ttype "d" int32
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index 2babb173df..6de989f515 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -15,7 +15,6 @@ package time
static Timers timers;\
static void addtimer(Timer*);\
-static bool deltimer(Timer*);\
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -31,15 +30,13 @@ func Sleep(ns int64) {\
func startTimer(t *Timer) {\
if(raceenabled)\
runtime·racerelease(t);\
-\truntime·lock(&timers);\
-\taddtimer(t);\
-\truntime·unlock(&timers);\
+\truntime·addtimer(t);\
}\
// stopTimer removes t from the timer heap if it is there.
// It returns true if t was removed, false if t wasn't even there.
func stopTimer(t *Timer) (stopped bool) {\
-\tstopped = deltimer(t);\
+\tstopped = runtime·deltimer(t);\
}\
// C runtime.
@@ -79,6 +76,14 @@ runtime·tsleep(int64 ns, int8 *reason)\
static FuncVal timerprocv = {timerproc};\
+void
+runtime·addtimer(Timer *t)\
+{\
+\truntime·lock(&timers);\
+\taddtimer(t);\
+\truntime·unlock(&timers);\
+}\
+\
// Add a timer to the heap and start or kick the timer proc
// if the new timer is earlier than any of the others.
static void
@@ -121,8 +126,8 @@ addtimer(Timer *t)\
// Delete timer t from the heap.
// Do not need to update the timerproc:
// if it wakes up early, no big deal.\
-static bool
-deltimer(Timer *t)\
+bool
+runtime·deltimer(Timer *t)\
{\
\tint32 i;\
\
コアとなるコードの解説
src/pkg/runtime/runtime.h
の変更
- 追加:
Goランタイムのグローバルヘッダーファイルであるvoid runtime·addtimer(Timer*); bool runtime·deltimer(Timer*);
runtime.h
に、runtime·addtimer
とruntime·deltimer
の関数プロトタイプが追加されました。これにより、これらの関数がGoランタイムの他のCファイルや、GoのコードからCgoを介して呼び出し可能になります。runtime·
プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのGoの内部的な命名規則です。
src/pkg/runtime/time.goc
の変更
-
static
キーワードの削除:-static bool deltimer(Timer*);
deltimer
関数の前方宣言からstatic
キーワードが削除されました。これにより、deltimer
はファイルスコープに限定されず、外部からアクセス可能なグローバルシンボルとなります。addtimer
も同様に、その定義からstatic
が削除されています。 -
startTimer
およびstopTimer
内の呼び出し変更:- runtime·lock(&timers); - addtimer(t); - runtime·unlock(&timers); + runtime·addtimer(t);
- stopped = deltimer(t); + stopped = runtime·deltimer(t);
Goの
time
パッケージのAPIであるstartTimer
とstopTimer
の内部で、タイマー管理関数を呼び出す箇所が変更されました。以前はaddtimer
やdeltimer
を直接呼び出していましたが、この変更後はruntime·addtimer
とruntime·deltimer
という、runtime.h
で宣言され外部に公開された関数を呼び出すように統一されました。これにより、time.goc
内部からも、外部に公開されたインターフェースを通じてタイマー操作を行うことになります。 -
runtime·addtimer
の追加とdeltimer
の変更:+void +runtime·addtimer(Timer *t) +{ + runtime·lock(&timers);\ + addtimer(t);\ + runtime·unlock(&timers);\
- }
```
runtime·addtimer
という新しい関数が追加されました。この関数は、内部的なaddtimer
関数を呼び出す前にtimers
ヒープをロックし、呼び出し後にアンロックすることで、複数のgoroutineからの同時アクセスに対するスレッドセーフティを確保しています。
```diff
-static bool
-deltimer(Timer *t)
+bool
+runtime·deltimer(Timer *t)
```
`deltimer` 関数の定義から `static` キーワードが削除され、関数名が `runtime·deltimer` に変更されました。これにより、この関数も外部から直接呼び出し可能になります。
これらの変更により、Goランタイムのタイマー管理機能がモジュール化され、ネットワークポーラーのような他のランタイムコンポーネントから、安全かつ効率的にタイマーを操作できるようになりました。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/d0c11d20b8eeebcdf2ab597c3b494e40287f9c9b
- 関連するGo CL (Change List) - 統合ネットワークポーラーの準備: https://golang.org/cl/7326051
- このコミットのGo CL: https://golang.org/cl/7446047
参考にした情報源リンク
- Goのランタイムに関する一般的な情報:
- The Go Programming Language Specification: https://go.dev/ref/spec
- Go's runtime package documentation: https://pkg.go.dev/runtime
- C言語の
static
キーワードについて:- C static keyword: https://www.geeksforgeeks.org/static-keyword-in-c/
- Goのネットワークポーラーに関する情報 (より詳細な理解のため):
- Go's netpoller: https://go.dev/src/runtime/netpoll.go (現在の実装)
- Go issue for integrated network poller (関連する議論): https://github.com/golang/go/issues/4316 (古い情報を含む可能性あり)
- GoのCgoに関する情報 (GoとCの連携について):
- Go and Cgo: https://go.dev/blog/cgo
- Go's cgo documentation: https://pkg.go.dev/cmd/cgo
- Goのタイマーに関する情報:
- Go's time package documentation: https://pkg.go.dev/time
- Go's runtime/time.goc (現在の実装): https://go.dev/src/runtime/time.go (Go 1.4以降はGo言語で書かれているため、当時のGoCとは異なる)
- Go's runtime/runtime.h (現在の実装): https://go.dev/src/runtime/runtime.h