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

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

このコミットは、Go言語の標準ライブラリであるnetパッケージにおけるWindowsビルドの問題を修正するものです。具体的には、timeoutError型とその関連変数をsrc/pkg/net/fd.goからsrc/pkg/net/net.goへ移動することで、Windows環境でのコンパイルエラーを解消しています。

コミット

commit 01507b9ad168b98a1d528e6039f98a13d633034e
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Tue Dec 20 14:32:33 2011 -0800

    net: fix Windows build
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5505048
---
 src/pkg/net/fd.go  | 8 --------
 src/pkg/net/net.go | 8 ++++++++
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
index 5318c51c9a..bcd04a0ad8 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -377,14 +377,6 @@ func (fd *netFD) CloseWrite() error {
 	return fd.shutdown(syscall.SHUT_WR)
 }
 
-type timeoutError struct{}
-
-func (e *timeoutError) Error() string   { return "i/o timeout" }
-func (e *timeoutError) Timeout() bool   { return true }
-func (e *timeoutError) Temporary() bool { return true }
-
-var errTimeout error = &timeoutError{}
-
 func (fd *netFD) Read(p []byte) (n int, err error) {
 	if fd == nil {
 		return 0, os.EINVAL
diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go
index 48f0ae791c..b236dfdb1d 100644
--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -157,6 +157,14 @@ func (e *OpError) Timeout() bool {
 	return ok && t.Timeout()\n }\n \n+type timeoutError struct{}\n+\n+func (e *timeoutError) Error() string   { return "i/o timeout" }\n+func (e *timeoutError) Timeout() bool   { return true }\n+func (e *timeoutError) Temporary() bool { return true }\n+\n+var errTimeout error = &timeoutError{}\n+\n type AddrError struct {\n \tErr  string\n \tAddr string\n```

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

[https://github.com/golang/go/commit/01507b9ad168b98a1d528e6039f98a13d633034e](https://github.com/golang/go/commit/01507b9ad168b98a1d528e6039f98a13d633034e)

## 元コミット内容

このコミットは、Go言語の`net`パッケージにおけるWindowsビルドの不具合を修正することを目的としています。具体的には、`timeoutError`というエラー型とそのインスタンスである`errTimeout`の定義を、`src/pkg/net/fd.go`から`src/pkg/net/net.go`へ移動しています。

## 変更の背景

Go言語はクロスプラットフォーム対応を重視しており、異なるオペレーティングシステム(OS)で同じコードベースが問題なく動作・コンパイルされることを目指しています。このコミットが行われた2011年当時、Go言語はまだ発展途上にあり、OS固有のビルド問題が発生することは珍しくありませんでした。

`timeoutError`は、ネットワーク操作におけるタイムアウトを示すエラー型です。このような汎用的なエラー型は、`net`パッケージ内の複数のファイルや、場合によっては他のパッケージからも参照される可能性があります。

`src/pkg/net/fd.go`はファイルディスクリプタ(File Descriptor)に関連する低レベルのネットワーク操作を扱うファイルであり、OS固有の実装を含むことがあります。一方、`src/pkg/net/net.go`は`net`パッケージの主要な定義や汎用的な機能を含む、より高レベルかつ共通的なファイルです。

Windows環境でのビルド時に、`fd.go`に定義されていた`timeoutError`が、他のファイルやモジュールから正しく参照できない、あるいはリンケージの問題を引き起こす可能性がありました。これは、Goのパッケージ構造やコンパイルの仕組み、特にWindowsにおけるシンボル解決の特性に起因するものであったと考えられます。`timeoutError`のような基本的なエラー型は、パッケージ全体で共通して利用されるべきであり、特定のOSに依存する可能性のある`fd.go`ではなく、より汎用的な`net.go`に配置することが適切であるという判断がなされたと推測されます。

## 前提知識の解説

### Go言語の`net`パッケージ

`net`パッケージは、Go言語の標準ライブラリの一部であり、ネットワークI/O機能を提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワークプロトコルを扱うためのインターフェースや型が含まれています。ソケットの作成、接続、データの送受信、リスニングなどの機能を提供し、Goアプリケーションでネットワーク通信を行う際の基盤となります。

### `timeoutError`型とエラーインターフェース

Go言語では、エラーは`error`インターフェースによって表現されます。このインターフェースは、`Error() string`というメソッドを一つだけ持ちます。
`timeoutError`は、この`error`インターフェースを実装したカスタムエラー型です。さらに、Go 1.10以降で導入された`net.Error`インターフェース(当時はまだ存在しないが、同様の概念はあった)が持つ`Timeout() bool`メソッドと`Temporary() bool`メソッドも実装しています。

*   `Error() string`: エラーメッセージを返します。ここでは"i/o timeout"を返します。
*   `Timeout() bool`: そのエラーがタイムアウトによるものかどうかを示します。`true`を返します。
*   `Temporary() bool`: そのエラーが一時的なものであり、再試行によって解決する可能性があるかどうかを示します。`true`を返します。

このようなカスタムエラー型を定義することで、エラーの種類を詳細に判別し、それに応じた処理(例:タイムアウトなら再試行する、など)を実装することが可能になります。

### `syscall.SHUT_WR`

`syscall`パッケージは、OSのシステムコールへの低レベルなインターフェースを提供します。`syscall.SHUT_WR`は、ソケットのシャットダウン操作において、書き込み側(送信側)のみをシャットダウンすることを示す定数です。`fd.CloseWrite()`メソッド内で使用されており、ソケットの書き込みチャネルを閉じます。

### Goのパッケージとビルドシステム

Go言語のコードはパッケージに分割され、各パッケージは通常、独自のディレクトリに配置されます。パッケージ内のファイルは、同じパッケージ名を持つ限り、互いに定義を参照できます。しかし、異なるパッケージ間での参照や、OS固有のビルドタグ(例:`// +build windows`)を持つファイルが存在する場合、ビルドプロセスはより複雑になります。

このコミットの背景にある問題は、おそらくWindows環境でのコンパイル時に、`fd.go`が`timeoutError`を定義しているにもかかわらず、その定義が`net`パッケージ内の他の部分や、`net`パッケージを利用する外部のコードから正しく「見えない」状態になっていたことに関連していると考えられます。これは、GoのコンパイラやリンカがWindows上で特定のシンボル解決の順序や可視性のルールを適用した結果かもしれません。

## 技術的詳細

このコミットの技術的な核心は、`timeoutError`型とそのインスタンス`errTimeout`の定義場所の変更です。

元のコードでは、`src/pkg/net/fd.go`内に`timeoutError`型と`errTimeout`変数が定義されていました。`fd.go`は、ファイルディスクリプタを介した低レベルなネットワークI/O操作を扱うファイルであり、OS固有のコードを含むことがあります。

```go
// src/pkg/net/fd.go (変更前)
type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }

var errTimeout error = &timeoutError{}

この定義がWindowsビルドで問題を引き起こした原因として、以下の可能性が考えられます。

  1. シンボル解決の問題: Windowsのリンカが、fd.go内で定義されたtimeoutErrorを、netパッケージ内の他のファイル(例: net.go)や、netパッケージを利用する外部のコードから正しく解決できなかった。これは、GoのビルドシステムがWindows上で特定のファイルやパッケージのロード順序に依存していたり、特定のOS固有のビルドタグ(例: // +build windows)がfd.goに適用されており、その結果としてtimeoutErrorの定義が特定のビルド環境でのみ利用可能になっていた、といった状況が考えられます。
  2. 依存関係の循環または不整合: fd.gonet.goに依存し、かつnet.gotimeoutErrorを参照する必要がある場合、timeoutErrorfd.goに定義されていると、依存関係の循環や、net.gotimeoutErrorの定義を認識する前に参照しようとする不整合が生じた可能性があります。
  3. 汎用性の欠如: timeoutErrorはネットワーク操作全般で発生しうる汎用的なエラーであり、特定の低レベルなファイルディスクリプタ操作に限定されるべきではありません。net.gonetパッケージのコアとなる定義を含むため、このような汎用的なエラー型を配置するのに適した場所です。これにより、パッケージ内のどのファイルからでも、あるいはnetパッケージをインポートする他のパッケージからでも、timeoutErrorを安定して参照できるようになります。

コミットでは、timeoutError型とその関連変数をsrc/pkg/net/net.goに移動しました。

// src/pkg/net/net.go (変更後)
type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }

var errTimeout error = &timeoutError{}

この変更により、timeoutErrorの定義がnetパッケージのより中心的なファイルに配置され、Windows環境でのビルド時に発生していたシンボル解決やリンケージの問題が解消されたと考えられます。これは、Goのクロスコンパイルや特定のOSでのビルドの挙動に関する深い理解に基づいた修正と言えます。

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

このコミットによるコードの変更は、以下の2つのファイルにわたります。

  1. src/pkg/net/fd.go:

    • timeoutError型の定義が削除されました。
    • Error() stringTimeout() boolTemporary() boolメソッドの実装が削除されました。
    • errTimeout変数の宣言と初期化が削除されました。 これらの変更により、fd.goからtimeoutErrorに関するコードが完全に削除されています。
  2. src/pkg/net/net.go:

    • timeoutError型の定義が追加されました。
    • Error() stringTimeout() boolTemporary() boolメソッドの実装が追加されました。
    • errTimeout変数の宣言と初期化が追加されました。 これらの変更により、net.gotimeoutErrorに関するコードが追加されています。

変更の差分は以下の通りです。

diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
index 5318c51c9a..bcd04a0ad8 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -377,14 +377,6 @@ func (fd *netFD) CloseWrite() error {
 	return fd.shutdown(syscall.SHUT_WR)
 }
 
-type timeoutError struct{}
-
-func (e *timeoutError) Error() string   { return "i/o timeout" }
-func (e *timeoutError) Timeout() bool   { return true }
-func (e *timeoutError) Temporary() bool { return true }
-
-var errTimeout error = &timeoutError{}
-
 func (fd *netFD) Read(p []byte) (n int, err error) {
 	if fd == nil {
 		return 0, os.EINVAL
diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go
index 48f0ae791c..b236dfdb1d 100644
--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -157,6 +157,14 @@ func (e *OpError) Timeout() bool {
 	return ok && t.Timeout()\n }\n \n+type timeoutError struct{}\n+\n+func (e *timeoutError) Error() string   { return "i/o timeout" }\n+func (e *timeoutError) Timeout() bool   { return true }\n+func (e *timeoutError) Temporary() bool { return true }\n+\n+var errTimeout error = &timeoutError{}\n+\n type AddrError struct {\n \tErr  string\n \tAddr string

コアとなるコードの解説

このコミットのコアとなる変更は、timeoutError型とerrTimeout変数の定義をfd.goからnet.goへ移動したことです。

  • fd.goからの削除: fd.goは、ファイルディスクリプタ(File Descriptor)を抽象化し、低レベルなI/O操作を扱うためのコードを含んでいます。このファイルはOS固有のコードを含むことがあり、特定のプラットフォームでのみコンパイルされる部分があるかもしれません。timeoutErrorのような汎用的なエラー型がここに定義されていると、Windowsのような特定の環境でビルドする際に、その定義が他の部分から正しく参照できない、あるいはリンケージの問題を引き起こす可能性がありました。fd.goからこの定義を削除することで、その問題を回避しています。

  • net.goへの追加: net.goは、netパッケージの主要な型、関数、変数などを定義する、より中心的なファイルです。このファイルにtimeoutErrorを移動することで、以下の利点が得られます。

    1. 可視性の向上: net.goに定義された型は、netパッケージ内の他のすべてのファイルから、そしてnetパッケージをインポートする外部のパッケージからも、一貫して参照可能になります。これにより、Windowsビルドにおけるシンボル解決の問題が解消されます。
    2. 適切な配置: timeoutErrorは、特定の低レベルなファイルディスクリプタ操作に限定されるエラーではなく、ネットワークI/O全般で発生しうる汎用的なタイムアウトエラーです。そのため、netパッケージのコア定義を含むnet.goに配置することが、論理的にも適切です。
    3. クロスプラットフォーム互換性: 汎用的なエラー型をOS固有の可能性のあるファイルから分離し、よりプラットフォームに依存しないコアファイルに置くことで、Goのクロスプラットフォームビルドの堅牢性が向上します。

この変更は、Go言語の初期段階におけるクロスプラットフォームビルドの課題と、パッケージ設計における適切な責務分担の重要性を示しています。

関連リンク

参考にした情報源リンク

  • 特になし (コミットメッセージとGo言語の一般的な知識に基づいています)