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

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

このコミットは、Go言語の標準ライブラリnetパッケージにおける内部的なネットワークファイルディスクリプタ(netFD)のCloseReadおよびCloseWriteメソッドを、外部からアクセスできない非公開(unexported)メソッドに変更するものです。これにより、netパッケージの内部実装の詳細がカプセル化され、APIの安定性と安全性が向上します。

コミット

commit ebe5f203bfdf4dce75fff47189892d5f594d6133
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Apr 4 09:07:44 2014 +0900

    net: don't export netFD closeRead and closeWrite methods
    
    LGTM=iant
    R=iant
    CC=golang-codereviews
    https://golang.org/cl/83910043

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

https://github.com/golang/go/commit/ebe5f203bfdf4dce75fff47189892d5f594d6133

元コミット内容

net: don't export netFD closeRead and closeWrite methods

このコミットの目的は、netFD構造体のCloseReadおよびCloseWriteメソッドを非公開にすることです。

変更の背景

Go言語では、パッケージの外部からアクセス可能な識別子(関数、メソッド、変数、構造体フィールドなど)は、その名前の最初の文字を大文字にすることで「エクスポート(export)」されます。逆に、最初の文字を小文字にすると「非エクスポート(unexport)」され、そのパッケージ内でのみアクセス可能となります。

このコミットが行われた背景には、Go標準ライブラリの設計原則、特に「カプセル化」と「APIの安定性」があります。netFDは、ネットワーク接続の低レベルなファイルディスクリプタを抽象化するための内部構造体であり、そのメソッドは通常、netパッケージのより高レベルな型(例: net.TCPConn, net.UnixConn)によって内部的に利用されます。

netFD.CloseReadnetFD.CloseWriteがエクスポートされていると、外部のコードがこれらの低レベルなメソッドに直接アクセスできてしまいます。これは以下の問題を引き起こす可能性があります。

  1. 内部実装への依存: ユーザーが内部的なnetFDの実装詳細に依存するコードを書いてしまうと、将来的にnetパッケージの内部実装が変更された際に、ユーザーのコードが壊れる可能性があります。
  2. 不適切な使用: 低レベルなファイルディスクリプタ操作は複雑であり、誤った使用はリソースリークやデッドロックなど、予期せぬ問題を引き起こす可能性があります。
  3. APIの混乱: ユーザーにとって、どのメソッドが公開APIの一部であり、どのメソッドが内部的なものであるかが不明瞭になります。

これらの問題を避けるため、netFDCloseReadCloseWriteメソッドを非公開にすることで、netパッケージの内部構造を隠蔽し、よりクリーンで安定したAPIを提供することが目的とされました。ユーザーは引き続きnet.TCPConnnet.UnixConnのエクスポートされたCloseReadCloseWriteメソッドを使用できますが、その内部でnetFDの非公開メソッドが呼び出される形になります。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびネットワークプログラミングに関する前提知識が必要です。

  1. Go言語のエクスポートルール:

    • Goでは、識別子(変数、関数、型、メソッドなど)の最初の文字が大文字の場合、その識別子はパッケージの外部にエクスポートされ、他のパッケージからアクセス可能になります。
    • 最初の文字が小文字の場合、その識別子は非エクスポート(unexported)となり、その識別子が定義されているパッケージ内でのみアクセス可能です。
    • このルールは、Goにおけるカプセル化と情報隠蔽の基本的なメカニズムです。
  2. netパッケージとネットワークプログラミング:

    • Goのnetパッケージは、ネットワークI/Oプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワークプロトコルを扱うための機能が含まれています。
    • ファイルディスクリプタ (File Descriptor, FD): オペレーティングシステムがファイルやソケットなどのI/Oリソースを識別するために使用する抽象的なハンドルです。ネットワークプログラミングでは、ソケットがファイルディスクリプタとして扱われます。
    • netFD構造体: netパッケージの内部で、OSのファイルディスクリプタをラップし、ネットワーク操作(読み書き、シャットダウンなど)を管理するための構造体です。これはGoのnetパッケージの内部実装の詳細であり、通常、ユーザーが直接触れることはありません。
    • 半クローズ (Half-close): TCPなどのストリーム指向ソケットでは、接続の一方の方向(読み取り側または書き込み側)のみをシャットダウンする機能があります。これは、shutdownシステムコール(Unix系)やclosesocketshutdownの組み合わせ(Windows)によって実現されます。
      • CloseRead(): 接続の読み取り側をシャットダウンします。これにより、それ以上データを受信できなくなります。
      • CloseWrite(): 接続の書き込み側をシャットダウンします。これにより、それ以上データを送信できなくなります。
    • syscallパッケージ: Goのsyscallパッケージは、低レベルなオペレーティングシステムプリミティブへのアクセスを提供します。ネットワーク操作においては、syscall.SHUT_RD(読み取り側シャットダウン)、syscall.SHUT_WR(書き込み側シャットダウン)などが使用されます。
  3. Goのインターフェースと実装:

    • Goでは、インターフェースはメソッドのシグネチャの集合を定義します。具体的な型がそのインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たします。
    • net.Connインターフェースは、一般的なネットワーク接続の振る舞いを定義しますが、CloseReadCloseWriteメソッドは含まれていません。これは、すべての種類のネットワーク接続(例: UDP)が半クローズの概念を持つわけではないためです。
    • net.TCPConnnet.UnixConnのような具体的な接続型は、net.Connインターフェースを満たしつつ、TCPやUnixドメインソケットに特有のCloseReadCloseWriteメソッドを提供します。これらのメソッドは、内部的にnetFDの対応するメソッドを呼び出します。

技術的詳細

このコミットの技術的な核心は、Go言語のエクスポートルールを適用し、netFD構造体の特定のメソッドの可視性を変更することにあります。

変更前は、netFD構造体にはCloseRead()CloseWrite()というメソッドがありました。Goのエクスポートルールに従い、これらのメソッドは名前がCで始まるため、netパッケージの外部からもアクセス可能な公開メソッドでした。

変更後、これらのメソッドはcloseRead()closeWrite()にリネームされました。これにより、メソッド名の最初の文字が小文字になったため、これらのメソッドはnetパッケージ内でのみアクセス可能な非公開メソッドとなりました。

この変更は、以下のファイルに影響を与えています。

  • src/pkg/net/fd_plan9.go: Plan 9オペレーティングシステム向けのファイルディスクリプタ実装。
  • src/pkg/net/fd_unix.go: Unix系オペレーティングシステム向けのファイルディスクリプタ実装。
  • src/pkg/net/fd_windows.go: Windowsオペレーティングシステム向けのファイルディスクリプタ実装。
  • src/pkg/net/tcpsock_plan9.go: Plan 9向けのTCPソケット実装。
  • src/pkg/net/tcpsock_posix.go: POSIX準拠システム向けのTCPソケット実装。
  • src/pkg/net/unixsock_posix.go: POSIX準拠システム向けのUnixドメインソケット実装。

これらのファイルでは、netFDCloseRead()CloseWrite()メソッドの定義がcloseRead()closeWrite()に変更され、それに伴い、net.TCPConnnet.UnixConnなどの高レベルな型からnetFDのこれらのメソッドを呼び出している箇所も、新しい非公開名に更新されています。

例えば、net/tcpsock_posix.goTCPConn.CloseRead()メソッドは、変更前はc.fd.CloseRead()を呼び出していましたが、変更後はc.fd.closeRead()を呼び出すように修正されています。これにより、TCPConn.CloseRead()は引き続き公開APIとして機能しますが、その内部実装はnetFDの非公開メソッドに依存する形となり、netFDの内部詳細が外部に漏れることがなくなります。

この変更は、Goの標準ライブラリが内部実装と公開APIを明確に分離し、堅牢性と保守性を高めるための典型的なリファクタリングの一例です。

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

このコミットにおけるコアとなるコードの変更は、主に以下の2点です。

  1. netFD構造体のメソッド名の変更:

    • CloseRead() -> closeRead()
    • CloseWrite() -> closeWrite() これらの変更は、src/pkg/net/fd_plan9.go, src/pkg/net/fd_unix.go, src/pkg/net/fd_windows.go の各ファイルで行われています。
  2. 変更されたメソッドの呼び出し箇所の更新:

    • net.TCPConnnet.UnixConnなどの高レベルな型が、内部でnetFDのこれらのメソッドを呼び出している箇所を、新しい非公開名に更新。 これらの変更は、src/pkg/net/tcpsock_plan9.go, src/pkg/net/tcpsock_posix.go, src/pkg/net/unixsock_posix.go の各ファイルで行われています。

具体的な変更例を以下に示します。

src/pkg/net/fd_unix.go (メソッド定義の変更)

--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -208,11 +208,11 @@ func (fd *netFD) shutdown(how int) error {
 	return nil
 }
 
-func (fd *netFD) CloseRead() error {
+func (fd *netFD) closeRead() error {
 	return fd.shutdown(syscall.SHUT_RD)
 }
 
-func (fd *netFD) CloseWrite() error {
+func (fd *netFD) closeWrite() error {
 	return fd.shutdown(syscall.SHUT_WR)
 }

src/pkg/net/tcpsock_posix.go (メソッド呼び出しの変更)

--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -78,7 +78,7 @@ func (c *TCPConn) CloseRead() error {
 	if !c.ok() {
 		return syscall.EINVAL
 	}
-	return c.fd.CloseRead()
+	return c.fd.closeRead()
 }
 
 // CloseWrite shuts down the writing side of the TCP connection.
@@ -87,7 +87,7 @@ func (c *TCPConn) CloseWrite() error {
 	if !c.ok() {
 		return syscall.EINVAL
 	}
-	return c.fd.CloseWrite()
+	return c.fd.closeWrite()
 }

コアとなるコードの解説

上記のコード変更は、Go言語における「エクスポートされた識別子」と「非エクスポートされた識別子」の概念を直接的に示しています。

  • func (fd *netFD) CloseRead() error から func (fd *netFD) closeRead() error への変更:

    • これは、netFD型に紐付けられたCloseReadメソッドの名前をcloseReadに変更したものです。
    • Goのルールにより、CloseReadはパッケージ外部からアクセス可能(エクスポート済み)でしたが、closeReadはパッケージ内部からのみアクセス可能(非エクスポート)になります。
    • 同様の変更がCloseWriteメソッドにも適用されています。
    • これらのメソッドは、内部的にfd.shutdown()を呼び出して、OSレベルでのソケットの読み取り側または書き込み側のシャットダウン(半クローズ)を実行します。
  • return c.fd.CloseRead() から return c.fd.closeRead() への変更:

    • これは、net.TCPConn(またはnet.UnixConn)のCloseReadメソッドの実装内で、内部的に保持しているnetFDインスタンス(c.fd)のCloseReadメソッドを呼び出していた箇所を、新しい非公開メソッドcloseReadに修正したものです。
    • TCPConn.CloseRead()自体は引き続きエクスポートされた公開メソッドであり、ユーザーはこれまで通りこのメソッドを呼び出すことができます。
    • しかし、その内部で呼び出されるnetFDのメソッドが非公開になったことで、netFDの低レベルな実装詳細が外部に直接公開されることがなくなりました。これにより、netパッケージの内部構造がより適切にカプセル化され、外部からの不適切なアクセスや依存を防ぐことができます。

この変更は、Goの標準ライブラリが、ユーザーに提供する高レベルなAPIと、そのAPIを支える低レベルな内部実装との間に明確な境界線を引くための重要なステップです。これにより、ライブラリの保守性が向上し、将来的な内部変更がユーザーコードに与える影響を最小限に抑えることができます。

関連リンク

参考にした情報源リンク