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

[インデックス 17731] ファイルの概要

このコミットは、GoランタイムのWindows固有のシステムコール関連ファイルである src/pkg/runtime/syscall_windows.goc に変更を加えています。具体的には、syscall.NewCallbackCDecl 関数の定義をコメントアウトし、その機能を一時的に無効化しています。

コミット

commit 4216203bcf8b46ee874d4f5a637891c34ae9d7ca
Author: Russ Cox <rsc@golang.org>
Date:   Wed Oct 2 21:39:45 2013 -0400

    runtime: remove syscall.NewCallbackCDecl on Windows
    
    It is not possible to use (there is no declaration in package syscall),
    and no one seems to care.
    
    Alex Brainman may bring this back properly for Go 1.3.
    
    Fixes #6338.
    
    R=golang-dev, r, alex.brainman
    CC=golang-dev
    https://golang.org/cl/14287043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4216203bcf8b46ee874d4f5a637891c34ae9d7ca

元コミット内容

GoランタイムからWindowsにおける syscall.NewCallbackCDecl を削除します。

この関数は使用不可能であり(syscall パッケージに宣言がないため)、誰も関心を示していないようです。

Alex BrainmanがGo 1.3でこれを適切に復活させる可能性があります。

Issue #6338 を修正します。

変更の背景

このコミットの主な背景は、syscall.NewCallbackCDecl 関数がGoの syscall パッケージ内で適切に宣言されておらず、結果として利用できない状態であったことです。加えて、この機能に対するユーザーからの需要や関心が低いと判断されたため、一時的に削除されることになりました。

コミットメッセージには「Alex Brainman may bring this back properly for Go 1.3.」とあり、これは将来的にこの機能がより適切に実装され、Go 1.3で再導入される可能性が示唆されています。これは、単なる不要なコードの削除ではなく、将来的な改善を見据えた一時的な措置であることを意味します。

また、「Fixes #6338」とあることから、このコミットが特定のバグや問題(Issue 6338)を解決する目的も持っていたことがわかります。

前提知識の解説

1. Goの syscall パッケージと外部関数インターフェース (FFI)

Go言語の syscall パッケージは、オペレーティングシステムが提供する低レベルなプリミティブ(システムコール)へのアクセスを提供します。Windows環境においては、Win32 APIなどのネイティブなC言語ベースのライブラリと連携するために、GoからCの関数を呼び出したり、CのコードからGoの関数をコールバックとして呼び出したりする機能が必要になります。このような異なる言語間の連携を可能にする仕組みを外部関数インターフェース (FFI: Foreign Function Interface) と呼びます。

2. コールバック関数

コールバック関数とは、ある関数に引数として渡され、その関数内で特定のイベントが発生した際に呼び出される関数のことです。Windowsプログラミングでは、GUIイベント処理、非同期I/O、タイマー処理など、様々な場面でコールバックが利用されます。Goの関数をWindows APIにコールバックとして登録する場合、Goの関数ポインタをWindowsが理解できる形式(C言語の関数ポインタ)に変換する必要があります。

3. syscall.NewCallbacksyscall.NewCallbackCDecl

Goの syscall パッケージには、Goの関数をC言語の関数ポインタに変換するための NewCallback 関数が存在します。これは、Goの関数をWindows APIが期待する形式(通常は stdcall 呼び出し規約)に変換するために使用されます。

一方、syscall.NewCallbackCDecl は、Goの関数を cdecl 呼び出し規約に従うC言語の関数ポインタに変換することを意図した関数です。

4. 呼び出し規約 (Calling Convention)

呼び出し規約とは、関数が呼び出される際に、引数がどのようにスタックに積まれ、誰がスタックをクリーンアップするか、戻り値がどのように返されるかなどを定義する取り決めです。Windows環境では主に以下の2つの呼び出し規約がよく使われます。

  • stdcall (Standard Call):
    • Windows API関数の多くで採用されている標準的な呼び出し規約です。
    • 引数は右から左へスタックに積まれます。
    • 呼び出された関数(callee)がスタックをクリーンアップします。
    • 可変長引数を持つ関数には使用できません。
  • cdecl (C Declaration):
    • C/C++言語のデフォルトの呼び出し規約です。
    • 引数は右から左へスタックに積まれます。
    • 呼び出し元(caller)がスタックをクリーンアップします。
    • 可変長引数を持つ関数に使用できます。

GoのランタイムがWindows APIと連携する際には、これらの呼び出し規約を正しく扱う必要があります。NewCallback は通常 stdcall を、NewCallbackCDeclcdecl を扱うために設計されていました。

技術的詳細

このコミットは、GoランタイムのWindows固有のコールバックメカニズムに関するものです。syscall.NewCallbackCDecl は、Goの関数をC言語の cdecl 呼び出し規約に準拠したコールバック関数ポインタとしてエクスポートすることを目的としていました。しかし、コミットメッセージが示すように、この関数は syscall パッケージ内で適切に宣言されていなかったため、実質的に使用不可能でした。

Goの syscall パッケージは、OSの低レベルな機能にアクセスするためのインターフェースを提供しますが、その実装はOSごとに異なります。Windowsの場合、GoのランタイムはWin32 APIと連携するために、Goの関数をCの関数ポインタとして公開するメカニズムを必要とします。runtime·compilecallback は、Goの関数をコンパイルして、Cのコードから呼び出し可能なエントリポイントを生成するランタイム内部関数です。この関数は、引数 true または false を取ることで、それぞれ stdcall または cdecl 呼び出し規約に対応するコールバックを生成するように設計されていました。

syscall.NewCallbackCDecl が削除された技術的な理由は、syscall パッケージにその宣言が存在しなかったため、Goのユーザーコードからこの関数を呼び出すことができなかった点にあります。つまり、ランタイム内部にはその機能の実装(runtime·compilecallbackfalse で呼び出す部分)が存在していたものの、外部に公開されていなかったため、デッドコードとなっていたわけです。

この削除は、Goのランタイムが不要なコードを整理し、メンテナンス性を向上させるための措置と見なせます。また、cdecl 呼び出し規約のコールバックがGoのWindowsアプリケーション開発においてあまり必要とされていなかったことも、削除の判断に影響したと考えられます。多くのWindows APIは stdcall を使用しており、NewCallback で十分なケースが多かったのでしょう。

コミットメッセージにある「Alex Brainman may bring this back properly for Go 1.3.」という記述は、この機能自体が完全に不要になったわけではなく、将来的に必要性が生じた場合、より堅牢で適切な形で再導入される可能性を示唆しています。これは、Goの開発プロセスが、必要に応じて機能を一時的に削除し、より良い設計で再導入するというアプローチを取ることがあることを示しています。

コアとなるコードの変更箇所

変更は src/pkg/runtime/syscall_windows.goc ファイルで行われています。

--- a/src/pkg/runtime/syscall_windows.goc
+++ b/src/pkg/runtime/syscall_windows.goc
@@ -40,9 +40,14 @@ func NewCallback(fn Eface) (code uintptr) {
  	code = (uintptr)runtime·compilecallback(fn, true);
  }\n 
+/*
+ * If this is needed, uncomment here and add a declaration in package syscall
+ * next to the NewCallback declaration.
+ *
  func NewCallbackCDecl(fn Eface) (code uintptr) {
  	code = (uintptr)runtime·compilecallback(fn, false);
  }\n
+ */
 
  func Syscall(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
  	WinCall c;

具体的には、func NewCallbackCDecl(fn Eface) (code uintptr) の定義全体がC言語のコメントブロック /* ... */ で囲まれ、コメントアウトされています。

コアとなるコードの解説

コメントアウトされた NewCallbackCDecl 関数は、以下のような構造をしていました。

func NewCallbackCDecl(fn Eface) (code uintptr) {
 	code = (uintptr)runtime·compilecallback(fn, false);
}

この関数は、Goの Eface 型(インターフェース値)として渡された関数 fn を、runtime·compilecallback という内部ランタイム関数に渡しています。runtime·compilecallback は、Goの関数をC言語から呼び出し可能な形式にコンパイルし、そのエントリポイントのアドレス(uintptr 型)を返します。

ここで重要なのは、runtime·compilecallback の第二引数に false が渡されている点です。この false は、生成されるコールバックが cdecl 呼び出し規約に従うことを示していました。対照的に、NewCallback 関数では true が渡され、stdcall 呼び出し規約のコールバックを生成します。

このコミットでは、NewCallbackCDecl 関数全体がコメントアウトされました。これは、この関数が syscall パッケージで宣言されていなかったため、外部から呼び出すことができず、実質的にデッドコードとなっていたためです。

コメントブロックの冒頭には、以下のコメントが追加されています。

/*
 * If this is needed, uncomment here and add a declaration in package syscall
 * next to the NewCallback declaration.
 *

このコメントは、将来的に NewCallbackCDecl が必要になった場合、この部分のコメントを解除し、同時に syscall パッケージ内の NewCallback の宣言の隣に NewCallbackCDecl の宣言を追加する必要があることを明確に指示しています。これは、コードの意図を明確にし、将来的な再導入の際のガイドラインを提供するためのものです。

この変更により、GoのWindowsランタイムは cdecl 呼び出し規約のコールバックを直接生成する機能を一時的に失いましたが、必要に応じて容易に復活させられるようになっています。

関連リンク

  • Go Change-Id: I2111111111111111111111111111111111111111 (Go CL 14287043): https://golang.org/cl/14287043
  • Go Issue #6338: コミットメッセージには Fixes #6338 と記載されていますが、公開されているGoのIssueトラッカーでこの番号のIssueを直接見つけることはできませんでした。これは、内部的なトラッキング番号であるか、あるいは非常に古い、または非公開のIssueである可能性があります。

参考にした情報源リンク