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

[インデックス 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.ReaderFromio.WriterTo インターフェースが実装されている場合はそれらを優先的に利用し、そうでない場合は内部的にバッファを使ってデータを読み書きします。

技術的詳細

このコミットの主要な変更点は、Plan 9向けのネットワークソケット実装におけるコードの重複排除とAPIの同期です。

  1. 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 など)に集中できるようになります。
  2. 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.goTCPConn.ReadFrom メソッドが、syscall.EPLAN9 を返す代わりに genericReadFrom(c, r) を呼び出すように変更されました。これは、Plan 9のTCP接続でも io.ReaderFrom の汎用的な実装が利用可能になったことを意味します。
  3. FileListener のドキュメント修正:

    • src/pkg/net/file_plan9.goFileListener 関数のコメントが修正されました。
    • 変更前: 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 には影響しないということを明確にしています。
  4. 関数シグネチャの修正:

    • 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.gosrc/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.goTCPConn.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では、戻り値の変数名を明示的に指定することは必須ではなく、特にシンプルな関数では省略されることが一般的です。これにより、コードの簡潔性が向上します。

関連リンク

参考にした情報源リンク