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

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

このコミットは、Go言語のnetパッケージにおけるPlan 9オペレーティングシステム向けのネットワーク処理に関する修正と改善を目的としています。具体的には、ネットワーク接続のデータファイルのオープンタイミング、接続クローズ時のリソース解放の堅牢性、およびAcceptコールにおけるリモートアドレスの設定の正確性に関する問題に対処しています。これにより、Plan 9環境下でのGoアプリケーションのネットワーク通信の信頼性と正確性が向上します。

コミット

commit 66b69a1719040e05f8ccef8110aaff192968c29a
Author: Akshat Kumar <seed@mail.nanosouffle.net>
Date:   Tue Feb 19 17:11:17 2013 -0800

    net: Plan 9: open data file and set remote-addr properly
    
    The data file should be opened when a Conn is first
    established, rather than waiting for the first Read or
    Write.
    
    Upon Close, we now make sure to try to close both, the
    ctl as well as data files and set both to nil, even in
    the face of errors, instead of returning early.
    
    The Accept call was not setting the remote address
    of the connection properly. Now, we read the correct
    file.
    
    Make functions that establish Conn use newTCPConn
    or newUDPConn.
    
    R=rsc, rminnich, ality, dave
    CC=golang-dev
    https://golang.org/cl/7228068

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

https://github.com/golang/go/commit/66b69a1719040e05f8ccef8110aaff192968c29a

元コミット内容

net: Plan 9: open data file and set remote-addr properly

The data file should be opened when a Conn is first
established, rather than waiting for the first Read or
Write.

Upon Close, we now make sure to try to close both, the
ctl as well as data files and set both to nil, even in
the face of errors, instead of returning early.

The Accept call was not setting the remote address
of the connection properly. Now, we read the correct
file.

Make functions that establish Conn use newTCPConn
or newUDPConn.

R=rsc, rminnich, ality, dave
CC=golang-dev
https://golang.org/cl/7228068

変更の背景

このコミットは、Go言語のnetパッケージがPlan 9オペレーティングシステム上で動作する際の、いくつかの既存の不具合と非効率性を修正するために行われました。

  1. データファイルの遅延オープンによる非効率性: 以前の実装では、ネットワーク接続のデータファイル(実際のデータ送受信を行うファイルディスクリプタ)が、最初のReadまたはWrite操作が呼び出されるまで開かれませんでした。これは、接続が確立された直後にリソースが利用可能であるべきという期待に反し、最初のデータ操作時に余分なオーバーヘッドと潜在的なエラーを引き起こす可能性がありました。
  2. 接続クローズ時の不完全なリソース解放: 接続をクローズする際、制御ファイル(ctl)とデータファイル(data)の両方が適切に閉じられ、nilに設定されるべきでした。しかし、以前の実装では、エラーが発生した場合に早期にリターンしてしまい、片方のファイルが閉じられないまま残る可能性がありました。これはリソースリークや後続の操作での問題につながります。
  3. Acceptコールにおけるリモートアドレスの誤設定: サーバー側で新しい接続を受け入れるAccept関数が、確立された接続のリモートアドレスを正しく設定していませんでした。これは、接続の相手を正確に識別できないという問題を引き起こし、ログ記録、セキュリティ、またはアプリケーションロジックに影響を与える可能性がありました。
  4. 一貫性のない接続確立: TCPConnUDPConnといった具体的な接続タイプを確立する関数が、newTCPConnnewUDPConnといったヘルパー関数を使用せず、直接構造体を初期化していました。これにより、コードの重複や将来的な変更の際に一貫性が失われるリスクがありました。

これらの問題に対処することで、Plan 9環境におけるGoのネットワークスタックの堅牢性、効率性、および正確性を向上させることが目的とされました。

前提知識の解説

このコミットを理解するためには、以下の概念が重要です。

  1. Plan 9オペレーティングシステム:

    • Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。その最も特徴的な設計原則の一つは「Everything is a file(すべてはファイルである)」です。これは、プロセス、ネットワーク接続、デバイスなど、システム内のあらゆるリソースがファイルシステム上のファイルとして表現され、標準的なファイル操作(open, read, write, closeなど)を通じてアクセスされることを意味します。
    • ネットワーク接続も例外ではありません。Plan 9では、ネットワーク接続は通常、制御ファイル(ctl)とデータファイル(data)のペアとして表現されます。
      • ctlファイル: 接続の確立、設定、状態のクエリなど、接続の制御情報をやり取りするために使用されます。
      • dataファイル: 実際のデータ(ペイロード)の送受信に使用されます。
    • Go言語は、その設計思想の一部にPlan 9の影響を受けており、特に初期のネットワーク実装ではPlan 9のファイルシステムベースのネットワークモデルを強く反映していました。
  2. Go言語のnetパッケージ:

    • Goの標準ライブラリであるnetパッケージは、TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うためのプラットフォーム非依存のインターフェースを提供します。
    • しかし、内部的には各オペレーティングシステム(Linux, Windows, macOS, そしてPlan 9など)のネイティブなネットワークAPIを呼び出すためのOS固有の実装を持っています。このコミットで変更されているファイル(fd_plan9.go, ipsock_plan9.goなど)は、まさにPlan 9に特化した実装部分です。
    • netFD構造体: netパッケージ内部でネットワークファイルディスクリプタを抽象化するために使用される構造体です。Plan 9の場合、この構造体はctlファイルとdataファイルへの参照を保持します。
  3. ファイルディスクリプタとリソース管理:

    • ファイルディスクリプタ(File Descriptor, FD)は、オペレーティングシステムがファイルやI/Oリソースを識別するために使用する抽象的なハンドルです。ネットワークソケットもファイルディスクリプタとして扱われることが一般的です。
    • プログラムがファイルディスクリプタを開くと、対応するシステムリソースが消費されます。これらのリソースは、プログラムが終了するか、明示的にクローズされるまで解放されません。リソースリークを防ぐためには、不要になったファイルディスクリプタを確実にクローズすることが非常に重要です。

これらの背景知識を踏まえることで、コミットがなぜ特定のファイルを変更し、どのような挙動の改善を目指しているのかがより明確になります。

技術的詳細

このコミットは、Plan 9環境におけるGoのネットワーク処理のいくつかの側面を改善しています。

  1. データファイルの即時オープン:

    • 以前は、netFDReadまたはWriteメソッドが初めて呼び出されたときに、dataファイルがos.OpenFile(fd.dir+"/data", os.O_RDWR, 0)によって開かれていました。
    • このコミットでは、newFD関数(netFD構造体を初期化するヘルパー関数)のシグネチャが変更され、ctlファイルだけでなくdataファイルも引数として受け取るようになりました。
    • これにより、dialPlan9acceptPlan9といった接続確立時に、dataファイルが即座に開かれ、newFDに渡されるようになりました。これにより、接続が確立された時点でデータ送受信の準備が整い、最初のRead/Write時の遅延やエラー処理の複雑さが解消されます。
    • ReadおよびWriteメソッド内のfd.data == nilチェックとファイルオープンロジックは削除されました。
  2. Closeメソッドの堅牢化:

    • 以前のCloseメソッドでは、fd.ctl.Close()でエラーが発生した場合、すぐにリターンしていました。この場合、fd.datanilでなければ、fd.data.Close()が呼び出されず、データファイルが閉じられない可能性がありました。
    • 新しい実装では、fd.ctl.Close()のエラーをerr変数に保持しつつ、fd.data != nilであれば必ずfd.data.Close()を試みるようになりました。
    • fd.data.Close()でエラーが発生した場合、それが最初のctl.Close()のエラーがnilであった場合にのみ、そのエラーをerrに設定します。これにより、両方のクローズ操作を試み、最初のエラーを優先して報告しつつ、リソースの解放を最大化します。
    • 最後に、fd.ctl = nilfd.data = nilが必ず実行されるようになり、ファイルディスクリプタの参照が確実にクリアされるようになりました。
  3. Acceptコールにおけるリモートアドレスの正確な設定:

    • listenPlan9acceptPlan9メソッドは、新しい接続を受け入れる際に、その接続のリモートアドレスを正しく取得していませんでした。以前は、リスナーのディレクトリ(l.dir)からリモートアドレスを読み取ろうとしていましたが、これは新しい接続固有のアドレスではありませんでした。
    • 修正後、acceptPlan9は、新しく確立された接続の固有のディレクトリ(/net/ + l.proto + / + name)からリモートアドレス(/remoteファイル)を読み取るようになりました。これにより、Acceptによって返されるnetFDオブジェクトが正しいリモートアドレスを持つことが保証されます。
    • また、acceptPlan9内でもdataファイルが即座に開かれ、newFDに渡されるようになりました。
  4. newTCPConnおよびnewUDPConnヘルパー関数の利用:

    • dialTCPdialUDPといった関数が、TCPConnUDPConn構造体を直接初期化する代わりに、それぞれnewTCPConn(fd)newUDPConn(fd)という新しいヘルパー関数を使用するように変更されました。
    • newUDPConn関数が追加され、*UDPConnを返すようになりました。
    • これにより、接続オブジェクトの生成ロジックが一元化され、コードの可読性と保守性が向上します。

これらの変更は、Plan 9環境におけるGoのネットワークスタックの信頼性と正確性を大幅に向上させ、リソース管理のベストプラクティスに沿ったものとなっています。

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

このコミットによって変更された主要なファイルと、その変更の概要は以下の通りです。

  1. src/pkg/net/fd_plan9.go:

    • newFD関数のシグネチャが変更され、data *os.File引数が追加されました。
    • ReadおよびWriteメソッドから、fd.data == nilの場合にデータファイルを開くロジックが削除されました。代わりに、fd.ok() || fd.data == nilというチェックが追加され、dataファイルが有効でない場合は即座にエラーを返すようになりました。
    • Closeメソッドのロジックが変更され、ctldataの両方のファイルを確実に閉じ、エラーが発生しても両方をnilに設定するようになりました。
  2. src/pkg/net/ipsock_plan9.go:

    • dialPlan9関数内で、ctlファイルを開いた直後にdataファイルも開くようになりました。そして、newFDを呼び出す際にdataファイルを渡すようになりました。エラーハンドリングも、dataファイルが開けなかった場合にctlファイルを閉じるように修正されました。
    • listenPlan9関数内で、newFDを呼び出す際にdataファイルとしてnilを渡すようになりました(リスナーはデータファイルを直接持たないため)。
    • netFDnetFD()メソッドが、newFDを呼び出す際にdataファイルも渡すように変更されました。
    • acceptPlan9関数内で、新しく受け入れられた接続のdataファイルを開くようになりました。また、リモートアドレスを読み取るパスが、リスナーのディレクトリではなく、新しく確立された接続の固有のディレクトリを参照するように修正されました。newFDを呼び出す際にdataファイルを渡すようになりました。
  3. src/pkg/net/tcpsock_plan9.go:

    • dialTCP関数が、&TCPConn{conn{fd}}という直接的な構造体初期化の代わりに、newTCPConn(fd)ヘルパー関数を使用するように変更されました。
  4. src/pkg/net/udpsock_plan9.go:

    • newUDPConnという新しいヘルパー関数が追加されました。
    • ReadFromUDPおよびWriteToUDPメソッドから、c.fd.data == nilの場合にデータファイルを開くロジックが削除されました。代わりに、c.ok() || c.fd.data == nilというチェックが追加され、dataファイルが有効でない場合は即座にエラーを返すようになりました。
    • dialUDP関数が、&UDPConn{conn{fd}}という直接的な構造体初期化の代わりに、newUDPConn(fd)ヘルパー関数を使用するように変更されました。
    • ListenUDP関数内で、リスナーのdataファイルを開くようになりました。そして、newUDPConn(l.netFD())を使用するように変更されました。

これらの変更は、Plan 9におけるネットワーク接続のライフサイクル管理とリソースハンドリングを改善し、より堅牢で予測可能な挙動を実現しています。

コアとなるコードの解説

ここでは、主要な変更点について、具体的なコードの差分を基に解説します。

src/pkg/net/fd_plan9.go

newFD関数の変更

--- a/src/pkg/net/fd_plan9.go
+++ b/src/pkg/net/fd_plan9.go
@@ -29,22 +29,16 @@ func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
 	return dialTimeoutRace(net, addr, timeout)
 }
 
-func newFD(proto, name string, ctl *os.File, laddr, raddr Addr) *netFD {
-	return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr}
+func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD {
+	return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, data, laddr, raddr}
 }
  • 変更点: newFD関数のシグネチャにdata *os.Fileが追加されました。
  • 解説: 以前はnetFD構造体のdataフィールドはnilで初期化されていました。この変更により、接続が確立される際にdataファイルが外部から渡され、netFDオブジェクトが生成された時点でdataファイルへの参照を持つことができるようになりました。これは、データファイルの遅延オープンをなくし、接続確立時に即座にデータ送受信の準備を整えるための基盤となります。

ReadおよびWriteメソッドの変更

--- a/src/pkg/net/fd_plan9.go
+++ b/src/pkg/net/fd_plan9.go
@@ -34,15 +28,9 @@ func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
 
 func (fd *netFD) Read(b []byte) (n int, err error) {
-	if !fd.ok() {
+	if !fd.ok() || fd.data == nil {
 		return 0, syscall.EINVAL
 	}
-	if fd.data == nil {
-		fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
-		if err != nil {
-			return 0, err
-		}
-	}
 	n, err = fd.data.Read(b)
 	if fd.proto == "udp" && err == io.EOF {
 		n = 0
@@ -54,15 +48,9 @@ func (fd *netFD) Read(b []byte) (n int, err error) {
 }
 
 func (fd *netFD) Write(b []byte) (n int, err error) {
-	if !fd.ok() {
+	if !fd.ok() || fd.data == nil {
 		return 0, syscall.EINVAL
 	}
-	if fd.data == nil {
-		fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
-		if err != nil {
-			return 0, err
-		}
-	}
 	return fd.data.Write(b)
 }
  • 変更点: ReadWriteメソッドから、fd.data == nilの場合にdataファイルを開くロジックが削除されました。また、if !fd.ok()の条件がif !fd.ok() || fd.data == nilに変更されました。
  • 解説: newFDの変更により、dataファイルは接続確立時に既に開かれていることが期待されるため、これらのメソッド内で再度開く必要がなくなりました。fd.data == nilのチェックが追加されたのは、dataファイルが適切に設定されていない場合に備えて、早期に無効な引数エラー(syscall.EINVAL)を返すためです。これにより、コードが簡潔になり、エラー処理のパスも明確になります。

Closeメソッドの変更

--- a/src/pkg/net/fd_plan9.go
+++ b/src/pkg/net/fd_plan9.go
@@ -85,11 +73,10 @@ func (fd *netFD) Close() error {
 		return syscall.EINVAL
 	}
 	err := fd.ctl.Close()
-\tif err != nil {\n-\t\treturn err\n-\t}\n \tif fd.data != nil {\n-\t\terr = fd.data.Close()\n+\t\tif err1 := fd.data.Close(); err1 != nil && err == nil {\n+\t\t\terr = err1\n+\t\t}\n \t}\n \tfd.ctl = nil
 \tfd.data = nil
  • 変更点: fd.ctl.Close()でエラーが発生してもすぐにリターンせず、fd.data.Close()も試みるようになりました。エラーの伝播ロジックも変更されました。
  • 解説:
    • err := fd.ctl.Close(): まず制御ファイルを閉じます。エラーが発生しても、すぐにリターンしません。
    • if fd.data != nil: データファイルが存在する場合、そのクローズを試みます。
    • if err1 := fd.data.Close(); err1 != nil && err == nil: データファイルのクローズでエラー(err1)が発生し、かつ制御ファイルのクローズでエラーがなかった場合(err == nil)、err1を最終的なエラーとしてerrに設定します。これにより、両方のクローズ操作を試み、最初のエラーを優先して報告しつつ、リソースの解放を最大化します。
    • fd.ctl = nilfd.data = nil: 最後に、ファイルディスクリプタの参照を確実にnilに設定します。これにより、リソースリークを防ぎ、オブジェクトが適切にクリーンアップされた状態を保証します。

src/pkg/net/ipsock_plan9.go

dialPlan9関数の変更

--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -114,17 +114,24 @@ func dialPlan9(net string, laddr, raddr Addr) (*netFD, error) {
 		f.Close()
 		return nil, err
 	}
+	data, err := os.OpenFile("/net/"+proto+"/"+name+"/data", os.O_RDWR, 0)
+	if err != nil {
+		f.Close()
+		return nil, err
+	}
 	laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
 	if err != nil {
+		data.Close()
 		f.Close()
 		return nil, err
 	}
 	raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote")
 	if err != nil {
+		data.Close()
 		f.Close()
 		return nil, err
 	}
-	return newFD(proto, name, f, laddr, raddr), nil
+	return newFD(proto, name, f, data, laddr, raddr), nil
 }
  • 変更点: ctlファイル(f)を開いた直後にdataファイルも開くようになりました。newFDの呼び出しにdataファイルが追加されました。エラーハンドリングも、dataファイルが開けなかった場合にctlファイルを閉じるように修正されました。
  • 解説: これにより、dialPlan9(クライアント側での接続確立)が完了した時点で、netFDオブジェクトがctldataの両方のファイルディスクリプタを保持するようになります。これは、fd_plan9.goRead/Writeメソッドの変更と連携し、データファイルの即時オープンを実現します。

acceptPlan9関数の変更

--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -161,15 +168,16 @@ func (l *netFD) acceptPlan9() (*netFD, error) {
 		return nil, err
 	}
 	name := string(buf[:n])
-\tladdr, err := readPlan9Addr(l.proto, l.dir+"/local")
+\tdata, err := os.OpenFile("/net/"+l.proto+"/"+name+"/data", os.O_RDWR, 0)
 \tif err != nil {\n \t\tf.Close()\n \t\treturn nil, err\n \t}\n-\traddr, err := readPlan9Addr(l.proto, l.dir+"/remote")
+\traddr, err := readPlan9Addr(l.proto, "/net/"+l.proto+"/"+name+"/remote")
 \tif err != nil {\n+\t\tdata.Close()\n \t\tf.Close()\n \t\treturn nil, err
 \t}\n-\treturn newFD(l.proto, name, f, laddr, raddr), nil
+\treturn newFD(l.proto, name, f, data, l.laddr, raddr), nil
 }
  • 変更点: 新しく受け入れられた接続のdataファイルを開くようになりました。リモートアドレスを読み取るパスが、リスナーのディレクトリではなく、新しく確立された接続の固有のディレクトリを参照するように修正されました。newFDを呼び出す際にdataファイルが追加されました。
  • 解説:
    • data, err := os.OpenFile(...): dialPlan9と同様に、acceptPlan9でも新しい接続のdataファイルを即座に開きます。
    • raddr, err := readPlan9Addr(l.proto, "/net/"+l.proto+"/"+name+"/remote"): 最も重要な修正点の一つです。以前はリスナーのl.dirからリモートアドレスを読み取ろうとしていましたが、これは誤りでした。新しいコードでは、name変数(新しく受け入れられた接続の識別子)を使用して、その接続固有のリモートアドレスファイルから正確なリモートアドレスを読み取るようになりました。これにより、Acceptが返す接続オブジェクトが正しいリモートアドレスを持つことが保証されます。

src/pkg/net/tcpsock_plan9.go

dialTCP関数の変更

--- a/src/pkg/net/tcpsock_plan9.go
+++ b/src/pkg/net/tcpsock_plan9.go
@@ -98,7 +98,7 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, e
 	if err != nil {
 		return nil, err
 	}
-	return &TCPConn{conn{fd}}, nil
+	return newTCPConn(fd), nil
 }
  • 変更点: &TCPConn{conn{fd}}という直接的な構造体初期化の代わりに、newTCPConn(fd)ヘルパー関数を使用するように変更されました。
  • 解説: これは、接続オブジェクトの生成ロジックを一元化し、コードの可読性と保守性を向上させるためのリファクタリングです。newTCPConnは、TCPConnオブジェクトを生成する際の標準的な方法を提供します。

src/pkg/net/udpsock_plan9.go

newUDPConn関数の追加

--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -19,6 +19,10 @@ type UDPConn struct {
 	conn
 }
 
+func newUDPConn(fd *netFD) *UDPConn {
+	return &UDPConn{conn{fd}}
+}
+
 // ReadFromUDP reads a UDP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
  • 変更点: newUDPConnという新しいヘルパー関数が追加されました。
  • 解説: TCPConnと同様に、UDPConnオブジェクトを生成するための標準的なヘルパー関数が提供されました。これにより、UDPConnのインスタンス化が一貫した方法で行われるようになります。

ReadFromUDPおよびWriteToUDPメソッドの変更

--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -27,15 +31,9 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
 // Timeout() == true after a fixed time limit; see SetDeadline and
 // SetReadDeadline.
 func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
-\tif !c.ok() {\n+\tif !c.ok() || c.fd.data == nil {\n \t\treturn 0, nil, syscall.EINVAL\n \t}\n-\tif c.fd.data == nil {\n-\t\tc.fd.data, err = os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0)\n-\t\tif err != nil {\n-\t\t\treturn 0, nil, err
-\t\t}\n-\t}\n \tbuf := make([]byte, udpHeaderSize+len(b))\n \tm, err := c.fd.data.Read(buf)\n \tif err != nil {\
@@ -76,16 +74,9 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,\
 // SetWriteDeadline.  On packet-oriented connections, write timeouts
 // are rare.\n func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {\n-\tif !c.ok() {\n+\tif !c.ok() || c.fd.data == nil {\n \t\treturn 0, syscall.EINVAL\n \t}\n-\tif c.fd.data == nil {\n-\t\tf, err := os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0)\n-\t\tif err != nil {\n-\t\t\treturn 0, err\n-\t\t}\n-\t\tc.fd.data = f\n-\t}\n \th := new(udpHeader)\n \th.raddr = addr.IP.To16()\n \th.laddr = c.fd.laddr.(*UDPAddr).IP.To16()\
  • 変更点: ReadFromUDPWriteToUDPメソッドから、c.fd.data == nilの場合にdataファイルを開くロジックが削除されました。また、if !c.ok()の条件がif !c.ok() || c.fd.data == nilに変更されました。
  • 解説: fd_plan9.goRead/Writeメソッドと同様に、UDPConnのデータファイルも接続確立時に開かれるようになったため、これらのメソッド内で再度開く必要がなくなりました。

dialUDPおよびListenUDP関数の変更

--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -141,7 +132,7 @@ func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, e
 	if err != nil {
 		return nil, err
 	}
-	return &UDPConn{conn{fd}}, nil
+	return newUDPConn(fd), nil
 }
 
 const udpHeaderSize = 16*3 + 2*2
@@ -193,7 +184,11 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
 	if err != nil {
 		return nil, err
 	}
-	return &UDPConn{conn{l.netFD()}}, nil
+	l.data, err = os.OpenFile(l.dir+"/data", os.O_RDWR, 0)
+	if err != nil {
+		return nil, err
+	}
+	return newUDPConn(l.netFD()), nil
 }
  • 変更点: dialUDPnewUDPConn(fd)を使用するように変更されました。ListenUDPは、リスナーのdataファイルを開くようになり、newUDPConn(l.netFD())を使用するように変更されました。
  • 解説: dialUDPの変更は、newUDPConnヘルパー関数の一貫した使用を促進します。ListenUDPの変更は、UDPリスナーも関連するdataファイルを適切に管理し、newUDPConnを通じて一貫したオブジェクト生成を行うことを保証します。

これらの変更は、Plan 9環境におけるGoのネットワークスタックの堅牢性、正確性、および保守性を向上させるための重要なステップです。

関連リンク

参考にした情報源リンク