[インデックス 13301] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnetパッケージにおける、Plan 9オペレーティングシステム上でのテストの安定性向上と、ネットワーク関連の挙動の修正を目的としています。具体的には、以下のファイルが変更されています。
src/pkg/net/dial_test.go: ネットワーク接続テストに関する変更。src/pkg/net/ipraw_test.go: IP生ソケットテストに関する変更。src/pkg/net/ipsock_plan9.go: Plan 9固有のIPソケット実装に関する変更。src/pkg/net/multicast_test.go: マルチキャストテストに関する変更。src/pkg/net/net_test.go: 汎用的なネットワークテストに関する変更。src/pkg/net/tcpsock_plan9.go: Plan 9固有のTCPソケット実装に関する変更。src/pkg/net/udpsock.go: UDPソケットの汎用的な定義に関する変更。src/pkg/net/udpsock_plan9.go: Plan 9固有のUDPソケット実装に関する変更。src/pkg/net/udpsock_posix.go: POSIX互換システム向けのUDPソケット実装に関する変更。src/pkg/net/unicast_test.go: ユニキャストテストに関する変更。
コミット
- コミットハッシュ:
42a76efc924cdae869efa1c936d263134ff225d6 - Author: Fazlul Shahriar fshahriar@gmail.com
- Date: Wed Jun 6 18:38:56 2012 -0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/42a76efc924cdae869efa1c936d263134ff225d6
元コミット内容
net: pass tests on Plan 9 again
R=golang-dev
CC=golang-dev
https://golang.org/cl/6280045
変更の背景
このコミットの主な背景は、Go言語のnetパッケージがPlan 9オペレーティングシステム上で正しく動作し、関連するテストが再びパスするようにすることです。コミットメッセージ「net: pass tests on Plan 9 again」が示す通り、以前はPlan 9環境でネットワーク関連のテストが失敗していたか、期待通りに動作していなかったと考えられます。
Plan 9は、その独特なファイルシステム中心の設計により、ネットワーク操作も通常のファイル操作として抽象化されています。このため、他のPOSIX準拠のOS(Linux, macOS, BSDなど)とは異なるアプローチが必要となる場合があります。Goのnetパッケージは、様々なOSに対応するために、OS固有の実装(例: *_plan9.go, *_posix.go)を持っています。
このコミットは、Plan 9固有のネットワーク実装におけるバグ修正、リソースリークの防止、およびPlan 9の特性に合わせたテストの調整を行うことで、Goのネットワーク機能がこの環境で堅牢に動作することを保証しようとしています。特に、ファイルディスクリプタのリークは、長時間の実行や多数の接続を扱うアプリケーションにおいて深刻な問題を引き起こす可能性があるため、その修正は重要です。また、Plan 9のネットワークスタックの特性上、一部のテストが他のOSと同じように動作しない、あるいは非常に時間がかかる場合があるため、それらのテストを適切にスキップまたは調整する必要がありました。
前提知識の解説
Plan 9 from Bell Labs
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。その最も特徴的な設計思想は、「すべてがファイルである」という原則です。ネットワークインターフェース、プロセス、デバイスなども含め、システム内のあらゆるリソースがファイルシステムのエントリとして表現されます。
- ファイルシステム中心の設計: ネットワーク接続も
/netディレクトリ以下の特殊なファイルとして扱われます。例えば、TCP接続を確立するには、/net/tcp/cloneを開き、そのファイルディスクリプタに対してconnectコマンドを書き込むといった操作を行います。 /netディレクトリ: Plan 9におけるネットワーク操作の主要なインターフェースです。このディレクトリ以下に、tcp、udp、ipなどのサブディレクトリがあり、それぞれが対応するプロトコルのインターフェースを提供します。- リソース管理: ファイルディスクリプタは重要なシステムリソースであり、適切に閉じられないとリソースリークを引き起こし、システムのパフォーマンス低下や不安定化を招きます。
Go言語のnetパッケージ
Go言語の標準ライブラリであるnetパッケージは、TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うための機能を提供します。このパッケージは、クロスプラットフォームでのネットワークプログラミングを容易にするために、OS固有の差異を抽象化しています。しかし、完全に抽象化できない部分や、特定のOSで最適化が必要な場合には、*_GOOS.goという命名規則に従ってOS固有のソースファイルが用意されます(例: tcpsock_plan9.goはPlan 9固有のTCPソケット実装)。
Go言語のビルド制約(// +build ディレクティブ)
Go言語では、ソースファイルの先頭に// +buildディレクティブを記述することで、条件付きコンパイルを行うことができます。これは、特定のOS、アーキテクチャ、またはカスタムタグが指定された場合にのみ、そのファイルをビルドに含める(または除外する)ために使用されます。
- 例:
// +build !plan9このディレクティブは、「plan9タグが指定されていない場合にのみこのファイルをビルドに含める」という意味です。つまり、Plan 9環境でビルドする際にはこのファイルは無視されます。これは、特定のテストや機能がPlan 9ではサポートされていない、または異なる実装が必要な場合に便利です。
SOMAXCONNとリスナーのバックログ
SOMAXCONNは、TCPリスニングソケットのバックログキューの最大長を定義する定数です。バックログキューは、サーバーがaccept呼び出しを待っている間に、カーネルがキューに入れることができる保留中の接続の最大数を指します。この値を超えて接続要求が来ると、それらの接続は拒否される可能性があります。Plan 9では、この値が/sys/include/ape/sys/socket.hで定義されており、このコミットではその値(5)をlistenerBacklogとして使用しています。
syscall.EPLAN9
syscallパッケージは、GoプログラムからOSのシステムコールを直接呼び出すためのインターフェースを提供します。syscall.EPLAN9は、Plan 9オペレーティングシステム固有のエラーコードの一つで、通常は「操作がサポートされていない」ことを示すために使用されます。このコミットでは、Plan 9上でデッドライン設定がサポートされていないことを示すためにこのエラーが返されています。
技術的詳細
このコミットは、主に以下の技術的な側面に焦点を当てています。
-
リソースリークの修正: Plan 9のネットワークインターフェースはファイルシステムを介して行われるため、ネットワーク操作中に開かれたファイルディスクリプタ(
*os.File)は、操作が完了した後、特にエラーパスにおいて適切に閉じられる必要があります。src/pkg/net/ipsock_plan9.goでは、readPlan9Addr,startPlan9,dialPlan9,listenPlan9,acceptPlan9といった関数内で、エラーが発生した場合にf.Close()が呼び出されるように修正されています。これにより、ファイルディスクリプタのリークが防止され、システムの安定性が向上します。また、readPlan9Addrにはdefer f.Close()が追加され、関数の終了時に確実にファイルが閉じられるようになっています。 -
Plan 9でのテストの調整:
- テストのスキップ:
src/pkg/net/ipraw_test.go,src/pkg/net/multicast_test.go,src/pkg/net/unicast_test.goには、// +build !plan9ディレクティブが追加されました。これは、これらのテストがPlan 9環境では適切に動作しないか、またはテストの目的がPlan 9のネットワークモデルに合致しないため、Plan 9でのビルド時にはこれらのテストファイルがコンパイルされないようにするためです。 - 特定のテストの動作変更:
src/pkg/net/dial_test.goのTestSelfConnectでは、runtime.GOOSがplan9の場合に、テストの反復回数nが1000から100に減らされています。これは、Plan 9を含む一部の非Linuxシステムでは、ローカルホストでリッスンしているものがないことを検出するのに時間がかかるため、テストの実行時間を短縮するための調整です。 - UDPリスナーテストのスキップ:
src/pkg/net/net_test.goのTestUDPListenCloseでは、runtime.GOOSがplan9の場合にテストがスキップされるようになりました。これは、Plan 9におけるUDPリスナーのクローズ動作が他のOSと異なり、このテストの意図に合わないか、またはテストが失敗するためと考えられます。
- テストのスキップ:
-
デッドライン設定の非サポート:
src/pkg/net/ipsock_plan9.goではplan9ListenerにSetDeadlineメソッドが追加されましたが、これはsyscall.EPLAN9(操作がサポートされていない)を返します。同様に、src/pkg/net/tcpsock_plan9.goとsrc/pkg/net/udpsock_plan9.goからは、TCPConnとUDPConnのSetDeadline,SetReadDeadline,SetWriteDeadlineメソッドが削除されました。これらの変更は、Plan 9のネットワークスタックが接続やリスナーに対するデッドライン(タイムアウト)設定を直接サポートしていないことを明確に示しています。Goのnetパッケージは、可能な限り共通のインターフェースを提供しますが、基盤となるOSが特定の機能を提供しない場合は、その旨を明示するか、関連するメソッドを削除するなどの対応が必要です。 -
TCPリスナーのクローズ処理の改善:
src/pkg/net/tcpsock_plan9.goでは、TCPListenerのClose()メソッドが修正され、コントロールファイルに"hangup"コマンドを書き込んでからファイルを閉じるようになりました。これは、Plan 9においてTCPリスナーを適切にシャットダウンするためのより堅牢な方法です。単にファイルを閉じるだけでなく、明示的に接続を終了させるシグナルを送ることで、リソースが確実に解放されるようにします。 -
エラー変数の移動:
src/pkg/net/udpsock_posix.goにあったErrWriteToConnectedエラー変数が、より汎用的なsrc/pkg/net/udpsock.goに移動されました。これにより、このエラーがPOSIX固有のものではなく、UDPソケット全般に関連するものであることが明確になります。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の通りです。
-
src/pkg/net/ipsock_plan9.goにおけるファイルディスクリプタのクローズ処理の追加:readPlan9Addr関数にdefer f.Close()を追加。startPlan9,dialPlan9,listenPlan9,acceptPlan9関数内のエラーパスにf.Close()呼び出しを追加。plan9ListenerにSetDeadlineメソッドを追加し、syscall.EPLAN9を返すように変更。
-
src/pkg/net/tcpsock_plan9.goにおけるTCPListener.Close()の実装変更:TCPConnからSetDeadline関連のメソッドを削除。TCPListenerのClose()メソッドで、コントロールファイルに"hangup"を書き込んでから閉じるように変更。
-
src/pkg/net/udpsock_plan9.goにおけるUDPConnのデッドライン関連メソッドの削除:UDPConnからSetDeadline,SetReadDeadline,SetWriteDeadlineメソッドを削除。
-
テストファイルへの
// +build !plan9ディレクティブの追加:src/pkg/net/ipraw_test.gosrc/pkg/net/multicast_test.gosrc/pkg/net/unicast_test.go
-
src/pkg/net/net_test.goにおけるTestUDPListenCloseのPlan 9でのスキップ:runtime.GOOSがplan9の場合にテストをスキップするロジックを追加。
コアとなるコードの解説
src/pkg/net/ipsock_plan9.goの変更
Plan 9では、ネットワーク接続はファイルとして扱われます。例えば、f, err := os.OpenFile("/net/tcp/clone", os.O_RDWR, 0)のようにファイルを開き、そのファイルディスクリプタを通じてネットワーク操作を行います。このため、開かれたファイルディスクリプタは、操作が完了した後に必ず閉じられる必要があります。
変更前は、エラーが発生した場合にファイルディスクリプタが閉じられず、リソースリークが発生する可能性がありました。このコミットでは、readPlan9Addr, startPlan9, dialPlan9, listenPlan9, acceptPlan9といった関数内で、f.Close()をエラーパスに追加することで、この問題を解決しています。特にreadPlan9Addrにdefer f.Close()が追加されたことは、関数の正常終了時にも確実にリソースが解放されることを保証する重要な改善です。
また、plan9ListenerにSetDeadlineメソッドが追加されましたが、これはsyscall.EPLAN9を返します。これは、Plan 9のネットワークスタックがリスナーに対するデッドライン設定を直接サポートしていないため、Goのインターフェースを満たしつつ、その制約を明示的に示すための実装です。
src/pkg/net/tcpsock_plan9.goの変更
TCPConnからSetDeadline関連のメソッドが削除されたのも、Plan 9がこれらの機能をサポートしていないためです。Goのnet.Connインターフェースはこれらのメソッドを要求しますが、OSが提供しない場合は、実装を削除するか、エラーを返すしかありません。この変更では、明示的に削除することで、Plan 9上でのTCPConnがこれらのデッドライン機能をサポートしないことを明確にしています。
TCPListenerのClose()メソッドの変更は、Plan 9におけるTCPリスナーの適切なシャットダウン方法を反映しています。単にファイルディスクリプタを閉じるだけでなく、コントロールファイルに"hangup"コマンドを書き込むことで、基盤となるネットワークスタックに対してリスナーを明示的に終了させるシグナルを送ります。これにより、リソースがより確実に解放され、ポートが速やかに再利用可能になります。
テストファイルへの// +build !plan9ディレクティブの追加
src/pkg/net/ipraw_test.go, src/pkg/net/multicast_test.go, src/pkg/net/unicast_test.goに// +build !plan9が追加されたことは、これらのテストがPlan 9のネットワークモデルや機能セットと互換性がない、またはPlan 9上で実行すると問題が発生するため、コンパイルから除外されることを意味します。これは、クロスプラットフォームのテスト戦略において一般的なアプローチであり、特定のOSの特性に合わせたテストの選択を可能にします。
src/pkg/net/net_test.goにおけるTestUDPListenCloseのPlan 9でのスキップ
TestUDPListenCloseがPlan 9でスキップされるようになったのは、Plan 9におけるUDPリスナーのクローズ動作が他のOSと異なるため、このテストが期待通りに動作しないか、またはテストの目的がPlan 9の動作に合致しないためです。t.Logf("skipping test on %q", runtime.GOOS)というログメッセージは、テストが意図的にスキップされたことを明確に示します。
関連リンク
- Gerrit Change-ID:
https://golang.org/cl/6280045- これはGoプロジェクトがコードレビューに利用しているGerritシステムにおける変更セットのIDです。このリンクを辿ることで、このコミットに至るまでの議論や、関連するパッチセットの履歴を確認することができます。
参考にした情報源リンク
- Plan 9 from Bell Labs 公式サイト: http://plan9.bell-labs.com/plan9/
- Go言語
netパッケージ ドキュメント: https://pkg.go.dev/net - Go言語 ビルド制約 (Build Constraints) に関するドキュメント: https://pkg.go.dev/cmd/go#hdr-Build_constraints
- Plan 9のネットワークに関する情報 (例: 9Pプロトコル):
- Plan 9のネットワークは9Pプロトコルに基づいています。詳細については、Plan 9の公式ドキュメントや関連する学術論文を参照してください。
- 例: https://9p.io/plan9/man/man9/intro.html (Plan 9のシステムコールとライブラリの概要)
- TCP
SOMAXCONN:- TCPのバックログキューに関する一般的な情報源(例: Linux man pages for
listen(2)など)。 - 例: https://man7.org/linux/man-pages/man2/listen.2.html (Linuxの
listenシステムコールに関するドキュメント)
- TCPのバックログキューに関する一般的な情報源(例: Linux man pages for
- Go言語
syscallパッケージ ドキュメント: https://pkg.go.dev/syscall - Go言語
osパッケージ ドキュメント: https://pkg.go.dev/os - Go言語
runtimeパッケージ ドキュメント: https://pkg.go.dev/runtime - Go言語
testingパッケージ ドキュメント: https://pkg.go.dev/testing