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

[インデックス 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関数内で行われています。

  1. if(m->mcache == nil):

    • これは、現在のOSスレッド(M)に関連付けられているmcachenil(つまり、割り当てられていないか、無効な状態)であるかどうかをチェックする条件文です。
    • コミットメッセージのコメントにあるように、「シグナルハンドラやthrowから呼び出された場合に発生しうる」状況を想定しています。これらのコンテキストでは、通常のGoコードの実行パスとは異なり、mcacheが適切に設定されていない可能性があります。
  2. m->mcache = runtime·allocmcache();:

    • もしmcachenilであれば、runtime·allocmcache()関数が呼び出され、新しいmcacheが割り当てられて、現在のMのmcacheポインタに設定されます。
    • これにより、パニック処理の残りの部分が、メモリ割り当てを必要とする場合に安全に実行できるようになります。例えば、パニックメッセージの文字列を構築したり、スタックトレースを記録したりする際に、一時的なメモリ割り当てが必要になることがあります。この割り当てがmcacheを介して行われるため、mcacheが有効であることが不可欠です。

この変更は、Goランタイムの堅牢性を高め、特に非同期的なエラー処理パスにおける潜在的なクラッシュを防ぐための防御的な措置です。これは、Go 1.2で導入された新しいスケジューラへのスムーズな移行を可能にするための、より大きな変更セットの一部でした。

関連リンク

参考にした情報源リンク

  • 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関数内で行われています。

  1. if(m->mcache == nil):

    • これは、現在のOSスレッド(M)に関連付けられているmcachenil(つまり、割り当てられていないか、無効な状態)であるかどうかをチェックする条件文です。
    • コミットメッセージのコメントにあるように、「シグナルハンドラやthrowから呼び出された場合に発生しうる」状況を想定しています。これらのコンテキストでは、通常のGoコードの実行パスとは異なり、mcacheが適切に設定されていない可能性があります。
  2. m->mcache = runtime·allocmcache();:

    • もしmcachenilであれば、runtime·allocmcache()関数が呼び出され、新しいmcacheが割り当てられて、現在のMのmcacheポインタに設定されます。
    • これにより、パニック処理の残りの部分が、メモリ割り当てを必要とする場合に安全に実行できるようになります。例えば、パニックメッセージの文字列を構築したり、スタックトレースを記録したりする際に、一時的なメモリ割り当てが必要になることがあります。この割り当てがmcacheを介して行われるため、mcacheが有効であることが不可欠です。

この変更は、Goランタイムの堅牢性を高め、特に非同期的なエラー処理パスにおける潜在的なクラッシュを防ぐための防御的な措置です。これは、Go 1.1で導入された新しいスケジューラへのスムーズな移行を可能にするための、より大きな変更セットの一部でした。

関連リンク

参考にした情報源リンク

  • Goソースコード (runtime/panic.c, runtime/mcache.goなど)
  • Go公式ドキュメント
  • Goに関する技術ブログや解説記事 (特にGoスケジューラに関するもの)
  • Goのコミット履歴と関連するコードレビュー (CL)
  • Web検索: "Go new scheduler 2013"