[インデックス 15328] ファイルの概要
コミット
commit 1e063eea38fe3ecc8bea4e35bc0482fa8100ea80
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Wed Feb 20 21:17:56 2013 +0400
runtime: prepare for M's running w/o mcache
Can not happen ATM. In preparation for the new scheduler.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7388043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1e063eea38fe3ecc8bea4e35bc0482fa8100ea80
元コミット内容
このコミットは、Goランタイムにおいて、M (Machine/OSスレッド) がmcacheなしで動作する状況に備えるための変更です。コミットメッセージには「Can not happen ATM. In preparation for the new scheduler.」とあり、これは現在のGoランタイムの設計では発生しない状況だが、将来導入される新しいスケジューラのために準備的な変更であることを示しています。具体的には、パニック発生時にmcacheがnilである場合に、新たにmcacheを割り当てる処理が追加されています。
変更の背景
この変更の背景には、Goランタイムのスケジューラの大規模な改修計画がありました。Goのスケジューラは、G (Goroutine)、M (Machine/OSスレッド)、P (Processor/論理プロセッサ) という3つのエンティティを基本としています。当時のスケジューラ(M:Nスケジューリング)は、GoroutineをOSスレッドに多重化して実行していましたが、いくつかの性能ボトルネックや複雑性を抱えていました。
特に、メモリ割り当てにおいては、各M(OSスレッド)が自身のローカルキャッシュであるmcache
を持っていました。これは、アロケータのロック競合を減らし、高速なメモリ割り当てを実現するための重要な最適化です。しかし、特定のシナリオ、特にシグナルハンドラやthrow
(Goランタイム内部のエラー処理)のような非同期的なコンテキストからパニックが発生した場合、現在のMが有効なmcache
を持っていない可能性が考慮され始めました。
このコミットは、将来の「新しいスケジューラ」(Go 1.2で導入された新しいスケジューラ、通称「GMPスケジューラ」)への移行を見据えた準備作業の一環です。新しいスケジューラでは、MとPの関連付けがより動的になり、Mが必ずしも常にPにアタッチされ、有効なmcache
を持っているとは限らない状況が発生しうるため、パニック処理のようなクリティカルなパスでmcache
の存在を保証する必要がありました。
前提知識の解説
Goランタイムスケジューラ (M, P, G)
- G (Goroutine): Go言語の軽量な並行処理単位。OSスレッドよりもはるかに軽量で、数百万個作成することも可能です。
- M (Machine): OSスレッドを表します。Goランタイムは、Gを実行するためにMを使用します。MはOSによってスケジュールされます。
- P (Processor): 論理プロセッサを表します。Gを実行するためにMに割り当てられるコンテキストです。Pは、実行可能なGのキューと、メモリ割り当てのための
mcache
を保持します。Go 1.2以前のスケジューラでは、Mが直接mcache
を持っていましたが、新しいスケジューラではPがmcache
を持つようになります。
mcache
mcache
は、Goランタイムのメモリ割り当てシステムにおける重要なコンポーネントです。各M(または新しいスケジューラではP)は、自身のローカルなメモリキャッシュであるmcache
を持ちます。これにより、Goroutineが小さなオブジェクトを割り当てる際に、グローバルなヒープロックを取得する必要がなくなり、アロケーションのパフォーマンスが大幅に向上します。mcache
は、様々なサイズのオブジェクトに対応するフリーリストの集合体として機能します。
パニック (Panic)
Goにおけるパニックは、プログラムの回復不可能なエラー状態を示します。通常、ランタイムエラー(例: nilポインタ参照、配列の範囲外アクセス)や、プログラマが明示的にpanic()
関数を呼び出した場合に発生します。パニックが発生すると、現在のGoroutineの実行が停止し、遅延関数(defer
)が実行されながらスタックがアンワインドされます。最終的に、recover()
によってパニックが捕捉されない限り、プログラムは異常終了します。
シグナルハンドラ
OSシグナルは、プログラムに対して非同期的にイベントを通知するメカニズムです(例: Ctrl+CによるSIGINT、メモリ不正アクセスによるSIGSEGV)。Goランタイムは、これらのシグナルを捕捉し、適切に処理するためのシグナルハンドラを持っています。シグナルハンドラは、通常のGoコードの実行フローとは独立して動作するため、特定のランタイムの状態(例: mcache
の有無)が保証されない場合があります。
技術的詳細
このコミットは、src/pkg/runtime/panic.c
ファイル内のruntime·startpanic
関数に2行のコードを追加しています。
runtime·startpanic
関数は、Goランタイム内でパニック処理が開始される際に呼び出される関数です。この関数は、パニックの初期化、スタックトレースの収集、およびパニックメッセージの出力など、パニック処理の重要なステップを実行します。
追加されたコードは以下の通りです。
+ if(m->mcache == nil) // can happen if called from signal handler or throw
+ m->mcache = runtime·allocmcache();
このコードは、現在のM(OSスレッド)がmcache
を持っていない(m->mcache == nil
)場合に、新しいmcache
を割り当てる(runtime·allocmcache()
を呼び出す)ことを保証します。
なぜこのようなチェックが必要なのでしょうか?
当時のGoランタイムでは、Mは常にPにアタッチされ、Pがmcache
を持つという前提がありました。しかし、シグナルハンドラやthrow
のような特定のコンテキストでは、MがPにアタッチされていない、あるいはPが有効なmcache
を持っていない状態でパニックが発生する可能性がありました。このような状況でm->mcache
にアクセスしようとすると、nilポインタ参照などのクラッシュを引き起こす可能性があります。
この変更は、将来の新しいスケジューラ(Go 1.2で導入されたGMPスケジューラ)への移行を見越したものです。新しいスケジューラでは、MとPの関連付けがより動的になり、Mが必ずしも常にPにアタッチされているとは限りません。そのため、パニック処理のようなクリティカルなパスにおいて、mcache
が確実に利用可能であることを保証するための防御的なプログラミングとしてこのコードが追加されました。これにより、スケジューラの変更によって発生しうる潜在的なクラッシュを防ぎ、ランタイムの堅牢性を高めています。
runtime·allocmcache()
関数は、新しいmcache
構造体を割り当て、それを初期化するGoランタイム内部の関数です。この関数が呼び出されることで、パニック処理がmcache
を必要とする後続の操作(例: エラーメッセージのフォーマットやスタックトレースの割り当て)を安全に実行できるようになります。
コアとなるコードの変更箇所
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index a0651e4ad5..603ff62eb3 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -382,6 +382,8 @@ nomatch:
void
runtime·startpanic(void)
{
+\tif(m->mcache == nil) // can happen if called from signal handler or throw
+\t\tm->mcache = runtime·allocmcache();
\tif(m->dying) {
\t\truntime·printf(\"panic during panic\\n\");
\t\truntime·exit(3);\
コアとなるコードの解説
変更はsrc/pkg/runtime/panic.c
ファイルのruntime·startpanic
関数内で行われています。
-
if(m->mcache == nil)
:- これは、現在のOSスレッド(M)に関連付けられている
mcache
がnil
(つまり、割り当てられていないか、無効な状態)であるかどうかをチェックする条件文です。 - コミットメッセージのコメントにあるように、「シグナルハンドラや
throw
から呼び出された場合に発生しうる」状況を想定しています。これらのコンテキストでは、通常のGoコードの実行パスとは異なり、mcache
が適切に設定されていない可能性があります。
- これは、現在のOSスレッド(M)に関連付けられている
-
m->mcache = runtime·allocmcache();
:- もし
mcache
がnil
であれば、runtime·allocmcache()
関数が呼び出され、新しいmcache
が割り当てられて、現在のMのmcache
ポインタに設定されます。 - これにより、パニック処理の残りの部分が、メモリ割り当てを必要とする場合に安全に実行できるようになります。例えば、パニックメッセージの文字列を構築したり、スタックトレースを記録したりする際に、一時的なメモリ割り当てが必要になることがあります。この割り当てが
mcache
を介して行われるため、mcache
が有効であることが不可欠です。
- もし
この変更は、Goランタイムの堅牢性を高め、特に非同期的なエラー処理パスにおける潜在的なクラッシュを防ぐための防御的な措置です。これは、Go 1.2で導入された新しいスケジューラへのスムーズな移行を可能にするための、より大きな変更セットの一部でした。
関連リンク
- Go 1.2 Release Notes (Scheduler): https://go.dev/doc/go1.2#scheduler
- Go Scheduler: M, P, G (A Visual Guide): https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html (これは一般的な解説であり、コミット当時の状況を直接示すものではありませんが、概念理解に役立ちます)
- Go CL 7388043: https://go.googlesource.com/go/+/7388043 (これはコミットメッセージに記載されているCLのリンクですが、現在はGitHubのコミットページにリダイレクトされます)
参考にした情報源リンク
- Goソースコード (runtime/panic.c, runtime/mcache.goなど)
- Go公式ドキュメント
- Goに関する技術ブログや解説記事 (特にGoスケジューラに関するもの)
- Goのコミット履歴と関連するコードレビュー (CL)
[インデックス 15328] ファイルの概要
コミット
commit 1e063eea38fe3ecc8bea4e35bc0482fa8100ea80
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Wed Feb 20 21:17:56 2013 +0400
runtime: prepare for M's running w/o mcache
Can not happen ATM. In preparation for the new scheduler.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7388043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1e063eea38fe3ecc8bea4e35bc0482fa8100ea80
元コミット内容
このコミットは、Goランタイムにおいて、M (Machine/OSスレッド) がmcacheなしで動作する状況に備えるための変更です。コミットメッセージには「Can not happen ATM. In preparation for the new scheduler.」とあり、これは現在のGoランタイムの設計では発生しない状況だが、将来導入される新しいスケジューラのために準備的な変更であることを示しています。具体的には、パニック発生時にmcacheがnilである場合に、新たにmcacheを割り当てる処理が追加されています。
変更の背景
この変更の背景には、Goランタイムのスケジューラの大規模な改修計画がありました。Goのスケジューラは、G (Goroutine)、M (Machine/OSスレッド)、P (Processor/論理プロセッサ) という3つのエンティティを基本としています。当時のスケジューラ(M:Nスケジューリング)は、GoroutineをOSスレッドに多重化して実行していましたが、いくつかの性能ボトルネックや複雑性を抱えていました。
特に、メモリ割り当てにおいては、各M(OSスレッド)が自身のローカルキャッシュであるmcache
を持っていました。これは、アロケータのロック競合を減らし、高速なメモリ割り当てを実現するための重要な最適化です。しかし、特定のシナリオ、特にシグナルハンドラやthrow
(Goランタイム内部のエラー処理)のような非同期的なコンテキストからパニックが発生した場合、現在のMが有効なmcache
を持っていない可能性が考慮され始めました。
このコミットは、将来の「新しいスケジューラ」(Go 1.1で導入された新しいスケジューラ、通称「GMPスケジューラ」)への移行を見据えた準備作業の一環です。新しいスケジューラでは、MとPの関連付けがより動的になり、Mが必ずしも常にPにアタッチされ、有効なmcache
を持っているとは限らない状況が発生しうるため、パニック処理のようなクリティカルなパスでmcache
の存在を保証する必要がありました。
前提知識の解説
Goランタイムスケジューラ (M, P, G)
- G (Goroutine): Go言語の軽量な並行処理単位。OSスレッドよりもはるかに軽量で、数百万個作成することも可能です。
- M (Machine): OSスレッドを表します。Goランタイムは、Gを実行するためにMを使用します。MはOSによってスケジュールされます。
- P (Processor): 論理プロセッサを表します。Gを実行するためにMに割り当てられるコンテキストです。Pは、実行可能なGのキューと、メモリ割り当てのための
mcache
を保持します。Go 1.1以前のスケジューラでは、Mが直接mcache
を持っていましたが、新しいスケジューラではPがmcache
を持つようになります。
mcache
mcache
は、Goランタイムのメモリ割り当てシステムにおける重要なコンポーネントです。各M(または新しいスケジューラではP)は、自身のローカルなメモリキャッシュであるmcache
を持ちます。これにより、Goroutineが小さなオブジェクトを割り当てる際に、グローバルなヒープロックを取得する必要がなくなり、アロケーションのパフォーマンスが大幅に向上します。mcache
は、様々なサイズのオブジェクトに対応するフリーリストの集合体として機能します。
パニック (Panic)
Goにおけるパニックは、プログラムの回復不可能なエラー状態を示します。通常、ランタイムエラー(例: nilポインタ参照、配列の範囲外アクセス)や、プログラマが明示的にpanic()
関数を呼び出した場合に発生します。パニックが発生すると、現在のGoroutineの実行が停止し、遅延関数(defer
)が実行されながらスタックがアンワインドされます。最終的に、recover()
によってパニックが捕捉されない限り、プログラムは異常終了します。
シグナルハンドラ
OSシグナルは、プログラムに対して非同期的にイベントを通知するメカニズムです(例: Ctrl+CによるSIGINT、メモリ不正アクセスによるSIGSEGV)。Goランタイムは、これらのシグナルを捕捉し、適切に処理するためのシグナルハンドラを持っています。シグナルハンドラは、通常のGoコードの実行フローとは独立して動作するため、特定のランタイムの状態(例: mcache
の有無)が保証されない場合があります。
技術的詳細
このコミットは、src/pkg/runtime/panic.c
ファイル内のruntime·startpanic
関数に2行のコードを追加しています。
runtime·startpanic
関数は、Goランタイム内でパニック処理が開始される際に呼び出される関数です。この関数は、パニックの初期化、スタックトレースの収集、およびパニックメッセージの出力など、パニック処理の重要なステップを実行します。
追加されたコードは以下の通りです。
+ if(m->mcache == nil) // can happen if called from signal handler or throw
+ m->mcache = runtime·allocmcache();
このコードは、現在のM(OSスレッド)がmcache
を持っていない(m->mcache == nil
)場合に、新しいmcache
を割り当てる(runtime·allocmcache()
を呼び出す)ことを保証します。
なぜこのようなチェックが必要なのでしょうか?
当時のGoランタイムでは、Mは常にPにアタッチされ、Pがmcache
を持つという前提がありました。しかし、シグナルハンドラやthrow
のような特定のコンテキストでは、MがPにアタッチされていない、あるいはPが有効なmcache
を持っていない状態でパニックが発生する可能性がありました。このような状況でm->mcache
にアクセスしようとすると、nilポインタ参照などのクラッシュを引き起こす可能性があります。
この変更は、Go 1.1で導入された新しいスケジューラ(GMPスケジューラ)への移行を見越したものです。新しいスケジューラでは、MとPの関連付けがより動的になり、Mが必ずしも常にPにアタッチされているとは限りません。そのため、パニック処理のようなクリティカルなパスにおいて、mcache
が確実に利用可能であることを保証するための防御的なプログラミングとしてこのコードが追加されました。これにより、スケジューラの変更によって発生しうる潜在的なクラッシュを防ぎ、ランタイムの堅牢性を高めています。
runtime·allocmcache()
関数は、新しいmcache
構造体を割り当て、それを初期化するGoランタイム内部の関数です。この関数が呼び出されることで、パニック処理がmcache
を必要とする後続の操作(例: エラーメッセージのフォーマットやスタックトレースの割り当て)を安全に実行できるようになります。
コアとなるコードの変更箇所
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index a0651e4ad5..603ff62eb3 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -382,6 +382,8 @@ nomatch:
void
runtime·startpanic(void)
{
+\tif(m->mcache == nil) // can happen if called from signal handler or throw
+\t\tm->mcache = runtime·allocmcache();
\tif(m->dying) {
\t\truntime·printf(\"panic during panic\\n\");
\t\truntime·exit(3);\
コアとなるコードの解説
変更はsrc/pkg/runtime/panic.c
ファイルのruntime·startpanic
関数内で行われています。
-
if(m->mcache == nil)
:- これは、現在のOSスレッド(M)に関連付けられている
mcache
がnil
(つまり、割り当てられていないか、無効な状態)であるかどうかをチェックする条件文です。 - コミットメッセージのコメントにあるように、「シグナルハンドラや
throw
から呼び出された場合に発生しうる」状況を想定しています。これらのコンテキストでは、通常のGoコードの実行パスとは異なり、mcache
が適切に設定されていない可能性があります。
- これは、現在のOSスレッド(M)に関連付けられている
-
m->mcache = runtime·allocmcache();
:- もし
mcache
がnil
であれば、runtime·allocmcache()
関数が呼び出され、新しいmcache
が割り当てられて、現在のMのmcache
ポインタに設定されます。 - これにより、パニック処理の残りの部分が、メモリ割り当てを必要とする場合に安全に実行できるようになります。例えば、パニックメッセージの文字列を構築したり、スタックトレースを記録したりする際に、一時的なメモリ割り当てが必要になることがあります。この割り当てが
mcache
を介して行われるため、mcache
が有効であることが不可欠です。
- もし
この変更は、Goランタイムの堅牢性を高め、特に非同期的なエラー処理パスにおける潜在的なクラッシュを防ぐための防御的な措置です。これは、Go 1.1で導入された新しいスケジューラへのスムーズな移行を可能にするための、より大きな変更セットの一部でした。
関連リンク
- Go 1.1 Release Notes (Scheduler): https://go.dev/doc/go1.1#scheduler
- Go Scheduler: M, P, G (A Visual Guide): https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html (これは一般的な解説であり、コミット当時の状況を直接示すものではありませんが、概念理解に役立ちます)
- Go CL 7388043: https://go.googlesource.com/go/+/7388043 (これはコミットメッセージに記載されているCLのリンクですが、現在はGitHubのコミットページにリダイレクトされます)
参考にした情報源リンク
- Goソースコード (runtime/panic.c, runtime/mcache.goなど)
- Go公式ドキュメント
- Goに関する技術ブログや解説記事 (特にGoスケジューラに関するもの)
- Goのコミット履歴と関連するコードレビュー (CL)
- Web検索: "Go new scheduler 2013"