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

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

このコミットは、Go言語の標準ライブラリnetパッケージにおけるデッドライン設定関連のヘルパー関数を、netFD構造体のメソッドとして再編成するものです。これにより、コードの可読性と保守性が向上し、異なるプラットフォーム間でのデッドライン処理の一貫性が確保されます。

コミット

commit 39a7017c26eeb1b5514ca6b756e1c7eede170e6b
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Tue Aug 13 20:00:58 2013 +0900

    net: make deadline helpers join to netFD as its methods
    
    Just for readability reasons; to prevent overlooking deadline stuff
    across over platforms.
    
    R=golang-dev, dvyukov
    CC=golang-dev
    https://golang.org/cl/8656044

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

https://github.com/golang/go/commit/39a7017c26eeb1b5514ca6b756e1c7eede170e6b

元コミット内容

このコミットは、netパッケージ内のデッドライン設定に関連するヘルパー関数(setDeadline, setReadDeadline, setWriteDeadline)を、netFD構造体のメソッドとして再定義するものです。主な目的は、コードの可読性を高め、異なるプラットフォーム(例: Plan 9, Unix系OS)間でのデッドライン処理のロジックを見落とすことを防ぐことにあります。

変更の背景

Go言語のnetパッケージは、ネットワークI/O操作を抽象化し、ソケット通信などの低レベルな処理を扱いやすくするための機能を提供します。このパッケージでは、ネットワーク接続における読み書きのタイムアウト(デッドライン)を設定する機能が重要です。

このコミットが行われた背景には、以下の理由が考えられます。

  1. 可読性の向上: 以前は、デッドラインを設定する関数がnetFD構造体とは独立したグローバル関数として存在していました。これにより、netFDインスタンスに対してデッドラインを設定する際に、どの関数がどのnetFDインスタンスに作用するのかが直感的に分かりにくい場合がありました。メソッドとして定義することで、fd.setDeadline(t)のように、対象となるnetFDインスタンスに直接関連付けられるため、コードの意図がより明確になります。
  2. 一貫性の確保: Go言語では、特定のデータ型に関連する操作は、その型のメソッドとして定義することが推奨されるイディオムです。netFDはネットワークファイルディスクリプタを抽象化する重要な構造体であり、デッドライン設定はそのnetFDの状態に直接影響を与える操作です。これをメソッドとして統一することで、netパッケージ全体の設計の一貫性が向上します。
  3. プラットフォーム間の見落とし防止: コミットメッセージにある「to prevent overlooking deadline stuff across over platforms」という記述は、異なるOS(Unix系、Plan 9など)でデッドライン処理の実装が異なる場合に、共通のインターフェース(netFDメソッド)を通じてアクセスすることで、プラットフォーム固有の差異による見落としやバグを防ぐ狙いがあることを示唆しています。メソッドとして定義することで、各プラットフォームの実装がnetFDの内部にカプセル化され、外部からは統一されたインターフェースで利用できるようになります。

前提知識の解説

Go言語のnetパッケージ

netパッケージは、TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うための基本的なインターフェースと実装を提供します。このパッケージは、低レベルなソケット操作を抽象化し、高レベルなネットワークプログラミングを可能にします。

netFD構造体

netFDnetパッケージ内部で使用される構造体で、ネットワークファイルディスクリプタ(File Descriptor)を抽象化したものです。ファイルディスクリプタは、OSがファイルやソケットなどのI/Oリソースを識別するために使用する整数値です。netFDは、このファイルディスクリプタと、それに関連するデッドライン情報(読み込みデッドライン、書き込みデッドライン)やポーリング記述子(pollDesc)などを管理します。

デッドライン(Deadlines)

ネットワークプログラミングにおいて、デッドラインはI/O操作のタイムアウトを設定するために使用されます。例えば、ネットワークからのデータ読み込みが指定された時間内に完了しない場合、操作はタイムアウトエラーを返します。Goのnet.Connインターフェースには、SetDeadline, SetReadDeadline, SetWriteDeadlineというメソッドがあり、それぞれ読み書き両方、読み込みのみ、書き込みのみのデッドラインを設定できます。これらのメソッドは内部的にnetFDのデッドライン設定機能を利用しています。

Go言語のメソッドと関数

Go言語では、関数は独立して定義されますが、メソッドは特定の型に関連付けられて定義されます。メソッドはレシーバ引数(func (recv Type) MethodName(...))を持ち、これによりその型のインスタンスに対して操作を行うことができます。メソッドとして定義することで、その操作が特定の型の振る舞いの一部であることが明確になり、オブジェクト指向的な設計に近づきます。

syscallパッケージ

syscallパッケージは、GoプログラムからOSのシステムコールを直接呼び出すための機能を提供します。ネットワーク操作の多くはOSのシステムコールに依存するため、netパッケージの内部ではsyscallパッケージが頻繁に利用されます。

技術的詳細

このコミットの技術的な変更は、主に以下の点に集約されます。

  1. 関数のメソッド化:

    • func setDeadline(fd *netFD, t time.Time) error
    • func setReadDeadline(fd *netFD, t time.Time) error
    • func setWriteDeadline(fd *netFD, t time.Time) error これらの関数が、それぞれnetFD構造体のメソッドとして再定義されました。
    • func (fd *netFD) setDeadline(t time.Time) error
    • func (fd *netFD) setReadDeadline(t time.Time) error
    • func (fd *netFD) setWriteDeadline(t time.Time) error これにより、これらの関数を呼び出す際には、setDeadline(c.fd, t)のようにnetFDインスタンスを引数として渡す代わりに、c.fd.setDeadline(t)のようにメソッドとして呼び出す形に変わりました。
  2. プラットフォーム固有の実装の調整:

    • src/pkg/net/fd_plan9.go: Plan 9 OS向けのデッドライン設定関数がメソッド化されました。Plan 9ではデッドラインがサポートされていないため、syscall.EPLAN9エラーを返す実装になっています。
    • src/pkg/net/fd_poll_runtime.go: Goランタイムのポーリングメカニズムを利用するデッドライン設定関数がメソッド化されました。ここでは、setDeadlineImplという内部ヘルパー関数が引き続き利用されます。
    • src/pkg/net/fd_poll_unix.go: Unix系OS向けのデッドライン設定関数がメソッド化されました。ここでは、pollDesc構造体のrdeadline(読み込みデッドライン)とwdeadline(書き込みデッドライン)が直接操作されます。特に、setDeadlineメソッドはsetReadDeadlinesetWriteDeadlineを内部で呼び出す形に変更されています。
  3. net.Connインターフェースの実装への影響: net.ConnインターフェースのSetDeadline, SetReadDeadline, SetWriteDeadlineメソッドの実装(src/pkg/net/net.go内)が、内部で呼び出すデッドライン設定関数を、netFDのメソッド呼び出しに切り替えました。例えば、c.fdnetFDのインスタンスである場合、setDeadline(c.fd, t)からc.fd.setDeadline(t)に変更されています。

  4. その他のファイルへの影響: src/pkg/net/sock_posix.go, src/pkg/net/tcpsock_plan9.go, src/pkg/net/tcpsock_posix.go, src/pkg/net/unixsock_posix.goなど、netFDのデッドライン設定関数を呼び出していた他のファイルも、同様にメソッド呼び出しに修正されています。

この変更は機能的な変更ではなく、純粋なリファクタリングです。外部から見たnetパッケージのAPI(net.Connのデッドラインメソッドなど)の振る舞いは変わりませんが、内部実装の構造が改善され、よりGoらしいイディオムに沿った形になりました。これにより、将来的なメンテナンスや機能拡張が容易になります。

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

このコミットにおけるコアとなる変更は、setDeadline, setReadDeadline, setWriteDeadlineの各関数がnetFD構造体のメソッドになった点です。

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

--- a/src/pkg/net/fd_plan9.go
+++ b/src/pkg/net/fd_plan9.go
@@ -108,15 +108,15 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
 	return os.NewFile(uintptr(dfd), s), nil
 }
 
-func setDeadline(fd *netFD, t time.Time) error {
+func (fd *netFD) setDeadline(t time.Time) error {
 	return syscall.EPLAN9
 }
 
-func setReadDeadline(fd *netFD, t time.Time) error {
+func (fd *netFD) setReadDeadline(t time.Time) error {
 	return syscall.EPLAN9
 }
 
-func setWriteDeadline(fd *netFD, t time.Time) error {
+func (fd *netFD) setWriteDeadline(t time.Time) error {
 	return syscall.EPLAN9
 }

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

--- a/src/pkg/net/fd_poll_runtime.go
+++ b/src/pkg/net/fd_poll_runtime.go
@@ -115,16 +115,16 @@ func convertErr(res int) error {
 	panic("unreachable")
 }
 
-func setReadDeadline(fd *netFD, t time.Time) error {
-	return setDeadlineImpl(fd, t, 'r')
+func (fd *netFD) setDeadline(t time.Time) error {
+	return setDeadlineImpl(fd, t, 'r'+'w')
 }
 
-func setWriteDeadline(fd *netFD, t time.Time) error {
-	return setDeadlineImpl(fd, t, 'w')
+func (fd *netFD) setReadDeadline(t time.Time) error {
+	return setDeadlineImpl(fd, t, 'r')
 }
 
-func setDeadline(fd *netFD, t time.Time) error {
-	return setDeadlineImpl(fd, t, 'r'+'w')
+func (fd *netFD) setWriteDeadline(t time.Time) error {
+	return setDeadlineImpl(fd, t, 'w')
 }
 
 func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {

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

--- a/src/pkg/net/fd_poll_unix.go
+++ b/src/pkg/net/fd_poll_unix.go
@@ -353,18 +353,18 @@ func (pd *pollDesc) Init(fd *netFD) error {
 
 // TODO(dfc) these unused error returns could be removed
 
-func setReadDeadline(fd *netFD, t time.Time) error {
-	fd.pd.rdeadline.setTime(t)
+func (fd *netFD) setDeadline(t time.Time) error {
+	fd.setReadDeadline(t)
+	fd.setWriteDeadline(t)
 	return nil
 }
 
-func setWriteDeadline(fd *netFD, t time.Time) error {
-	fd.pd.wdeadline.setTime(t)
+func (fd *netFD) setReadDeadline(t time.Time) error {
+	fd.pd.rdeadline.setTime(t)
 	return nil
 }
 
-func setDeadline(fd *netFD, t time.Time) error {
-	setReadDeadline(fd, t)
-	setWriteDeadline(fd, t)
+func (fd *netFD) setWriteDeadline(t time.Time) error {
+	fd.pd.wdeadline.setTime(t)
 	return nil
 }

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

--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -160,7 +160,7 @@ func (c *conn) SetDeadline(t time.Time) error {
 	if !c.ok() {
 		return syscall.EINVAL
 	}
-	return setDeadline(c.fd, t)
+	return c.fd.setDeadline(t)
 }
 
 // SetReadDeadline implements the Conn SetReadDeadline method.
@@ -168,7 +168,7 @@ func (c *conn) SetReadDeadline(t time.Time) error {
 	if !c.ok() {
 		return syscall.EINVAL
 	}
-	return setReadDeadline(c.fd, t)
+	return c.fd.setReadDeadline(t)
 }
 
 // SetWriteDeadline implements the Conn SetWriteDeadline method.
@@ -176,7 +176,7 @@ func (c *conn) SetWriteDeadline(t time.Time) error {
 	if !c.ok() {
 		return syscall.EINVAL
 	}
-	return setWriteDeadline(c.fd, t)
+	return c.fd.setWriteDeadline(t)
 }
 
 // SetReadBuffer sets the size of the operating system's

コアとなるコードの解説

上記のコード変更は、netFDに関連するデッドライン設定ロジックを、独立した関数からnetFD構造体のメソッドへと移行させています。

  • fd_plan9.go: Plan 9 OSではデッドラインがサポートされていないため、setDeadline, setReadDeadline, setWriteDeadlineの各メソッドは、いずれもsyscall.EPLAN9エラーを返します。これは、Plan 9環境でのデッドライン設定が機能しないことを明示しています。変更前は独立した関数でしたが、変更後はnetFDのメソッドとして定義され、fdインスタンスを通じて呼び出されるようになります。

  • fd_poll_runtime.go: このファイルは、Goランタイムの内部ポーリングメカニズムを利用してデッドラインを処理します。

    • 変更前は、setReadDeadline, setWriteDeadline, setDeadlineがそれぞれ独立した関数として定義され、setDeadlineImplという内部関数を呼び出していました。
    • 変更後、これらの関数はnetFDのメソッドになりました。特に注目すべきは、func (fd *netFD) setDeadline(t time.Time) errorsetDeadlineImpl(fd, t, 'r'+'w')を呼び出すようになった点です。これは、読み書き両方のデッドラインを設定することを意味します。また、setReadDeadlinesetWriteDeadlineもそれぞれ対応するモードでsetDeadlineImplを呼び出します。
  • fd_poll_unix.go: Unix系OSにおけるデッドライン処理の実装です。

    • 変更前は、setReadDeadline, setWriteDeadline, setDeadlineが独立した関数でした。setDeadlineは内部でsetReadDeadlinesetWriteDeadlineを呼び出していました。
    • 変更後、これらもnetFDのメソッドになりました。func (fd *netFD) setDeadline(t time.Time) errorは、内部でfd.setReadDeadline(t)fd.setWriteDeadline(t)を呼び出すことで、読み書き両方のデッドラインを設定します。setReadDeadlinesetWriteDeadlineは、それぞれfd.pd.rdeadline.setTime(t)fd.pd.wdeadline.setTime(t)を直接呼び出し、ポーリング記述子(pollDesc)内のデッドライン時刻を設定します。
  • net.go: net.Connインターフェースの具体的な実装であるconn構造体のメソッド(SetDeadline, SetReadDeadline, SetWriteDeadline)が変更されました。

    • 以前は、setDeadline(c.fd, t)のように、c.fdnetFDインスタンス)を引数として独立した関数を呼び出していました。
    • 変更後は、c.fd.setDeadline(t)のように、netFDインスタンスのメソッドを直接呼び出す形になりました。これにより、connのデッドライン設定が、その内部のnetFDインスタンスの振る舞いとしてより明確に表現されるようになりました。

これらの変更は、Go言語のイディオムに沿ったリファクタリングであり、netFDがデッドライン設定という自身の状態に関連する操作を自身で管理するという、より自然な設計になっています。これにより、コードの意図が明確になり、将来的な変更やデバッグが容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: netパッケージ, syscallパッケージ
  • Go言語のソースコード: src/pkg/net/ ディレクトリ内の関連ファイル
  • Go言語におけるメソッドと関数の概念に関する一般的な情報源