[インデックス 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操作を抽象化し、ソケット通信などの低レベルな処理を扱いやすくするための機能を提供します。このパッケージでは、ネットワーク接続における読み書きのタイムアウト(デッドライン)を設定する機能が重要です。
このコミットが行われた背景には、以下の理由が考えられます。
- 可読性の向上: 以前は、デッドラインを設定する関数が
netFD
構造体とは独立したグローバル関数として存在していました。これにより、netFD
インスタンスに対してデッドラインを設定する際に、どの関数がどのnetFD
インスタンスに作用するのかが直感的に分かりにくい場合がありました。メソッドとして定義することで、fd.setDeadline(t)
のように、対象となるnetFD
インスタンスに直接関連付けられるため、コードの意図がより明確になります。 - 一貫性の確保: Go言語では、特定のデータ型に関連する操作は、その型のメソッドとして定義することが推奨されるイディオムです。
netFD
はネットワークファイルディスクリプタを抽象化する重要な構造体であり、デッドライン設定はそのnetFD
の状態に直接影響を与える操作です。これをメソッドとして統一することで、net
パッケージ全体の設計の一貫性が向上します。 - プラットフォーム間の見落とし防止: コミットメッセージにある「to prevent overlooking deadline stuff across over platforms」という記述は、異なるOS(Unix系、Plan 9など)でデッドライン処理の実装が異なる場合に、共通のインターフェース(
netFD
メソッド)を通じてアクセスすることで、プラットフォーム固有の差異による見落としやバグを防ぐ狙いがあることを示唆しています。メソッドとして定義することで、各プラットフォームの実装がnetFD
の内部にカプセル化され、外部からは統一されたインターフェースで利用できるようになります。
前提知識の解説
Go言語のnet
パッケージ
net
パッケージは、TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うための基本的なインターフェースと実装を提供します。このパッケージは、低レベルなソケット操作を抽象化し、高レベルなネットワークプログラミングを可能にします。
netFD
構造体
netFD
はnet
パッケージ内部で使用される構造体で、ネットワークファイルディスクリプタ(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
パッケージが頻繁に利用されます。
技術的詳細
このコミットの技術的な変更は、主に以下の点に集約されます。
-
関数のメソッド化:
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)
のようにメソッドとして呼び出す形に変わりました。
-
プラットフォーム固有の実装の調整:
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
メソッドはsetReadDeadline
とsetWriteDeadline
を内部で呼び出す形に変更されています。
-
net.Conn
インターフェースの実装への影響:net.Conn
インターフェースのSetDeadline
,SetReadDeadline
,SetWriteDeadline
メソッドの実装(src/pkg/net/net.go
内)が、内部で呼び出すデッドライン設定関数を、netFD
のメソッド呼び出しに切り替えました。例えば、c.fd
がnetFD
のインスタンスである場合、setDeadline(c.fd, t)
からc.fd.setDeadline(t)
に変更されています。 -
その他のファイルへの影響:
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) error
がsetDeadlineImpl(fd, t, 'r'+'w')
を呼び出すようになった点です。これは、読み書き両方のデッドラインを設定することを意味します。また、setReadDeadline
とsetWriteDeadline
もそれぞれ対応するモードでsetDeadlineImpl
を呼び出します。
- 変更前は、
-
fd_poll_unix.go
: Unix系OSにおけるデッドライン処理の実装です。- 変更前は、
setReadDeadline
,setWriteDeadline
,setDeadline
が独立した関数でした。setDeadline
は内部でsetReadDeadline
とsetWriteDeadline
を呼び出していました。 - 変更後、これらも
netFD
のメソッドになりました。func (fd *netFD) setDeadline(t time.Time) error
は、内部でfd.setReadDeadline(t)
とfd.setWriteDeadline(t)
を呼び出すことで、読み書き両方のデッドラインを設定します。setReadDeadline
とsetWriteDeadline
は、それぞれfd.pd.rdeadline.setTime(t)
とfd.pd.wdeadline.setTime(t)
を直接呼び出し、ポーリング記述子(pollDesc
)内のデッドライン時刻を設定します。
- 変更前は、
-
net.go
:net.Conn
インターフェースの具体的な実装であるconn
構造体のメソッド(SetDeadline
,SetReadDeadline
,SetWriteDeadline
)が変更されました。- 以前は、
setDeadline(c.fd, t)
のように、c.fd
(netFD
インスタンス)を引数として独立した関数を呼び出していました。 - 変更後は、
c.fd.setDeadline(t)
のように、netFD
インスタンスのメソッドを直接呼び出す形になりました。これにより、conn
のデッドライン設定が、その内部のnetFD
インスタンスの振る舞いとしてより明確に表現されるようになりました。
- 以前は、
これらの変更は、Go言語のイディオムに沿ったリファクタリングであり、netFD
がデッドライン設定という自身の状態に関連する操作を自身で管理するという、より自然な設計になっています。これにより、コードの意図が明確になり、将来的な変更やデバッグが容易になります。
関連リンク
- Go CL 8656044: https://golang.org/cl/8656044
参考にした情報源リンク
- Go言語の公式ドキュメント:
net
パッケージ,syscall
パッケージ - Go言語のソースコード:
src/pkg/net/
ディレクトリ内の関連ファイル - Go言語におけるメソッドと関数の概念に関する一般的な情報源