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

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

このコミットは、Go言語の標準ライブラリである net パッケージ内の tcpsock_posix.go ファイルに対する変更です。具体的には、TCP接続の確立処理において、selfConnect と呼ばれる稀なバグを早期に検出するための診断コードが追加されています。この変更は、Goのネットワークスタックの堅牢性を向上させることを目的としています。

コミット

commit 2155a0408eb50ce8ae47d5b3b53c3363498cd716
Author: Rob Pike <r@golang.org>
Date:   Tue Feb 21 14:53:07 2012 +1100

    net: add diagnostic to try to catch selfConnect bug earlier
    TBR=dsymonds
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/5683057

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

https://github.com/golang/go/commit/2155a0408eb50ce8ae47d5b3b53c3363498cd716

元コミット内容

net: add diagnostic to try to catch selfConnect bug earlier
TBR=dsymonds

R=golang-dev
CC=golang-dev
https://golang.org/cl/5683057

変更の背景

このコミットの主な背景は、Go言語の net パッケージにおける selfConnect と呼ばれる特定のバグの存在です。selfConnect は、TCP接続を確立する際に、クライアントが自分自身に接続しようとしてしまう、非常に稀な競合状態によって発生する問題です。これは、特に同時接続(simultaneous connection)のメカニズムが関与する場合に顕在化することがありました。

Goの net パッケージは、内部的に syscall パッケージを使用してOSのソケットAPIを呼び出しています。DialTCP 関数は、指定されたリモートアドレス(raddr)への接続を試みますが、何らかの理由で fd.raddr (ファイルディスクリプタに関連付けられたリモートアドレス) が nil になるという異常な状態が発生することがありました。この nilraddr は、後続の処理でパニックを引き起こす可能性があり、デバッグが困難でした。

このコミットは、この selfConnect バグが実際に発生する前に、fd.raddrnil であるという異常な状態を早期に検出し、パニックを発生させることで、問題の根本原因を特定しやすくするための診断メカニズムを追加しています。これにより、開発者はより迅速に問題を特定し、修正に取り組むことができるようになります。

前提知識の解説

TCP/IPとソケットプログラミング

TCP/IPはインターネットの基盤となる通信プロトコル群です。TCP(Transmission Control Protocol)は、信頼性の高いコネクション指向の通信を提供し、データの順序保証や再送制御を行います。ソケットは、ネットワーク通信のエンドポイントを表す抽象化された概念で、アプリケーションがネットワーク経由でデータを送受信するためのインターフェースを提供します。

net パッケージ (Go言語)

Go言語の net パッケージは、ネットワークI/Oのためのポータブルなインターフェースを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルをサポートしています。DialTCP 関数は、TCPネットワーク上でリモートアドレスへの接続を確立するために使用されます。

DialTCP 関数

DialTCPnet パッケージの関数で、以下のようなシグネチャを持ちます。 func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)

  • net: "tcp", "tcp4", "tcp6" などのネットワークタイプを指定します。
  • laddr: ローカルアドレス(オプション)。指定しない場合、OSが適切なローカルアドレスを選択します。
  • raddr: リモートアドレス。接続先のアドレスを指定します。
  • 戻り値: 成功した場合は *TCPConn(TCP接続を表す構造体)と nil エラー、失敗した場合は nil とエラーを返します。

TCPAddr 構造体

TCPAddr は、TCPネットワークアドレスを表す構造体です。IPアドレスとポート番号を含みます。

internetSocket 関数

internetSocketnet パッケージ内部で使用される関数で、指定されたネットワークタイプ、ローカルアドレス、リモートアドレス、ソケットタイプ(例: syscall.SOCK_STREAM)、プロトコルなどに基づいてソケットを作成し、接続を試みます。この関数は、OSのシステムコールをラップしています。

fd.raddr

fd はファイルディスクリプタ(file descriptor)を表す内部的な構造体で、ネットワーク接続に関する情報(ローカルアドレス、リモートアドレスなど)を保持しています。fd.raddr は、このファイルディスクリプタに関連付けられたリモートアドレスを指します。正常なTCP接続では、この raddr は接続先のリモートアドレスを保持している必要があります。

selfConnect バグ

selfConnect は、TCPの同時接続(simultaneous connection)メカニズムに関連する稀なケースで発生する可能性のあるバグです。通常、TCP接続はクライアントがサーバーに接続要求を送信し、サーバーがそれを受け入れるという非対称なプロセスで行われます。しかし、TCPプロトコルには、両方のエンドポイントが同時に connect (または Dial) を呼び出し、互いに接続しようとする「同時接続」というメカニズムが存在します。このメカニズムは非常に稀にしか使用されませんが、特定の条件下で DialTCP が自分自身に接続してしまうような論理的な問題を引き起こすことがありました。このバグが発生すると、fd.raddr が適切に設定されず nil になることがあり、後続の処理で問題を引き起こす可能性がありました。

panic

Go言語における panic は、プログラムの実行を停止させる回復不可能なエラーを示します。通常、予期せぬプログラミングエラーや、プログラムが続行できないような致命的な状態に陥った場合に発生させます。このコミットでは、nil raddr という異常な状態を早期に panic させることで、開発者がデバッグしやすいようにしています。

技術的詳細

このコミットは、src/pkg/net/tcpsock_posix.go ファイルの DialTCP 関数に診断ロジックを追加しています。

DialTCP 関数は、まず internetSocket を呼び出してソケットを作成し、接続を試みます。この internetSocket の呼び出し後、fd (ファイルディスクリプタ) が返されます。この fd には、接続が成功した場合、リモートアドレス情報が fd.raddr として設定されているはずです。

追加された診断コードは、以下の checkRaddr という匿名関数です。

checkRaddr := func(s string) {
    if err == nil && fd.raddr == nil {
        panic("nil raddr in DialTCP: " + s)
    }
}

この関数は、以下の条件をチェックします。

  1. err == nil: internetSocket の呼び出しでエラーが発生していないこと。つまり、ソケットの作成と初期接続は成功していると見なされる状態。
  2. fd.raddr == nil: にもかかわらず、fd のリモートアドレス (raddr) が nil であること。

この両方の条件が真である場合、それは異常な状態であり、"nil raddr in DialTCP: " + s というメッセージと共に panic を発生させます。s は、checkRaddr が呼び出された場所を示す文字列("early" または "after close")です。

この checkRaddr 関数は、DialTCP 関数内の2つの重要なポイントで呼び出されます。

  1. internetSocket 呼び出し直後 (checkRaddr("early")): 最初の internetSocket 呼び出しが成功した直後に、fd.raddrnil でないことを確認します。これにより、初期接続フェーズでの nil raddr の問題を早期に捕捉します。

  2. selfConnect リトライループ内 (checkRaddr("after close")): DialTCP 関数には、selfConnect バグを検出して再試行するためのループが存在します。このループは、selfConnect(fd) が真(つまり、自分自身に接続してしまった)であり、かつ laddrnil(ローカルアドレスが指定されていない)の場合に、既存のソケットを閉じ、再度 internetSocket を呼び出して接続を試みます。この再試行の後にも、fd.raddrnil になっていないかを checkRaddr("after close") で確認します。これにより、再試行プロセス中に発生する可能性のある nil raddr の問題も捕捉します。

この診断コードの追加により、selfConnect バグやその他の関連する競合状態によって fd.raddrnil になるという、通常ではありえない状態が発生した場合に、プログラムが静かに続行して後でより理解しにくいエラーを引き起こすのではなく、即座に panic することで、問題の根本原因を特定しやすくなります。これは、開発者がデバッグを行う上で非常に有用な情報を提供します。

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

src/pkg/net/tcpsock_posix.go ファイルの DialTCP 関数に以下のコードが追加されました。

--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -230,6 +230,13 @@ 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
@@ -250,6 +257,7 @@ 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 {

コアとなるコードの解説

追加されたコードは、DialTCP 関数内で fd (ファイルディスクリプタ) の raddr (リモートアドレス) が nil になっていないかをチェックする診断ロジックです。

  1. checkRaddr 匿名関数の定義: checkRaddr という名前の匿名関数が定義されています。この関数は文字列 s を引数に取り、診断メッセージの一部として使用します。 この関数は、internetSocket の呼び出しでエラーが発生しておらず (err == nil)、かつ fd.raddrnil である場合に panic を発生させます。これは、接続が成功したと見なされるにもかかわらず、リモートアドレス情報が欠落しているという矛盾した状態を検出するためのものです。

  2. checkRaddr("early"): 最初の internetSocket 呼び出しの直後に checkRaddr("early") が呼び出されます。これは、接続確立の初期段階で fd.raddr が正しく設定されていることを確認するためのものです。もしここで nil raddr が検出されれば、初期接続プロセスに問題があることを示します。

  3. checkRaddr("after close"): DialTCP 関数には、selfConnect バグを処理するための再試行ループがあります。このループは、selfConnect(fd) が真(自分自身に接続してしまった)の場合に、既存のソケットを閉じ (fd.Close())、再度 internetSocket を呼び出して接続を試みます。この再試行の直後に checkRaddr("after close") が呼び出されます。これは、再試行プロセスが nil raddr の状態を引き起こしていないことを確認するためのものです。

この診断コードは、本番環境で通常発生しないような、非常に稀な競合状態やバグによって fd.raddrnil になるケースを早期に特定し、デバッグを容易にすることを目的としています。panic を発生させることで、問題の発生箇所と状況を明確にし、開発者が根本原因を追跡できるようにします。

関連リンク

参考にした情報源リンク

  • Go言語の公式リポジトリ: https://github.com/golang/go
  • Gerrit Change-ID 5683057: https://golang.org/cl/5683057 (このコミットに対応するGoのコードレビューシステムのエントリ)
  • Go言語の net パッケージのソースコード (特に tcpsock_posix.go): https://github.com/golang/go/blob/master/src/net/tcpsock_posix.go
  • Go言語の net パッケージにおける selfConnect バグに関する議論や関連するIssue (当時の情報源を特定するのは困難ですが、GoのIssueトラッカーやメーリングリストで関連する議論が見つかる可能性があります)

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

このコミットは、Go言語の標準ライブラリである net パッケージ内の tcpsock_posix.go ファイルに対する変更です。具体的には、TCP接続の確立処理において、selfConnect と呼ばれる稀なバグを早期に検出するための診断コードが追加されています。この変更は、Goのネットワークスタックの堅牢性を向上させることを目的としています。

コミット

commit 2155a0408eb50ce8ae47d5b3b53c3363498cd716
Author: Rob Pike <r@golang.org>
Date:   Tue Feb 21 14:53:07 2012 +1100

    net: add diagnostic to try to catch selfConnect bug earlier
    TBR=dsymonds
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/5683057

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

https://github.com/golang/go/commit/2155a0408eb50ce8ae47d5b3b53c3363498cd716

元コミット内容

net: add diagnostic to try to catch selfConnect bug earlier
TBR=dsymonds

R=golang-dev
CC=golang-dev
https://golang.org/cl/5683057

変更の背景

このコミットの主な背景は、Go言語の net パッケージにおける selfConnect と呼ばれる特定のバグの存在です。selfConnect は、TCP接続を確立する際に、クライアントが自分自身に接続しようとしてしまう、非常に稀な競合状態によって発生する問題です。これは、特に同時接続(simultaneous connection)のメカニズムが関与する場合に顕在化することがありました。

Goの net パッケージは、内部的に syscall パッケージを使用してOSのソケットAPIを呼び出しています。DialTCP 関数は、指定されたリモートアドレス(raddr)への接続を試みますが、何らかの理由で fd.raddr (ファイルディスクリプタに関連付けられたリモートアドレス) が nil になるという異常な状態が発生することがありました。この nilraddr は、後続の処理でパニックを引き起こす可能性があり、デバッグが困難でした。

このコミットは、この selfConnect バグが実際に発生する前に、fd.raddrnil であるという異常な状態を早期に検出し、パニックを発生させることで、問題の根本原因を特定しやすくするための診断メカニズムを追加しています。これにより、開発者はより迅速に問題を特定し、修正に取り組むことができるようになります。

前提知識の解説

TCP/IPとソケットプログラミング

TCP/IPはインターネットの基盤となる通信プロトコル群です。TCP(Transmission Control Protocol)は、信頼性の高いコネクション指向の通信を提供し、データの順序保証や再送制御を行います。ソケットは、ネットワーク通信のエンドポイントを表す抽象化された概念で、アプリケーションがネットワーク経由でデータを送受信するためのインターフェースを提供します。

net パッケージ (Go言語)

Go言語の net パッケージは、ネットワークI/Oのためのポータブルなインターフェースを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルをサポートしています。DialTCP 関数は、TCPネットワーク上でリモートアドレスへの接続を確立するために使用されます。

DialTCP 関数

DialTCPnet パッケージの関数で、以下のようなシグネチャを持ちます。 func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)

  • net: "tcp", "tcp4", "tcp6" などのネットワークタイプを指定します。
  • laddr: ローカルアドレス(オプション)。指定しない場合、OSが適切なローカルアドレスを選択します。
  • raddr: リモートアドレス。接続先のアドレスを指定します。
  • 戻り値: 成功した場合は *TCPConn(TCP接続を表す構造体)と nil エラー、失敗した場合は nil とエラーを返します。

TCPAddr 構造体

TCPAddr は、TCPネットワークアドレスを表す構造体です。IPアドレスとポート番号を含みます。

internetSocket 関数

internetSocketnet パッケージ内部で使用される関数で、指定されたネットワークタイプ、ローカルアドレス、リモートアドレス、ソケットタイプ(例: syscall.SOCK_STREAM)、プロトコルなどに基づいてソケットを作成し、接続を試みます。この関数は、OSのシステムコールをラップしています。

fd.raddr

fd はファイルディスクリプタ(file descriptor)を表す内部的な構造体で、ネットワーク接続に関する情報(ローカルアドレス、リモートアドレスなど)を保持しています。fd.raddr は、このファイルディスクリプタに関連付けられたリモートアドレスを指します。正常なTCP接続では、この raddr は接続先のリモートアドレスを保持している必要があります。

selfConnect バグ

selfConnect は、TCPの同時接続(simultaneous connection)メカニズムに関連する稀なケースで発生する可能性のあるバグです。通常、TCP接続はクライアントがサーバーに接続要求を送信し、サーバーがそれを受け入れるという非対称なプロセスで行われます。しかし、TCPプロトコルには、両方のエンドポイントが同時に connect (または Dial) を呼び出し、互いに接続しようとする「同時接続」というメカニズムが存在します。このメカニズムは非常に稀にしか使用されませんが、特定の条件下で DialTCP が自分自身に接続してしまうような論理的な問題を引き起こすことがありました。このバグが発生すると、fd.raddr が適切に設定されず nil になることがあり、後続の処理で問題を引き起こす可能性がありました。

panic

Go言語における panic は、プログラムの実行を停止させる回復不可能なエラーを示します。通常、予期せぬプログラミングエラーや、プログラムが続行できないような致命的な状態に陥った場合に発生させます。このコミットでは、nil raddr という異常な状態を早期に panic させることで、開発者がデバッグしやすいようにしています。

技術的詳細

このコミットは、src/pkg/net/tcpsock_posix.go ファイルの DialTCP 関数に診断ロジックを追加しています。

DialTCP 関数は、まず internetSocket を呼び出してソケットを作成し、接続を試みます。この internetSocket の呼び出し後、fd (ファイルディスクリプタ) が返されます。この fd には、接続が成功した場合、リモートアドレス情報が fd.raddr として設定されているはずです。

追加された診断コードは、以下の checkRaddr という匿名関数です。

checkRaddr := func(s string) {
    if err == nil && fd.raddr == nil {
        panic("nil raddr in DialTCP: " + s)
    }
}

この関数は、以下の条件をチェックします。

  1. err == nil: internetSocket の呼び出しでエラーが発生していないこと。つまり、ソケットの作成と初期接続は成功していると見なされる状態。
  2. fd.raddr == nil: にもかかわらず、fd のリモートアドレス (raddr) が nil であること。

この両方の条件が真である場合、それは異常な状態であり、"nil raddr in DialTCP: " + s というメッセージと共に panic を発生させます。s は、checkRaddr が呼び出された場所を示す文字列("early" または "after close")です。

この checkRaddr 関数は、DialTCP 関数内の2つの重要なポイントで呼び出されます。

  1. internetSocket 呼び出し直後 (checkRaddr("early")): 最初の internetSocket 呼び出しが成功した直後に、fd.raddrnil でないことを確認します。これにより、初期接続フェーズでの nil raddr の問題を早期に捕捉します。

  2. selfConnect リトライループ内 (checkRaddr("after close")): DialTCP 関数には、selfConnect バグを検出して再試行するためのループが存在します。このループは、selfConnect(fd) が真(つまり、自分自身に接続してしまった)であり、かつ laddrnil(ローカルアドレスが指定されていない)の場合に、既存のソケットを閉じ、再度 internetSocket を呼び出して接続を試みます。この再試行の後にも、fd.raddrnil になっていないかを checkRaddr("after close") で確認します。これにより、再試行プロセス中に発生する可能性のある nil raddr の問題も捕捉します。

この診断コードの追加により、selfConnect バグやその他の関連する競合状態によって fd.raddrnil になるという、通常ではありえない状態が発生した場合に、プログラムが静かに続行して後でより理解しにくいエラーを引き起こすのではなく、即座に panic することで、問題の根本原因を特定しやすくなります。これは、開発者がデバッグを行う上で非常に有用な情報を提供します。

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

src/pkg/net/tcpsock_posix.go ファイルの DialTCP 関数に以下のコードが追加されました。

--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -230,6 +230,13 @@ 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
@@ -250,6 +257,7 @@ 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 {

コアとなるコードの解説

追加されたコードは、DialTCP 関数内で fd (ファイルディスクリプタ) の raddr (リモートアドレス) が nil になっていないかをチェックする診断ロジックです。

  1. checkRaddr 匿名関数の定義: checkRaddr という名前の匿名関数が定義されています。この関数は文字列 s を引数に取り、診断メッセージの一部として使用します。 この関数は、internetSocket の呼び出しでエラーが発生しておらず (err == nil)、かつ fd.raddrnil である場合に panic を発生させます。これは、接続が成功したと見なされるにもかかわらず、リモートアドレス情報が欠落しているという矛盾した状態を検出するためのものです。

  2. checkRaddr("early"): 最初の internetSocket 呼び出しの直後に checkRaddr("early") が呼び出されます。これは、接続確立の初期段階で fd.raddr が正しく設定されていることを確認するためのものです。もしここで nil raddr が検出されれば、初期接続プロセスに問題があることを示します。

  3. checkRaddr("after close"): DialTCP 関数には、selfConnect バグを処理するための再試行ループがあります。このループは、selfConnect(fd) が真(自分自身に接続してしまった)の場合に、既存のソケットを閉じ (fd.Close())、再度 internetSocket を呼び出して接続を試みます。この再試行の直後に checkRaddr("after close") が呼び出されます。これは、再試行プロセスが nil raddr の状態を引き起こしていないことを確認するためのものです。

この診断コードは、本番環境で通常発生しないような、非常に稀な競合状態やバグによって fd.raddrnil になるケースを早期に特定し、デバッグを容易にすることを目的としています。panic を発生させることで、問題の発生箇所と状況を明確にし、開発者が根本原因を追跡できるようにします。

関連リンク

参考にした情報源リンク