[インデックス 12102] ファイルの概要
このコミットは、Go言語の標準ライブラリ net
パッケージにおける DialTCP
関数の挙動を修正し、特にARMアーキテクチャでのビルド問題を解決するためのものです。既存の回避策を拡張し、DialTCP
がリモートアドレス(raddr
)をnil
として返す可能性のある、特定かつ不明な条件下での問題を緩和することを目的としています。
コミット
commit 152d806b169a54564a21fd91d5cd3fb3cee1a5dc
Author: Rob Pike <r@golang.org>
Date: Tue Feb 21 16:48:05 2012 +1100
net: extend the workaround to DialTCP to try to get arm building again.
Awfulness by the bucket that we hope
Fixes #3057.
R=golang-dev, mikioh.mikioh, dsymonds, r, rsc
CC=golang-dev
https://golang.org/cl/5687060
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/152d806b169a54564a21fd91d5cd3fb3cee1a5dc
元コミット内容
このコミットは、net
パッケージのDialTCP
関数における既存の回避策を拡張し、特にARMアーキテクチャでのビルドが再び可能になるようにするためのものです。コミットメッセージには「Awfulness by the bucket that we hope」(願わくば、このひどい状況が改善されることを)という表現があり、当時の問題の複雑さと、この変更が根本的な解決ではなく一時的な回避策であることを示唆しています。また、Fixes #3057
と記載されており、GoのIssueトラッカーのIssue 3057を修正するものであることがわかります。
変更の背景
この変更の背景には、Go言語のnet
パッケージ、特にDialTCP
関数が、特定の条件下(特にARMアーキテクチャ環境下)で、確立されたTCP接続のリモートアドレス(raddr
)をnil
として返すというバグがありました。通常、接続が成功した場合、リモートアドレスは常に有効な値を持つことが期待されます。しかし、このバグにより、net.Conn
インターフェースのRemoteAddr()
メソッドがnil
を返し、アプリケーションが予期せぬエラーやパニックを引き起こす可能性がありました。
Issue #3057では、この問題が報告されており、DialTCP
が内部的に呼び出すGetpeername
システムコールが、一部のプラットフォーム(特にARM)で失敗することが原因であると推測されていました。Getpeername
はソケットのピア(接続相手)のアドレスを取得するためのシステムコールですが、そのエラーが適切に処理されず、結果としてraddr
がnil
になるケースがあったようです。
このコミットは、このnil raddr
問題に対する既存の回避策をさらに強化し、特にARM環境でのGoのビルドと実行を安定させることを目的としています。コミットメッセージの「Awfulness by the bucket that we hope」という表現は、この問題が根本的な原因が特定しにくい、厄介なバグであり、この変更が完全な解決ではなく、一時的な対処療法であることを示唆しています。
前提知識の解説
net
パッケージ: Go言語の標準ライブラリで、ネットワークI/O機能を提供します。TCP/UDP接続、DNSルックアップなどが含まれます。DialTCP
関数:net
パッケージの一部で、指定されたネットワークアドレス(laddr
:ローカルアドレス、raddr
:リモートアドレス)を使用してTCP接続を確立するために使用されます。成功すると*TCPConn
オブジェクトを返します。*TCPConn
: 確立されたTCP接続を表す構造体です。RemoteAddr()
メソッドを持ち、接続先のリモートアドレスを返します。sockaddr
: ソケットアドレスを表す汎用的なデータ構造です。OSのシステムコール(例:bind
,connect
,getpeername
)で使用されます。Getpeername
システムコール: 接続されたソケットのピア(リモートエンド)のアドレスを取得するために使用されるPOSIXシステムコールです。selfConnect
: ネットワーク接続が自分自身(ローカルホスト)に対して行われたかどうかを検出するためのロジックです。これは、特定のネットワークプロトコルやテストシナリオで重要になることがあります。- ARMアーキテクチャ: スマートフォン、タブレット、組み込みシステムなどで広く使用されているプロセッサアーキテクチャです。特定のシステムコールやカーネルの挙動が、他のアーキテクチャ(x86など)と異なる場合があります。
panic
: Go言語における回復不可能なエラー状態です。通常、プログラムの実行を停止させます。開発中のデバッグ目的で一時的に挿入されることがあります。- 回避策 (Workaround): 問題の根本原因を解決するのではなく、その影響を一時的に回避するための対処法です。
技術的詳細
このコミットは、主にsrc/pkg/net/ipsock_posix.go
とsrc/pkg/net/tcpsock_posix.go
の2つのファイルに変更を加えています。
-
src/pkg/net/ipsock_posix.go
の変更:internetSocket
関数内で、ra == nil
の場合にpanic
を引き起こしていたデバッグ用のコードが削除されました。このpanic
は、selfConnect
のデバッグ中にraddr
がnil
になる状況を特定するために一時的に追加されていたものです。この削除は、この特定のデバッグ用チェックがもはや必要ないか、あるいはより広範な回避策の一部として統合されたことを示唆しています。
-
src/pkg/net/tcpsock_posix.go
の変更:sockaddrToTCP
関数内のpanic
メッセージが変更されました。以前は「TODO(r): Diagnose when we will turn a non-nil sockaddr into a nil. Part of diagnosing the selfConnect bug.」というデバッグ目的のコメントが含まれていましたが、これが「Diagnose when we will turn a non-nil sockaddr into a nil.」と簡潔になり、デバッグの段階から一般的な診断メッセージへと変更されています。DialTCP
関数から、checkRaddr
というヘルパー関数とその呼び出しが削除されました。このcheckRaddr
関数もまた、fd.raddr
がnil
である場合にpanic
を引き起こすデバッグ用のチェックでした。この削除は、ipsock_posix.go
での変更と同様に、この特定のデバッグ用チェックが不要になったことを意味します。- 最も重要な変更は、
selfConnect
関数内に追加された新しいロジックです。
この新しいコードは、func selfConnect(fd *netFD) bool { // The socket constructor can return an fd with raddr nil under certain // unknown conditions. The errors in the calls there to Getpeername // are discarded, but we can't catch the problem there because those // calls are sometimes legally erroneous with a "socket not connected". // Since this code (selfConnect) is already trying to work around // a problem, we make sure if this happens we recognize trouble and // ask the DialTCP routine to try again. // TODO: try to understand what's really going on. if fd.laddr == nil || fd.raddr == nil { return true } l := fd.laddr.(*TCPAddr) r := fd.raddr.(*TCPAddr) return l.Port == r.Port && l.IP.Equal(r.IP) }
fd.laddr
またはfd.raddr
のいずれかがnil
である場合、selfConnect
関数がtrue
を返すようにします。selfConnect
がtrue
を返すと、DialTCP
関数内のループが再試行をトリガーします。これにより、internetSocket
がnil raddr
を返すような「未知の条件」が発生した場合でも、DialTCP
が接続を再試行し、最終的に有効なraddr
を持つ接続を確立できる可能性が高まります。 コメントには、「ソケットコンストラクタが、特定の未知の条件下でraddr
がnil
のfd
を返すことがある」と明記されており、Getpeername
呼び出しでのエラーが破棄されるため、問題が捕捉しにくいことが説明されています。この変更は、根本原因が不明なまま、nil raddr
の問題を回避するための「回避策の拡張」として機能します。
これらの変更は、DialTCP
がnil raddr
を返すという特定のバグに対する、より堅牢な回避策を導入しています。特にARMアーキテクチャでこの問題が頻繁に発生していたため、この変更によってARM環境でのGoのネットワーク機能の安定性が向上し、ビルドが再び成功するようになったと考えられます。
コアとなるコードの変更箇所
src/pkg/net/ipsock_posix.go
--- a/src/pkg/net/ipsock_posix.go
+++ b/src/pkg/net/ipsock_posix.go
@@ -117,10 +117,6 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s
if ra, oserr = raddr.sockaddr(family); oserr != nil {
goto Error
}
- if ra == nil {
- // TODO(r): part of selfConnect debugging
- panic("ra nil when raddr non-nil")
- }
}
fd, oserr = socket(net, family, sotype, proto, la, ra, toAddr)
if oserr != nil {
src/pkg/net/tcpsock_posix.go
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -29,8 +29,7 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr {
return &TCPAddr{sa.Addr[0:], sa.Port}
default:
if sa != nil {
- // TODO(r): Diagnose when we will turn a non-nil sockaddr into a nil.
- // Part of diagnosing the selfConnect bug.
+ // Diagnose when we will turn a non-nil sockaddr into a nil.
panic(fmt.Sprintf("unexpected type in sockaddrToTCP: %T", sa))
}
}
@@ -237,13 +236,6 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
- checkRaddr := func(s string) {
- if err == nil && fd.raddr == nil {
- panic("nil raddr in DialTCP: " + s)
- }
- }
- checkRaddr("early")
-
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
@@ -264,7 +256,6 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
fd.Close()
fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
- checkRaddr("after close")
}
if err != nil {
@@ -274,6 +265,17 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
}
func selfConnect(fd *netFD) bool {
+ // The socket constructor can return an fd with raddr nil under certain
+ // unknown conditions. The errors in the calls there to Getpeername
+ // are discarded, but we can't catch the problem there because those
+ // calls are sometimes legally erroneous with a "socket not connected".
+ // Since this code (selfConnect) is already trying to work around
+ // a problem, we make sure if this happens we recognize trouble and
+ // ask the DialTCP routine to try again.
+ // TODO: try to understand what's really going on.
+ if fd.laddr == nil || fd.raddr == nil {
+ return true
+ }
l := fd.laddr.(*TCPAddr)
r := fd.raddr.(*TCPAddr)
return l.Port == r.Port && l.IP.Equal(r.IP)
コアとなるコードの解説
このコミットの核心は、selfConnect
関数に新しい条件分岐を追加し、DialTCP
関数内のデバッグ用panic
とcheckRaddr
ヘルパー関数を削除した点にあります。
-
デバッグ用
panic
の削除:src/pkg/net/ipsock_posix.go
のinternetSocket
関数から、ra == nil
の場合に発生するpanic
が削除されました。これは、selfConnect
のデバッグ中にraddr
がnil
になる状況を特定するためのものでした。src/pkg/net/tcpsock_posix.go
のDialTCP
関数から、checkRaddr
関数とその呼び出しが削除されました。これもfd.raddr
がnil
である場合にpanic
を引き起こすデバッグ用のチェックでした。 これらの削除は、問題の診断フェーズが終わり、より広範な回避策が導入されたことを示しています。
-
selfConnect
関数の変更:selfConnect
関数に以下の新しいロジックが追加されました。if fd.laddr == nil || fd.raddr == nil { return true }
- この変更は、
netFD
オブジェクトのローカルアドレス(laddr
)またはリモートアドレス(raddr
)のいずれかがnil
である場合、selfConnect
関数がtrue
を返すようにします。 DialTCP
関数は、selfConnect(fd)
がtrue
を返す限り、接続の再試行ループに入ります。これにより、internetSocket
が何らかの理由でnil raddr
を持つfd
を返した場合でも、DialTCP
は接続を閉じ、再試行することで、最終的に有効なアドレスを持つ接続を確立しようとします。- このロジックは、
Getpeername
システムコールがエラーを破棄し、raddr
がnil
になる「未知の条件」に対する直接的な回避策です。特にARMアーキテクチャでこの問題が顕著であったため、この変更はARM環境でのネットワーク接続の安定性を大幅に向上させることが期待されました。
要するに、このコミットは、デバッグ用の厳格なチェックを削除し、代わりに、nil
アドレスという異常な状態をselfConnect
が検出し、DialTCP
に接続の再試行を促すという、より柔軟な(しかし根本原因を解決しない)回避策を導入しています。
関連リンク
- Go Issue #3057: net: DialTCP can return nil raddr
- Gerrit Change-Id: I5687060 (Goのコードレビューシステム)