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

[インデックス 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ランタイムのタイマー機能とネットワークポーラーが密接に連携する必要があります。

addtimerdeltimer は、Goランタイム内部でタイマーを管理するための関数です。これらは、time.Sleeptime.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.Sleeptime.After のようなGoの標準ライブラリのタイマー機能の低レベルな実装が含まれています。

static キーワード (C言語)

C言語における static キーワードは、関数や変数に対して異なる意味を持ちます。このコミットの文脈では、関数に対する static キーワードが重要です。

  • 関数に対する static: 関数に static を付けると、その関数のスコープは、その関数が定義されているファイル内に限定されます。つまり、他のファイルからはその関数を直接呼び出すことができません。これは、内部的なヘルパー関数や、特定のファイル内でのみ使用されるべき関数を隠蔽するために使用されます。

このコミットでは、addtimerdeltimer から 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ランタイムのタイマー管理関数 addtimerdeltimer の可視性(visibility)を変更することにあります。

元々、time.goc 内で static void addtimer(Timer*); および static bool deltimer(Timer*); と宣言されていたこれらの関数は、time.goc ファイル内からのみ呼び出すことができました。これは、これらの関数が time.goc の内部実装の詳細であり、外部から直接操作されることを意図していなかったためです。

しかし、統合されたネットワークポーラーがデッドラインを処理するためには、ネットワークI/O操作のタイムアウトを設定し、そのタイムアウトが発生した際に適切な処理を行う必要があります。この処理には、Goランタイムのタイマー機能、具体的には addtimerdeltimer を利用するのが最も効率的です。ネットワークポーラーはランタイムの別の部分に存在するため、これらのタイマー関数にアクセスできる必要がありました。

このコミットでは、以下の変更が行われています。

  1. src/pkg/runtime/runtime.h への宣言追加: void runtime·addtimer(Timer*); bool runtime·deltimer(Timer*); これらの行が runtime.h に追加されました。これにより、Goランタイムの他のCファイルや、GoのコードからCgoを介してこれらの関数を呼び出すことが可能になります。runtime· プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのものです。

  2. src/pkg/runtime/time.goc から static キーワードの削除: static void addtimer(Timer*); から static が削除され、void addtimer(Timer*); となりました。 static bool deltimer(Timer*); から static が削除され、bool deltimer(Timer*); となりました。 これにより、これらの関数はファイルスコープの制限がなくなり、runtime.h で宣言されたグローバルなシンボルとして扱われるようになります。

  3. time.goc 内での関数の再定義と呼び出し箇所の変更: addtimerdeltimer の実装自体は 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·lockruntime·unlock が追加されています。これは、複数のgoroutineから同時にタイマーが追加・削除される可能性があるため、スレッドセーフティを確保するためです。

この変更により、ネットワークポーラーは runtime·addtimerruntime·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 の変更

  • 追加:
    void	runtime·addtimer(Timer*);
    bool	runtime·deltimer(Timer*);
    
    Goランタイムのグローバルヘッダーファイルである runtime.h に、runtime·addtimerruntime·deltimer の関数プロトタイプが追加されました。これにより、これらの関数がGoランタイムの他のCファイルや、GoのコードからCgoを介して呼び出し可能になります。runtime· プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのGoの内部的な命名規則です。

src/pkg/runtime/time.goc の変更

  1. static キーワードの削除:

    -static bool deltimer(Timer*);
    

    deltimer 関数の前方宣言から static キーワードが削除されました。これにより、deltimer はファイルスコープに限定されず、外部からアクセス可能なグローバルシンボルとなります。addtimer も同様に、その定義から static が削除されています。

  2. startTimer および stopTimer 内の呼び出し変更:

    -	runtime·lock(&timers);
    -	addtimer(t);
    -	runtime·unlock(&timers);
    +	runtime·addtimer(t);
    
    -	stopped = deltimer(t);
    +	stopped = runtime·deltimer(t);
    

    Goの time パッケージのAPIである startTimerstopTimer の内部で、タイマー管理関数を呼び出す箇所が変更されました。以前は addtimerdeltimer を直接呼び出していましたが、この変更後は runtime·addtimerruntime·deltimer という、runtime.h で宣言され外部に公開された関数を呼び出すように統一されました。これにより、time.goc 内部からも、外部に公開されたインターフェースを通じてタイマー操作を行うことになります。

  3. 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ランタイムのタイマー管理機能がモジュール化され、ネットワークポーラーのような他のランタイムコンポーネントから、安全かつ効率的にタイマーを操作できるようになりました。

関連リンク

参考にした情報源リンク

このコミットは、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ランタイムのタイマー機能とネットワークポーラーが密接に連携する必要があります。

addtimerdeltimer は、Goランタイム内部でタイマーを管理するための関数です。これらは、time.Sleeptime.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.Sleeptime.After のようなGoの標準ライブラリのタイマー機能の低レベルな実装が含まれています。

static キーワード (C言語)

C言語における static キーワードは、関数や変数に対して異なる意味を持ちます。このコミットの文脈では、関数に対する static キーワードが重要です。

  • 関数に対する static: 関数に static を付けると、その関数のスコープは、その関数が定義されているファイル内に限定されます。つまり、他のファイルからはその関数を直接呼び出すことができません。これは、内部的なヘルパー関数や、特定のファイル内でのみ使用されるべき関数を隠蔽するために使用されます。

このコミットでは、addtimerdeltimer から 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ランタイムのタイマー管理関数 addtimerdeltimer の可視性(visibility)を変更することにあります。

元々、time.goc 内で static void addtimer(Timer*); および static bool deltimer(Timer*); と宣言されていたこれらの関数は、time.goc ファイル内からのみ呼び出すことができました。これは、これらの関数が time.goc の内部実装の詳細であり、外部から直接操作されることを意図していなかったためです。

しかし、統合されたネットワークポーラーがデッドラインを処理するためには、ネットワークI/O操作のタイムアウトを設定し、そのタイムアウトが発生した際に適切な処理を行う必要があります。この処理には、Goランタイムのタイマー機能、具体的には addtimerdeltimer を利用するのが最も効率的です。ネットワークポーラーはランタイムの別の部分に存在するため、これらのタイマー関数にアクセスできる必要がありました。

このコミットでは、以下の変更が行われています。

  1. src/pkg/runtime/runtime.h への宣言追加: void runtime·addtimer(Timer*); bool runtime·deltimer(Timer*); これらの行が runtime.h に追加されました。これにより、Goランタイムの他のCファイルや、GoのコードからCgoを介してこれらの関数を呼び出すことが可能になります。runtime· プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのものです。

  2. src/pkg/runtime/time.goc から static キーワードの削除: static void addtimer(Timer*); から static が削除され、void addtimer(Timer*); となりました。 static bool deltimer(Timer*); から static が削除され、bool deltimer(Timer*); となりました。 これにより、これらの関数はファイルスコープの制限がなくなり、runtime.h で宣言されたグローバルなシンボルとして扱われるようになります。

  3. time.goc 内での関数の再定義と呼び出し箇所の変更: addtimerdeltimer の実装自体は 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·lockruntime·unlock が追加されています。これは、複数のgoroutineから同時にタイマーが追加・削除される可能性があるため、スレッドセーフティを確保するためです。

この変更により、ネットワークポーラーは runtime·addtimerruntime·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 の変更

  • 追加:
    void	runtime·addtimer(Timer*);
    bool	runtime·deltimer(Timer*);
    
    Goランタイムのグローバルヘッダーファイルである runtime.h に、runtime·addtimerruntime·deltimer の関数プロトタイプが追加されました。これにより、これらの関数がGoランタイムの他のCファイルや、GoのコードからCgoを介して呼び出し可能になります。runtime· プレフィックスは、GoのリンカーがこれらのシンボルをGoの関数として認識するためのGoの内部的な命名規則です。

src/pkg/runtime/time.goc の変更

  1. static キーワードの削除:

    -static bool deltimer(Timer*);
    

    deltimer 関数の前方宣言から static キーワードが削除されました。これにより、deltimer はファイルスコープに限定されず、外部からアクセス可能なグローバルシンボルとなります。addtimer も同様に、その定義から static が削除されています。

  2. startTimer および stopTimer 内の呼び出し変更:

    -	runtime·lock(&timers);
    -	addtimer(t);
    -	runtime·unlock(&timers);
    +	runtime·addtimer(t);
    
    -	stopped = deltimer(t);
    +	stopped = runtime·deltimer(t);
    

    Goの time パッケージのAPIである startTimerstopTimer の内部で、タイマー管理関数を呼び出す箇所が変更されました。以前は addtimerdeltimer を直接呼び出していましたが、この変更後は runtime·addtimerruntime·deltimer という、runtime.h で宣言され外部に公開された関数を呼び出すように統一されました。これにより、time.goc 内部からも、外部に公開されたインターフェースを通じてタイマー操作を行うことになります。

  3. 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ランタイムのタイマー管理機能がモジュール化され、ネットワークポーラーのような他のランタイムコンポーネントから、安全かつ効率的にタイマーを操作できるようになりました。

関連リンク

参考にした情報源リンク