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

[インデックス 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におけるネットワーク操作の主要なインターフェースです。このディレクトリ以下に、tcpudpipなどのサブディレクトリがあり、それぞれが対応するプロトコルのインターフェースを提供します。
  • リソース管理: ファイルディスクリプタは重要なシステムリソースであり、適切に閉じられないとリソースリークを引き起こし、システムのパフォーマンス低下や不安定化を招きます。

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上でデッドライン設定がサポートされていないことを示すためにこのエラーが返されています。

技術的詳細

このコミットは、主に以下の技術的な側面に焦点を当てています。

  1. リソースリークの修正: Plan 9のネットワークインターフェースはファイルシステムを介して行われるため、ネットワーク操作中に開かれたファイルディスクリプタ(*os.File)は、操作が完了した後、特にエラーパスにおいて適切に閉じられる必要があります。src/pkg/net/ipsock_plan9.goでは、readPlan9Addr, startPlan9, dialPlan9, listenPlan9, acceptPlan9といった関数内で、エラーが発生した場合にf.Close()が呼び出されるように修正されています。これにより、ファイルディスクリプタのリークが防止され、システムの安定性が向上します。また、readPlan9Addrにはdefer f.Close()が追加され、関数の終了時に確実にファイルが閉じられるようになっています。

  2. 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.goTestSelfConnectでは、runtime.GOOSplan9の場合に、テストの反復回数nが1000から100に減らされています。これは、Plan 9を含む一部の非Linuxシステムでは、ローカルホストでリッスンしているものがないことを検出するのに時間がかかるため、テストの実行時間を短縮するための調整です。
    • UDPリスナーテストのスキップ: src/pkg/net/net_test.goTestUDPListenCloseでは、runtime.GOOSplan9の場合にテストがスキップされるようになりました。これは、Plan 9におけるUDPリスナーのクローズ動作が他のOSと異なり、このテストの意図に合わないか、またはテストが失敗するためと考えられます。
  3. デッドライン設定の非サポート: src/pkg/net/ipsock_plan9.goではplan9ListenerSetDeadlineメソッドが追加されましたが、これはsyscall.EPLAN9(操作がサポートされていない)を返します。同様に、src/pkg/net/tcpsock_plan9.gosrc/pkg/net/udpsock_plan9.goからは、TCPConnUDPConnSetDeadline, SetReadDeadline, SetWriteDeadlineメソッドが削除されました。これらの変更は、Plan 9のネットワークスタックが接続やリスナーに対するデッドライン(タイムアウト)設定を直接サポートしていないことを明確に示しています。Goのnetパッケージは、可能な限り共通のインターフェースを提供しますが、基盤となるOSが特定の機能を提供しない場合は、その旨を明示するか、関連するメソッドを削除するなどの対応が必要です。

  4. TCPリスナーのクローズ処理の改善: src/pkg/net/tcpsock_plan9.goでは、TCPListenerClose()メソッドが修正され、コントロールファイルに"hangup"コマンドを書き込んでからファイルを閉じるようになりました。これは、Plan 9においてTCPリスナーを適切にシャットダウンするためのより堅牢な方法です。単にファイルを閉じるだけでなく、明示的に接続を終了させるシグナルを送ることで、リソースが確実に解放されるようにします。

  5. エラー変数の移動: src/pkg/net/udpsock_posix.goにあったErrWriteToConnectedエラー変数が、より汎用的なsrc/pkg/net/udpsock.goに移動されました。これにより、このエラーがPOSIX固有のものではなく、UDPソケット全般に関連するものであることが明確になります。

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

このコミットにおけるコアとなるコードの変更箇所は以下の通りです。

  1. src/pkg/net/ipsock_plan9.goにおけるファイルディスクリプタのクローズ処理の追加:

    • readPlan9Addr関数にdefer f.Close()を追加。
    • startPlan9, dialPlan9, listenPlan9, acceptPlan9関数内のエラーパスにf.Close()呼び出しを追加。
    • plan9ListenerSetDeadlineメソッドを追加し、syscall.EPLAN9を返すように変更。
  2. src/pkg/net/tcpsock_plan9.goにおけるTCPListener.Close()の実装変更:

    • TCPConnからSetDeadline関連のメソッドを削除。
    • TCPListenerClose()メソッドで、コントロールファイルに"hangup"を書き込んでから閉じるように変更。
  3. src/pkg/net/udpsock_plan9.goにおけるUDPConnのデッドライン関連メソッドの削除:

    • UDPConnからSetDeadline, SetReadDeadline, SetWriteDeadlineメソッドを削除。
  4. テストファイルへの// +build !plan9ディレクティブの追加:

    • src/pkg/net/ipraw_test.go
    • src/pkg/net/multicast_test.go
    • src/pkg/net/unicast_test.go
  5. src/pkg/net/net_test.goにおけるTestUDPListenCloseのPlan 9でのスキップ:

    • runtime.GOOSplan9の場合にテストをスキップするロジックを追加。

コアとなるコードの解説

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()をエラーパスに追加することで、この問題を解決しています。特にreadPlan9Addrdefer f.Close()が追加されたことは、関数の正常終了時にも確実にリソースが解放されることを保証する重要な改善です。

また、plan9ListenerSetDeadlineメソッドが追加されましたが、これはsyscall.EPLAN9を返します。これは、Plan 9のネットワークスタックがリスナーに対するデッドライン設定を直接サポートしていないため、Goのインターフェースを満たしつつ、その制約を明示的に示すための実装です。

src/pkg/net/tcpsock_plan9.goの変更

TCPConnからSetDeadline関連のメソッドが削除されたのも、Plan 9がこれらの機能をサポートしていないためです。Goのnet.Connインターフェースはこれらのメソッドを要求しますが、OSが提供しない場合は、実装を削除するか、エラーを返すしかありません。この変更では、明示的に削除することで、Plan 9上でのTCPConnがこれらのデッドライン機能をサポートしないことを明確にしています。

TCPListenerClose()メソッドの変更は、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です。このリンクを辿ることで、このコミットに至るまでの議論や、関連するパッチセットの履歴を確認することができます。

参考にした情報源リンク