[インデックス 18106] ファイルの概要
このコミットは、Go 1.3のリリースノートである doc/go1.3.txt
に、Windows環境における syscall.NewCallbackCDecl
関数の追加について言及する変更を加えるものです。これは、GoプログラムがWindows APIやC/C++ライブラリとの相互運用性を高めるために、特定の呼び出し規約(cdecl)を持つコールバック関数を登録できるようにする重要な機能のドキュメント更新です。
コミット
- コミットハッシュ:
e8b7def7f41c0cee643e0222adc296be85022318
- Author: Alex Brainman alex.brainman@gmail.com
- Date: Sat Dec 21 12:52:53 2013 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e8b7def7f41c0cee643e0222adc296be85022318
元コミット内容
doc/go1.3.txt: mention windows syscall.NewCallbackCDecl
R=golang-codereviews, r
CC=golang-codereviews
https://golang.org/cl/44160044
変更の背景
このコミットは、Go 1.3のリリースに向けたドキュメント更新の一環です。GoプログラムがWindows環境でC/C++ライブラリやWindows APIと連携する際、Goの関数を外部のC/C++コードから呼び出せるようにする「コールバック」の仕組みが必要になります。特に、C/C++の世界では複数の「呼び出し規約(calling convention)」が存在し、関数呼び出し時の引数の渡し方やスタックのクリーンアップ方法が異なります。
Windows APIや一部のC/C++ライブラリは、cdecl
という呼び出し規約を要求するコールバックを使用することがあります。Goの標準的なsyscall.NewCallback
関数は、Windowsで一般的なstdcall
規約に対応していますが、cdecl
規約には直接対応していませんでした。このため、cdecl
規約を必要とするWindowsの機能を利用する際に、Goから直接コールバックを渡すことが困難でした。
syscall.NewCallbackCDecl
の追加は、このギャップを埋め、GoがWindows環境での相互運用性をさらに高めるための重要な機能強化でした。このコミットは、その機能がGo 1.3で利用可能になることを公式ドキュメントに明記するためのものです。
前提知識の解説
1. コールバック関数
コールバック関数とは、ある関数(A)が別の関数(B)に引数として渡され、関数Bの内部で関数Aが呼び出されるようなプログラミングパターンです。OSのAPIやライブラリでは、非同期処理の完了通知、イベントハンドリング、カスタムロジックの挿入など、様々な場面でコールバックが利用されます。
2. 呼び出し規約 (Calling Convention)
呼び出し規約は、関数が呼び出される際に、引数がどのようにスタックに積まれ、誰がスタックをクリーンアップするか、戻り値がどのように返されるかなどを定義する取り決めです。CPUアーキテクチャやOS、コンパイラによって様々な規約が存在します。Windows環境で特に重要なのは以下の2つです。
-
stdcall
(Standard Call):- Windows APIで広く使われる規約です。
- 引数は右から左へスタックに積まれます。
- 呼び出された関数(callee)がスタックをクリーンアップします。
- Goの
syscall.NewCallback
は、このstdcall
規約に準拠したコールバックを生成します。
-
cdecl
(C Declaration):- C/C++言語のデフォルトの呼び出し規約です(特に32ビットWindows環境)。
- 引数は右から左へスタックに積まれます。
- 呼び出し元関数(caller)がスタックをクリーンアップします。
- この規約は、可変長引数を持つ関数(例:
printf
)をサポートするために重要です。呼び出し元が引数の数を把握しているため、スタックのクリーンアップを適切に行えます。
3. Goのsyscall
パッケージ
syscall
パッケージは、Goプログラムから低レベルなオペレーティングシステム(OS)のプリミティブ(システムコール)にアクセスするための機能を提供します。ファイル操作、プロセス管理、ネットワーク通信など、OSが提供する基本的なサービスを利用するために使われます。Windows環境では、DLL(Dynamic Link Library)の関数を呼び出すための機能(例: syscall.LoadLibrary
, syscall.GetProcAddress
, syscall.Syscall
)や、Goの関数を外部から呼び出せるコールバックとして登録する機能(syscall.NewCallback
, syscall.NewCallbackCDecl
)などが含まれます。
Go 1.4以降、syscall
パッケージは「ロックダウン」され、新しいOS固有の機能はgolang.org/x/sys
パッケージに移行される傾向にあります。しかし、既存のsyscall
パッケージの機能は引き続き利用可能です。
技術的詳細
syscall.NewCallbackCDecl
は、Goの関数ポインタをWindowsのcdecl
呼び出し規約に準拠したコールバック関数ポインタに変換するために導入されました。これにより、Goプログラムは、cdecl
規約を期待するWindows APIやC/C++ライブラリにGoの関数をコールバックとして渡すことができるようになります。
syscall.NewCallbackCDecl
の動作原理と特徴:
- Go関数からCdeclコールバックへの変換:
syscall.NewCallbackCDecl
は、Goの関数を受け取り、その関数を呼び出すための特殊なスタブコード(アセンブリコード)を生成します。このスタブコードは、cdecl
規約に従って引数を受け取り、Goランタイムに制御を渡し、Go関数を実行し、その後cdecl
規約に従って戻り値を返します。 - スタックのクリーンアップ:
cdecl
規約では呼び出し元がスタックをクリーンアップするため、生成されるコールバックスタブはスタックのクリーンアップを行いません。 - 制限事項:
- コールバック数の制限:
syscall.NewCallback
とsyscall.NewCallbackCDecl
を合わせて、プロセス内で作成できるコールバックの数には上限があります(少なくとも1024個)。これは、内部的に固定サイズのジャンプテーブルを使用しているためです。 - メモリリーク: これらのコールバックのために割り当てられたメモリは、プロセスのライフタイム中に解放されません。一度作成されたコールバックは、プロセス終了までメモリに残り続けます。
- 引数のサイズ: Go関数に渡される引数は、
uintptr
のサイズを超えてはなりません。 - 浮動小数点数の非サポート:
NewCallbackCDecl
(およびNewCallback
)を介してコールバックとして使用されるGo関数は、浮動小数点数の引数をサポートしません。
- コールバック数の制限:
- 現代のGo開発における位置づけ:
syscall.NewCallbackCDecl
はGoの標準ライブラリの一部ですが、前述の通りsyscall
パッケージ自体はGo 1.4以降「ロックダウン」されています。新しいコードでOSとの低レベルな相互作用を行う場合は、より移植性とメンテナンス性に優れたgolang.org/x/sys
パッケージの利用が推奨されます。しかし、既存のWindowsコールバックを扱うコードでは引き続きsyscall.NewCallbackCDecl
が利用されます。
このコミットは、この重要な機能がGo 1.3で導入されることを開発者コミュニティに知らせるためのドキュメント更新であり、機能そのものの実装コミットではありません。機能の実装は、コミットメッセージに記載されているCL (Change List) https://golang.org/cl/36180044
で行われました。
コアとなるコードの変更箇所
--- a/doc/go1.3.txt
+++ b/doc/go1.3.txt
@@ -1,2 +1,3 @@
-pull linker i/o into separate liblink C library (CL 35790044)
-misc/dist renamed misc/makerelease (CL 39920043)
+liblink: pull linker i/o into separate liblink C library (CL 35790044)
+misc/dist: renamed misc/makerelease (CL 39920043)
+syscall: add NewCallbackCDecl to use for windows callbacks (CL 36180044)
コアとなるコードの解説
このコミットは、doc/go1.3.txt
というファイルに対する変更です。このファイルは、Goの各バージョンで導入される主要な変更点や新機能、非互換性などをまとめたリリースノートのような役割を果たします。
変更内容は以下の通りです。
- pull linker i/o into separate liblink C library (CL 35790044)
が+liblink: pull linker i/o into separate liblink C library (CL 35790044)
に変更されました。これは、リンカのI/O処理が独立したliblink
Cライブラリに分離されたことに関する記述で、より明確な記述に修正されています。- misc/dist renamed misc/makerelease (CL 39920043)
が+misc/dist: renamed misc/makerelease (CL 39920043)
に変更されました。これは、misc/dist
ディレクトリがmisc/makerelease
にリネームされたことに関する記述で、これもより明確な記述に修正されています。+syscall: add NewCallbackCDecl to use for windows callbacks (CL 36180044)
という行が追加されました。これがこのコミットの主要な変更点です。この行は、syscall
パッケージにNewCallbackCDecl
関数が追加され、それがWindowsコールバックに使用されることを明記しています。括弧内のCL 36180044
は、この機能が実際に実装されたChange List(Goのコードレビューシステムにおける変更セットのID)を示しています。
つまり、このコミット自体はGoのランタイムコードやsyscall
パッケージのコードを変更するものではなく、Go 1.3のリリースノートに新しい機能の追加を記録するためのドキュメント更新です。これにより、開発者はGo 1.3で利用可能になる新機能としてsyscall.NewCallbackCDecl
の存在を知ることができます。
関連リンク
- Go Change List (CL) for
syscall.NewCallbackCDecl
implementation: https://golang.org/cl/36180044