[インデックス 14535] ファイルの概要
このコミットは、Go言語の標準ライブラリである net
パッケージにおける、Plan 9オペレーティングシステム向けのネットワークAPIのドキュメント更新と同期を目的としています。具体的には、Plan 9固有のネットワークソケット実装(IPRaw、TCP、UDP、Unixドメインソケット)において、Conn
インターフェースの実装を簡素化し、共通の conn
構造体を利用するように変更しています。これにより、コードの重複を排除し、APIの一貫性を向上させています。また、io.ReaderFrom
インターフェースのフォールバック実装である genericReadFrom
関数が net
パッケージの共通部分に移動され、Plan 9固有のソケットタイプでも利用可能になっています。
コミット
- コミットハッシュ:
253ed02918f2909de500ca36d4867a87877666f6
- 作者: Anthony Martin ality@pbrane.org
- コミット日時: 2012年11月30日 金曜日 11:41:50 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/253ed02918f2909de500ca36d4867a87877666f6
元コミット内容
net: update docs and sync API for Plan 9
R=golang-dev, dave, mikioh.mikioh, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/6674043
変更の背景
このコミットの背景には、Go言語の net
パッケージが様々なオペレーティングシステム(Linux, macOS, Windows, Plan 9など)に対応する中で、各OS固有のネットワーク実装におけるAPIの一貫性とコードの保守性を向上させる必要がありました。特にPlan 9は、その独特なファイルシステムベースのI/Oモデルを持つため、他のPOSIX準拠OSとは異なるアプローチが必要とされます。
以前の実装では、IPConn
, TCPConn
, UDPConn
, UnixConn
といったPlan 9固有のネットワーク接続タイプが、それぞれ Conn
インターフェースのメソッド(Read
, Write
, LocalAddr
, RemoteAddr
, SetDeadline
など)を個別に実装していました。これらの実装の多くは、Plan 9ではサポートされていない機能に対して syscall.EPLAN9
エラーを返すだけのプレースホルダーとなっており、コードの重複と冗長性がありました。
このコミットは、これらの重複を解消し、共通の基盤となる conn
構造体を導入することで、Plan 9向けのネットワークAPIをより簡潔で保守しやすい形に「同期」させることを目的としています。また、FileListener
のドキュメント修正も含まれており、APIの振る舞いをより正確に反映しています。
前提知識の解説
Go言語の net
パッケージ
Go言語の net
パッケージは、ネットワークI/Oのプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワークプロトコルを扱うためのインターフェースや関数が含まれています。net.Conn
インターフェースは、汎用的なネットワーク接続を表し、Read
, Write
, Close
, LocalAddr
, RemoteAddr
, SetDeadline
などのメソッドを定義しています。
Plan 9 from Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。その最大の特徴は、すべてのリソース(ファイル、デバイス、ネットワーク接続など)がファイルシステムとして表現され、標準的なファイルI/O操作(open
, read
, write
, close
)を通じてアクセスされるという設計思想です。これにより、ネットワーク通信もファイルシステム上の特殊なファイルを読み書きするような形で実現されます。
syscall.EPLAN9
syscall.EPLAN9
は、Go言語の syscall
パッケージで定義されているエラーコードの一つです。これは、Plan 9オペレーティングシステムではサポートされていない操作や機能が呼び出された場合に返されるエラーを示します。このコミット以前のPlan 9固有のネットワークソケット実装では、サポートされていない Conn
インターフェースのメソッドがこのエラーを返していました。
io.ReaderFrom
インターフェース
io.ReaderFrom
インターフェースは、ReadFrom(r Reader) (n int64, err error)
メソッドを定義します。このインターフェースを実装する型は、別の io.Reader
からデータを効率的に読み込むことができます。例えば、ネットワーク接続において、sendfile
のようなOSレベルの最適化を利用して、ファイルから直接ネットワークソケットにデータを転送する際に使用されることがあります。
io.Copy
関数
io.Copy(dst Writer, src Reader) (written int64, err error)
は、src
から dst
へデータをコピーするGo言語の標準関数です。io.ReaderFrom
や io.WriterTo
インターフェースが実装されている場合はそれらを優先的に利用し、そうでない場合は内部的にバッファを使ってデータを読み書きします。
技術的詳細
このコミットの主要な変更点は、Plan 9向けのネットワークソケット実装におけるコードの重複排除とAPIの同期です。
-
IPConn
,TCPConn
,UDPConn
,UnixConn
の簡素化:- 以前は、これらの型が
Conn
インターフェースのすべてのメソッド(Read
,Write
,LocalAddr
,RemoteAddr
,SetDeadline
など)を個別に実装していました。これらのメソッドの多くは、Plan 9では直接サポートされていないため、単にsyscall.EPLAN9
を返すだけの冗長なコードでした。 - このコミットでは、これらの型から個別のメソッド実装を削除し、代わりに共通の埋め込みフィールド
conn
を持つ構造体に変更されました。 - 例:
type IPConn bool
からtype IPConn struct { conn }
へ変更。 - これにより、
conn
構造体がConn
インターフェースの共通部分を処理し、Plan 9固有のソケットタイプはそれぞれの特殊な振る舞い(例:ReadFromIP
,WriteToUDP
など)に集中できるようになります。
- 以前は、これらの型が
-
genericReadFrom
の移動と利用:genericReadFrom
関数は、io.ReaderFrom
インターフェースのフォールバック実装として機能します。これは、sendfile
のようなOS固有の最適化が利用できない場合に、io.Copy
を使用してデータをコピーする汎用的な方法を提供します。- 以前は
src/pkg/net/sock_posix.go
に定義されていましたが、このコミットでsrc/pkg/net/net.go
に移動されました。これにより、Plan 9を含むすべてのOSで共通して利用できるようになりました。 src/pkg/net/tcpsock_plan9.go
のTCPConn.ReadFrom
メソッドが、syscall.EPLAN9
を返す代わりにgenericReadFrom(c, r)
を呼び出すように変更されました。これは、Plan 9のTCP接続でもio.ReaderFrom
の汎用的な実装が利用可能になったことを意味します。
-
FileListener
のドキュメント修正:src/pkg/net/file_plan9.go
のFileListener
関数のコメントが修正されました。- 変更前:
Closing c does not affect l, and closing l does not affect c.
- 変更後:
Closing l does not affect f, and closing f does not affect l.
- これは、
FileListener
が返すListener
と、その元となるos.File
の間の関係をより正確に記述しています。Listener
を閉じても元のファイルには影響せず、ファイルを閉じてもListener
には影響しないということを明確にしています。
-
関数シグネチャの修正:
DialTCP
,DialUDP
,ReadFrom
,WriteToUDP
,WriteTo
など、いくつかのPlan 9固有のネットワーク関数の戻り値の型シグネチャが修正されました。これは、Go 1のリリースに向けてAPIの一貫性を保つための変更と考えられます。例えば、func DialTCP(...) (c *TCPConn, err error)
がfunc DialTCP(...) (*TCPConn, error)
に変更されています。これは、戻り値の変数名を省略するGoの慣習に合わせたものです。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、src/pkg/net/iprawsock_plan9.go
, src/pkg/net/udpsock_plan9.go
, src/pkg/net/unixsock_plan9.go
における IPConn
, UDPConn
, UnixConn
型の定義の変更と、それに伴う冗長なメソッドの実装の削除です。
src/pkg/net/iprawsock_plan9.go
の変更例:
--- a/src/pkg/net/iprawsock_plan9.go
+++ b/src/pkg/net/iprawsock_plan9.go
@@ -7,74 +7,14 @@
package net
import (
-"os"
"syscall"
"time"
)
// IPConn is the implementation of the Conn and PacketConn interfaces
// for IP network connections.
-type IPConn bool
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *IPConn) Read(b []byte) (int, error) {
-return 0, syscall.EPLAN9
-}
-
-// Write implements the Conn Write method.
-func (c *IPConn) Write(b []byte) (int, error) {
-return 0, syscall.EPLAN9
-}
-
-// LocalAddr returns the local network address.
-func (c *IPConn) LocalAddr() Addr {
-return nil
-}
-
-// RemoteAddr returns the remote network address.
-func (c *IPConn) RemoteAddr() Addr {
-return nil
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *IPConn) SetDeadline(t time.Time) error {
-return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *IPConn) SetReadDeadline(t time.Time) error {
-return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *IPConn) SetWriteDeadline(t time.Time) error {
-return syscall.EPLAN9
-}
-
-// SetReadBuffer sets the size of the operating system\'s receive
-// buffer associated with the connection.
-func (c *IPConn) SetReadBuffer(bytes int) error {
-return syscall.EPLAN9
-}
-
-// SetWriteBuffer sets the size of the operating system\'s transmit
-// buffer associated with the connection.
-func (c *IPConn) SetWriteBuffer(bytes int) error {
-return syscall.EPLAN9
-}
-
-// File returns a copy of the underlying os.File, set to blocking
-// mode. It is the caller\'s responsibility to close f when finished.\n-// Closing c does not affect f, and closing f does not affect c.\n-func (c *IPConn) File() (f *os.File, err error) {\n-return nil, syscall.EPLAN9\n-}\n-\n-// Close closes the IP connection.\n-func (c *IPConn) Close() error {\n-return syscall.EPLAN9
+type IPConn struct {
+ conn
}
// ReadFromIP reads an IP packet from c, copying the payload into b.
src/pkg/net/net.go
と src/pkg/net/sock_posix.go
における genericReadFrom
の移動:
--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -44,6 +44,7 @@ package net
import (
"errors"
+ "io"
"os"
"syscall"
"time"
@@ -363,3 +364,14 @@ func (e *DNSConfigError) Error() string {
func (e *DNSConfigError) Timeout() bool { return false }
func (e *DNSConfigError) Temporary() bool { return false }
+
+type writerOnly struct {
+ io.Writer
+}
+
+// Fallback implementation of io.ReaderFrom\'s ReadFrom, when sendfile isn\'t
+// applicable.
+func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
+ // Use wrapper to hide existing r.ReadFrom from io.Copy.
+ return io.Copy(writerOnly{w}, r)
+}
--- a/src/pkg/net/sock_posix.go
+++ b/src/pkg/net/sock_posix.go
@@ -9,7 +9,6 @@
package net
import (
- "io"
"syscall"
"time"
)
@@ -76,14 +75,3 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
fd.setAddr(laddr, raddr)
return fd, nil
}
-
-type writerOnly struct {
- io.Writer
-}
-
-// Fallback implementation of io.ReaderFrom\'s ReadFrom, when sendfile isn\'t
-// applicable.
-func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
- // Use wrapper to hide existing r.ReadFrom from io.Copy.
- return io.Copy(writerOnly{w}, r)
-}
コアとなるコードの解説
Plan 9固有のネットワーク接続型の簡素化
IPConn
, UDPConn
, UnixConn
といったPlan 9固有のネットワーク接続型は、以前は bool
型として定義され、その上で Conn
インターフェースの各メソッドがレシーバーとして *IPConn
(または *UDPConn
, *UnixConn
) を持つ形で実装されていました。しかし、これらのメソッドの多くはPlan 9では直接サポートされておらず、単に syscall.EPLAN9
を返すだけのスタブ実装でした。
このコミットでは、これらの型が struct { conn }
のように、共通の conn
構造体を埋め込む形に変更されました。conn
構造体は、Goの net
パッケージ内で定義されている、ネットワーク接続の共通の振る舞いをカプセル化する内部構造体です。これにより、IPConn
などは conn
が提供する共通の Conn
インターフェースの実装を継承し、Plan 9固有の特殊な処理(例: ReadFromIP
, WriteToUDP
)のみを自身で実装すればよくなりました。これにより、コードの重複が大幅に削減され、保守性が向上しました。
例えば、IPConn
の定義が type IPConn bool
から type IPConn struct { conn }
に変わったことで、Read
, Write
, LocalAddr
などのメソッドは IPConn
自体で実装する必要がなくなり、埋め込まれた conn
型のメソッドが自動的に呼び出されるようになります。もしPlan 9でこれらのメソッドが特別な振る舞いを必要とする場合は、IPConn
型でそれらのメソッドをオーバーライドすることができます。
genericReadFrom
の共通化
genericReadFrom
関数は、io.ReaderFrom
インターフェースの汎用的な実装を提供します。これは、io.Copy
を利用して io.Reader
から io.Writer
へデータをコピーするものです。この関数が src/pkg/net/sock_posix.go
から src/pkg/net/net.go
へ移動されたことで、Plan 9を含むすべてのOSでこの汎用的な実装が利用可能になりました。
特に、src/pkg/net/tcpsock_plan9.go
の TCPConn.ReadFrom
メソッドが return 0, syscall.EPLAN9
から return genericReadFrom(c, r)
に変更されたことは重要です。これにより、Plan 9のTCP接続でも io.ReaderFrom
インターフェースが適切に機能するようになり、io.Copy
を介した効率的なデータ転送が可能になりました。これは、Plan 9のファイルシステムベースのI/OモデルとGoの標準I/Oインターフェースとの間のギャップを埋める一歩となります。
関数シグネチャの統一
DialTCP
, DialUDP
などの関数シグネチャから戻り値の変数名が削除されたのは、Go言語のコーディングスタイルガイドラインに沿った変更です。Goでは、戻り値の変数名を明示的に指定することは必須ではなく、特にシンプルな関数では省略されることが一般的です。これにより、コードの簡潔性が向上します。
関連リンク
- Go言語
net
パッケージのドキュメント: https://pkg.go.dev/net - Plan 9 from Bell Labs 公式サイト: https://9p.io/plan9/
- Go言語の
io
パッケージのドキュメント: https://pkg.go.dev/io - Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - Go Code Review Comments (Effective Go): https://go.dev/doc/effective_go#commentary (戻り値の変数名に関する一般的なGoの慣習について)
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/net
ディレクトリ): https://github.com/golang/go - Go CL 6674043: https://golang.org/cl/6674043 (元の変更リスト)
- Plan 9のネットワークに関する情報 (一般的な概念理解のため):
- Plan 9: The Network File System: https://9p.io/sys/doc/fs/fs.html
- Plan 9: Networking: https://9p.io/sys/doc/net/net.html
- Go言語の
io.ReaderFrom
とio.Copy
の動作に関する一般的な情報:- Go by Example: Reading Files: https://gobyexample.com/reading-files
- Go: io.Copy and io.ReaderFrom: https://medium.com/@vladimir.gorej/go-io-copy-and-io-readerfrom-62222122222