[インデックス 16433] ファイルの概要
このコミットは、Goランタイムからbadcallback()
関数群を削除するものです。具体的には、src/pkg/runtime
ディレクトリ内の様々なOS固有のCファイルおよびWindows固有のアセンブリファイルから、CgoコールバックがGoによって作成されていないスレッドで発生した場合にエラーメッセージを出力するbadcallback
関連のコードが削除されています。
コミット
- コミットハッシュ:
a3e0002e6a6d466a505d9d13c75880bf960df512
- Author: Jan Ziak 0xe2.0x9a.0x9b@gmail.com
- Date: Wed May 29 17:51:17 2013 +0200
- コミットメッセージ:
runtime: remove all badcallback() functions R=iant CC=golang-dev https://golang.org/cl/9738046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a3e0002e6a6d466a505d9d13c75880bf960df512
元コミット内容
runtime: remove all badcallback() functions
R=iant
CC=golang-dev
https://golang.org/cl/9738046
変更の背景
このコミットの背景には、GoランタイムにおけるCgo(C言語との相互運用)の進化があります。badcallback()
関数は、CgoコールバックがGoランタイムによって管理されていない(Goが作成していない)スレッドで実行された場合に、エラーメッセージを出力するためのものでした。これは、Goのガベージコレクタやスケジューラが正しく機能するために、GoルーチンがGoランタイムによって管理されるM(Machine)とG(Goroutine)のコンテキスト内で実行される必要があるという制約から来ています。
しかし、GoのCgoメカニズムは時間の経過とともに成熟し、外部Cコードからのコールバックのハンドリングがより堅牢になりました。この変更は、おそらくGoランタイムがCgoコールバックを処理する方法が改善され、badcallback
が検出していたような「Goが作成していないスレッドでのCgoコールバック」というシナリオがもはや発生しないか、あるいは異なる、より適切な方法で処理されるようになったことを示唆しています。これにより、不要になったコードを削除し、ランタイムの複雑さを軽減することが目的と考えられます。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
- Goランタイム (Go Runtime): Goプログラムの実行を管理するシステムです。これには、ガベージコレクタ、スケジューラ(ゴルーチンをOSスレッドにマッピングする)、メモリ管理などが含まれます。Goの並行処理モデルは、OSスレッドの上に抽象化された軽量なゴルーチンによって実現されます。
- Cgo: GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Cgoを使用すると、既存のCライブラリをGoプロジェクトに統合したり、パフォーマンスが重要な部分をCで記述したりすることができます。
- コールバック (Callback): ある関数が別の関数に引数として渡され、その別の関数が特定のイベント発生時や処理完了時に、渡された関数を呼び出すメカニズムです。Cgoの文脈では、CコードがGoの関数を呼び出す場合が「Cgoコールバック」に該当します。
- スレッド (Thread): オペレーティングシステムが管理する実行の単位です。Goランタイムは、複数のゴルーチンを少数のOSスレッドに多重化して実行します。
m
(Machine) とg
(Goroutine): Goランタイムの内部概念です。m
はOSスレッドを表し、g
はゴルーチンを表します。Goスケジューラは、g
をm
上で実行します。CgoコールバックがGoランタイムによって管理されていないスレッド(つまり、Goのm
に紐付けられていないOSスレッド)で発生すると、Goのスケジューラやガベージコレクタが予期しない状態になり、問題を引き起こす可能性がありました。#pragma dataflag 16
/#pragma textflag 7
: Goのコンパイラ(gc)が使用するディレクティブです。#pragma dataflag 16
: このデータセクションにはポインタが含まれていないことを示します。ガベージコレクタがこのセクションをスキャンする必要がないことを意味し、パフォーマンス向上に寄与します。#pragma textflag 7
: この関数はスタックスプリット(Goのスタック拡張メカニズム)を必要としないことを示します。これは、関数が非常に短く、スタックを使い切る可能性がない場合や、Goランタイムのコンテキスト外で実行される場合に用いられます。badcallback
関数は「foreign stack」(Goが管理しないスタック)で実行されるため、このフラグが設定されていました。
技術的詳細
削除されたbadcallback()
関数は、GoランタイムのC部分に実装されていました。その主な目的は、CgoコールバックがGoランタイムが認識しないスレッド(つまり、Goのスケジューラによって管理されていないOSスレッド)で発生した場合に、開発者に対して警告を発することでした。
具体的には、以下の処理を行っていました。
badcallback
という静的なint8
配列(C言語のchar
配列に相当)に、エラーメッセージ"runtime: cgo callback on thread not created by Go.\\n"
を格納していました。runtime·badcallback
という関数が定義されており、この関数は#pragma textflag 7
が付けられていました。これは、この関数がGoのスタックスプリットメカニズムの対象外であり、Goのm
やg
のコンテキストなしに、外部のスタック上で実行されることを意味します。- この関数は、標準エラー出力(ファイルディスクリプタ2)に、上記のエラーメッセージを書き込んでいました。Windows版では、
WriteFile
システムコールを使用していました。
この機能が削除されたということは、GoランタイムがCgoコールバックを処理する内部メカニズムが変更され、もはやこのような「不正な」コールバックがGoランタイムの制御外で発生することがなくなったか、あるいは発生してもGoランタイムがそれを適切に処理できるようになったことを意味します。
考えられる理由としては、以下のような点が挙げられます。
- Cgoコールバックのルーティング改善: Cgoコールバックが常にGoランタイムが管理するスレッドにルーティングされるようになった。例えば、CコードからGo関数が呼び出される際に、Goランタイムが内部的に適切なOSスレッド(Goの
m
に紐付けられたスレッド)にディスパッチするようになった。 - Goスケジューラの堅牢性向上: Goスケジューラが、外部スレッドからのコールバックをより安全に処理できるようになり、以前のような警告が不要になった。
- デバッグ情報の変更: この種のエラーがより根本的なレベルで捕捉され、異なるメカニズム(例えば、パニックやより詳細なランタイムエラー)で報告されるようになった。
この変更は、Goランタイムの内部的なクリーンアップと最適化の一環であり、Cgoの安定性と信頼性が向上したことを示しています。
コアとなるコードの変更箇所
このコミットでは、以下のファイルからbadcallback
に関連するコードが完全に削除されています。
src/pkg/runtime/os_darwin.c
src/pkg/runtime/os_freebsd.c
src/pkg/runtime/os_linux.c
src/pkg/runtime/os_netbsd.c
src/pkg/runtime/os_openbsd.c
src/pkg/runtime/os_plan9.c
src/pkg/runtime/os_windows.c
src/pkg/runtime/sys_windows_386.s
src/pkg/runtime/sys_windows_amd64.s
各os_*.c
ファイルからは、以下のC言語のコードブロックが削除されています。
#pragma dataflag 16 // no pointers
static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\\n";
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badcallback(void)
{
runtime·write(2, badcallback, sizeof badcallback - 1);
}
(os_plan9.c
ではruntime·pwrite
が使われています。)
src/pkg/runtime/os_windows.c
からは、以下のC言語のコードブロックが削除されています。
#pragma dataflag 16 // no pointers
int8 runtime·badcallbackmsg[] = "runtime: cgo callback on thread not created by Go.\\n";
int32 runtime·badcallbacklen = sizeof runtime·badcallbackmsg - 1;
src/pkg/runtime/sys_windows_386.s
からは、以下のx86アセンブリコードブロックが削除されています。
TEXT runtime·badcallback(SB),7,$24
// stderr
MOVL $-12, 0(SP)
MOVL SP, BP
CALL *runtime·GetStdHandle(SB)
MOVL BP, SP
MOVL AX, 0(SP) // handle
MOVL $runtime·badcallbackmsg(SB), DX // pointer
MOVL DX, 4(SP)
MOVL runtime·badcallbacklen(SB), DX // count
MOVL DX, 8(SP)
LEAL 20(SP), DX // written count
MOVL $0, 0(DX)
MOVL DX, 12(SP)
MOVL $0, 16(SP) // overlapped
CALL *runtime·WriteFile(SB)
MOVL BP, SI
RET
src/pkg/runtime/sys_windows_amd64.s
からは、以下のx64アセンブリコードブロックが削除されています。
// This should be called on a system stack,
// so we don't need to concern about split stack.
TEXT runtime·badcallback(SB),7,$0
SUBQ $48, SP
// stderr
MOVQ $-12, CX // stderr
MOVQ CX, 0(SP)
MOVQ runtime·GetStdHandle(SB), AX
CALL AX
MOVQ AX, CX // handle
MOVQ CX, 0(SP)
MOVQ $runtime·badcallbackmsg(SB), DX // pointer
MOVQ DX, 8(SP)
MOVL $runtime·badcallbacklen(SB), R8 // count
MOVQ R8, 16(SP)
LEAQ 40(SP), R9 // written count
MOVQ $0, 0(R9)
MOVQ R9, 24(SP)
MOVQ $0, 32(SP) // overlapped
MOVQ runtime·WriteFile(SB), AX
CALL AX
ADDQ $48, SP
RET
コアとなるコードの解説
削除されたコードは、GoランタイムがCgoコールバックの不正な使用を検出した場合に、標準エラー出力に警告メッセージを書き込むためのものでした。
static int8 badcallback[] = "..."
: これは、出力されるエラーメッセージの文字列リテラルを定義しています。#pragma dataflag 16
は、このデータがポインタを含まないため、ガベージコレクタがスキャンする必要がないことをGoコンパイラに伝えます。void runtime·badcallback(void)
: この関数が、実際にエラーメッセージを書き込む処理を実行していました。// This runs on a foreign stack, without an m or a g. No stack split.
というコメントは、この関数がGoランタイムが管理する通常のゴルーチンコンテキスト外で、外部のCコードから呼び出される可能性のあるスレッド上で実行されることを明確に示しています。そのため、Goのスタック拡張メカニズム(スタックスプリット)は適用されません。#pragma textflag 7
は、この関数のコードがスタックスプリットの対象外であることをコンパイラに指示します。runtime·write(2, badcallback, sizeof badcallback - 1);
(またはruntime·pwrite
、WindowsのアセンブリではWriteFile
システムコール) は、ファイルディスクリプタ2(標準エラー出力)に、badcallback
配列に格納されたメッセージを書き込んでいました。sizeof badcallback - 1
は、C文字列の終端を示すヌル文字を除いた長さを指定しています。
Windowsのアセンブリコードでは、GetStdHandle
を呼び出して標準エラー出力のハンドルを取得し、そのハンドルとメッセージのポインタ、長さをWriteFile
システムコールに渡してメッセージを書き込んでいました。
これらのコードの削除は、GoランタイムがCgoコールバックを処理する内部ロジックが変更され、もはやこのような明示的なbadcallback
チェックと警告が不要になったことを意味します。これは、GoのCgoメカニズムがより堅牢になり、外部スレッドからのコールバックがGoランタイムによって適切に処理されるようになった結果と考えられます。
関連リンク
- Go言語のCgoに関する公式ドキュメント: https://pkg.go.dev/cmd/cgo
- Goランタイムの内部構造に関する情報(M, P, Gモデルなど): Goの公式ドキュメントやブログ記事、またはGoのソースコード自体が参考になります。
参考にした情報源リンク
- Goのコミット履歴: https://github.com/golang/go/commits/master
- Goのソースコード(特に
src/runtime
ディレクトリ) - Cgoに関する一般的な情報
- Goランタイムのスケジューラに関する一般的な情報
- Goの
#pragma
ディレクティブに関する情報(Goコンパイラの内部ドキュメントや関連する議論)