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

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

このコミットは、Goランタイムにおけるネットワークタイマー関連のクラッシュを修正するものです。具体的には、netpollメカニズムにおける競合状態が原因で発生する可能性のある問題を解決しています。

コミット

commit 9707f269c1eb7ee68a2be93e87eb49d481fb1a84
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Tue Aug 13 12:55:57 2013 +0400

    runtime: fix network timers related crash
    Fixes #6103.
    
    R=golang-dev, alex.brainman
    CC=golang-dev
    https://golang.org/cl/12686045

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

https://github.com/golang/go/commit/9707f269c1eb7ee68a2be93e87eb49d481fb1a84

元コミット内容

このコミットは、Goランタイムにおけるネットワークタイマー関連のクラッシュを修正します。Issue #6103を解決します。

変更の背景

このコミットは、GoランタイムにおけるネットワークI/Oのタイムアウト処理に関連する既知のクラッシュ(Issue #6103)を修正するために導入されました。GoのネットワークI/Oは、内部的にnetpollというメカニズムを使用して非同期処理を実現しています。netpollは、ファイルディスクリプタ(ソケットなど)の準備ができたときに、対応するゴルーチンをスケジュールする役割を担っています。

問題の核心は、ネットワーク操作にタイムアウトが設定されている場合、タイムアウトタイマーが発火してゴルーチンがアンブロックされた直後に、別のゴルーチンが同じネットワーク接続に対して新しいタイムアウトを設定する、という競合状態にありました。この状況下で、netpollblock関数が誤った状態に陥り、ランタイムがクラッシュする可能性がありました。

具体的には、runtime_pollWait関数内でnetpollblockが呼び出され、I/Oが準備できるかタイムアウトするまでゴルーチンをブロックします。タイムアウトが発生してゴルーチンがアンブロックされた後、checkerrでエラーがチェックされます。しかし、このチェックの直前に別のゴルーチンが新しいタイムアウトを設定した場合、checkerrはエラーを返さず、netpollblockが再度呼び出されると、予期せぬ動作を引き起こし、最終的にランタイムのパニックやクラッシュにつながる可能性がありました。

この問題は、特に高負荷なネットワークアプリケーションや、頻繁にタイムアウト設定が変更されるようなシナリオで顕在化しやすかったと考えられます。

前提知識の解説

Goランタイムとスケジューラ

Go言語は独自のランタイムとスケジューラを持っています。これはOSのスケジューラとは異なり、Goのゴルーチン(軽量スレッド)を効率的にOSスレッドにマッピングし、実行を管理します。ネットワークI/Oのようなブロッキング操作が発生した場合、Goランタイムはゴルーチンをブロックせず、代わりにそのゴルーチンを非同期I/Oの完了を待つ状態にし、他のゴルーチンを実行できるようにします。

ネットワークI/Oとnetpoll

GoのネットワークI/Oは、内部的にOSの非同期I/Oメカニズム(Linuxのepoll、macOSのkqueue、WindowsのIOCPなど)を利用しています。GoランタイムはこれらのOS機能の上にnetpollという抽象化レイヤーを提供します。

  • PollDesc: ネットワーク接続(ソケット)の状態を管理するためのデータ構造です。I/Oの準備状況やタイムアウト情報などが含まれます。
  • netpollblock: PollDescの状態に基づいてゴルーチンをブロックする関数です。I/Oが準備できるか、タイムアウトが発生するまでゴルーチンをスリープさせます。
  • runtime_pollWait: ネットワークI/O操作(読み書きなど)を行う際に呼び出されるランタイム関数です。PollDescの状態をチェックし、必要に応じてnetpollblockを呼び出してゴルーチンを待機させます。

タイムアウトとデッドライン

Goのnet.Connインターフェースには、SetReadDeadlineSetWriteDeadlineSetDeadlineといったメソッドがあり、ネットワーク操作にタイムアウトを設定できます。これらのデッドラインは、内部的にGoランタイムのタイマー機能と連携し、指定された時間内にI/Oが完了しない場合に操作を中断させます。

競合状態(Race Condition)

複数のゴルーチンが共有リソース(この場合はPollDescの状態や関連するタイマー)に同時にアクセスし、そのアクセス順序によって結果が非決定的に変わる状況を指します。このコミットで修正された問題は、タイムアウトタイマーが発火してゴルーチンがアンブロックされるタイミングと、別のゴルーチンがデッドラインをリセットするタイミングの間に発生する競合状態でした。

技術的詳細

このコミットの技術的詳細は、src/pkg/runtime/netpoll.goc内のruntime_pollWait関数の変更に集約されます。

元のコードでは、netpollblockが一度呼び出され、それがfalse(つまり、I/Oが準備できたかタイムアウトした)を返した場合、checkerrでエラーをチェックし、エラーがなければruntime·throw("runtime_pollWait: unblocked by ioready")というパニックを引き起こしていました。これは、netpollblockfalseを返したにもかかわらず、checkerrがエラーを返さない(つまり、I/Oが準備できたわけでもタイムアウトしたわけでもない)という矛盾した状態を検出した場合の防御的な措置でした。

しかし、このロジックには、タイムアウトタイマーが発火してゴルーチンがアンブロックされた直後に、別のゴルーチンがSetDeadlineを呼び出して新しいタイムアウトを設定するという特定の競合状態が考慮されていませんでした。このシナリオでは、netpollblockはタイムアウトによってfalseを返しますが、その直後にデッドラインがリセットされるため、checkerrはエラーを返さなくなります。結果として、ランタイムは「ioreadyによってアンブロックされた」と誤解し、パニックを引き起こしていました。

この修正では、netpollblockの呼び出しをwhileループで囲むことで、この競合状態に対処しています。

-		if(!netpollblock(pd, mode)) {
+		while(!netpollblock(pd, mode)) {
 			err = checkerr(pd, mode);
-			if(err == 0)
-				runtime·throw("runtime_pollWait: unblocked by ioready");
+			if(err != 0)
+				break;
+			// Can happen if timeout has fired and unblocked us,
+			// but before we had a chance to run, timeout has been reset.
+			// Pretend it has not happened and retry.
 		}

変更後のロジックは以下のようになります。

  1. netpollblock(pd, mode)を呼び出し、I/Oが準備できるかタイムアウトするまで待機します。
  2. netpollblockfalseを返した場合(アンブロックされた場合)、checkerr(pd, mode)を呼び出してエラーをチェックします。
  3. checkerrがエラーを返した場合(err != 0)、ループを抜けます。これは、I/Oエラーまたはタイムアウトによってアンブロックされた正当なケースです。
  4. checkerrがエラーを返さなかった場合(err == 0)、これは競合状態が発生した可能性を示唆します。つまり、タイムアウトによってアンブロックされたが、その直後にデッドラインがリセットされたため、checkerrがエラーを検出できなかった状況です。この場合、ランタイムはパニックを起こす代わりに、コメントにあるように「タイムアウトが発火してアンブロックされたが、実行される前にタイムアウトがリセットされた」と判断し、whileループの先頭に戻ってnetpollblockを再試行します。これにより、ゴルーチンは正しい状態になるまで再度ブロックされることになります。

この変更により、デッドラインのリセットとタイムアウトの発火が同時に発生するような稀な競合状態においても、ランタイムがクラッシュすることなく、正しく動作を継続できるようになりました。

また、この修正を検証するために、src/pkg/net/timeout_test.goTestDeadlineRaceという新しいテストケースが追加されています。このテストは、複数のゴルーチンが非常に短い間隔でSetDeadlineを繰り返し呼び出し、同時にRead操作を行うことで、競合状態を意図的に発生させ、修正が正しく機能することを確認します。

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

src/pkg/net/timeout_test.go

--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -703,3 +703,36 @@ func TestProlongTimeout(t *testing.T) {
 		c.Write(buf[:])
 	}
 }
+
+func TestDeadlineRace(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("skipping test on %q", runtime.GOOS)
+	}
+
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+	ln := newLocalListener(t)
+	defer ln.Close()
+	c, err := Dial("tcp", ln.Addr().String())
+	if err != nil {
+		t.Fatalf("Dial: %v", err)
+	}
+	defer c.Close()
+	done := make(chan bool)
+	go func() {
+		t := time.NewTicker(2 * time.Microsecond).C
+		for {
+			if err := c.SetDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+				break
+			}
+			<-t
+		}
+		done <- true
+	}()
+	var buf [1]byte
+	for i := 0; i < 1024; i++ {
+		c.Read(buf[:]) // ignore possible timeout errors
+	}
+	c.Close()
+	<-done
+}

src/pkg/runtime/netpoll.goc

--- a/src/pkg/runtime/netpoll.goc
+++ b/src/pkg/runtime/netpoll.goc
@@ -113,10 +113,13 @@ func runtime_pollWait(pd *PollDesc, mode int) (err int) {
 	runtime·lock(pd);
 	err = checkerr(pd, mode);
 	if(err == 0) {
-		if(!netpollblock(pd, mode)) {
+		while(!netpollblock(pd, mode)) {
 			err = checkerr(pd, mode);
-			if(err == 0)
-				runtime·throw("runtime_pollWait: unblocked by ioready");
+			if(err != 0)
+				break;
+			// Can happen if timeout has fired and unblocked us,
+			// but before we had a chance to run, timeout has been reset.
+			// Pretend it has not happened and retry.
 		}
 	}
 	runtime·unlock(pd);

コアとなるコードの解説

src/pkg/runtime/netpoll.gocの変更

runtime_pollWait関数内のnetpollblockの呼び出しがif文からwhileループに変更されました。

  • 変更前:

    if(!netpollblock(pd, mode)) {
        err = checkerr(pd, mode);
        if(err == 0)
            runtime·throw("runtime_pollWait: unblocked by ioready");
    }
    

    このロジックでは、netpollblockfalseを返した場合(つまり、待機が解除された場合)、checkerrでエラーを確認します。もしエラーがなければ、それはI/Oが準備できたことを意味するはずですが、もしそうでない場合は「ioreadyによってアンブロックされた」というパニックを引き起こしていました。しかし、前述の競合状態では、タイムアウトでアンブロックされた直後にデッドラインがリセットされるため、checkerrがエラーを返さず、このパニックが誤って発生していました。

  • 変更後:

    while(!netpollblock(pd, mode)) {
        err = checkerr(pd, mode);
        if(err != 0)
            break;
        // Can happen if timeout has fired and unblocked us,
        // but before we had a chance to run, timeout has been reset.
        // Pretend it has not happened and retry.
    }
    

    whileループに変更されたことで、netpollblockfalseを返し、かつcheckerrがエラーを返さない(err == 0)という状況が発生した場合でも、パニックを起こさずにループを再試行するようになりました。これは、タイムアウトによってアンブロックされたが、その直後にデッドラインがリセットされたためにcheckerrがエラーを検出できなかったケースに対応します。ランタイムは、この状況を「一時的な状態」とみなし、再度netpollblockを呼び出してゴルーチンをブロックし直すことで、正しい状態に収束させます。これにより、ランタイムのクラッシュを防ぎ、堅牢性を向上させています。

src/pkg/net/timeout_test.goの追加

TestDeadlineRaceテストは、この修正の有効性を検証するために追加されました。

  • runtime.GOMAXPROCS(4)を設定し、複数のOSスレッドでゴルーチンが並行して実行される環境をシミュレートします。
  • ローカルのTCPリスナーと接続を作成します。
  • 新しいゴルーチンを起動し、非常に短い間隔(2マイクロ秒)で接続のデッドラインを繰り返し設定します。これにより、デッドラインの頻繁なリセットとタイムアウトの発火が同時に発生する可能性を高めます。
  • メインのゴルーチンでは、接続に対して繰り返しRead操作を行います。Readは内部的にruntime_pollWaitを呼び出すため、ここで競合状態が発生する可能性があります。
  • テストは、クラッシュせずに正常に完了することを確認します。これにより、修正が正しく機能し、デッドラインの競合状態によるクラッシュが防止されていることが保証されます。

関連リンク

参考にした情報源リンク

  • Go Issue #6103の議論
  • Goのソースコード(src/pkg/runtime/netpoll.goc, src/pkg/net/timeout_test.go
  • Goのネットワークプログラミングに関する公式ドキュメントやブログ記事(一般的なGoのネットワークI/Oとランタイムの動作理解のため)
  • Goのスケジューラに関する技術解説記事
  • 競合状態に関する一般的なプログラミングの概念

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

このコミットは、Goランタイムにおけるネットワークタイマー関連のクラッシュを修正するものです。具体的には、netpollメカニズムにおける競合状態が原因で発生する可能性のある問題を解決しています。

コミット

commit 9707f269c1eb7ee68a2be93e87eb49d481fb1a84
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Tue Aug 13 12:55:57 2013 +0400

    runtime: fix network timers related crash
    Fixes #6103.
    
    R=golang-dev, alex.brainman
    CC=golang-dev
    https://golang.org/cl/12686045

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

https://github.com/golang/go/commit/9707f269c1eb7ee68a2be93e87eb49d481fb1a84

元コミット内容

このコミットは、Goランタイムにおけるネットワークタイマー関連のクラッシュを修正します。Issue #6103を解決します。

変更の背景

このコミットは、GoランタイムにおけるネットワークI/Oのタイムアウト処理に関連する既知のクラッシュ(Issue #6103)を修正するために導入されました。GoのネットワークI/Oは、内部的にnetpollというメカニズムを使用して非同期処理を実現しています。netpollは、ファイルディスクリプタ(ソケットなど)の準備ができたときに、対応するゴルーチンをスケジュールする役割を担っています。

問題の核心は、ネットワーク操作にタイムアウトが設定されている場合、タイムアウトタイマーが発火してゴルーチンがアンブロックされた直後に、別のゴルーチンが同じネットワーク接続に対して新しいタイムアウトを設定する、という競合状態にありました。この状況下で、netpollblock関数が誤った状態に陥り、ランタイムがクラッシュする可能性がありました。

具体的には、runtime_pollWait関数内でnetpollblockが呼び出され、I/Oが準備できるかタイムアウトするまでゴルーチンをブロックします。タイムアウトが発生してゴルーチンがアンブロックされた後、checkerrでエラーがチェックされます。しかし、このチェックの直前に別のゴルーチンが新しいタイムアウトを設定した場合、checkerrはエラーを返さず、netpollblockが再度呼び出されると、予期せぬ動作を引き起こし、最終的にランタイムのパニックやクラッシュにつながる可能性がありました。

この問題は、特に高負荷なネットワークアプリケーションや、頻繁にタイムアウト設定が変更されるようなシナリオで顕在化しやすかったと考えられます。

前提知識の解説

Goランタイムとスケジューラ

Go言語は独自のランタイムとスケジューラを持っています。これはOSのスケジューラとは異なり、Goのゴルーチン(軽量スレッド)を効率的にOSスレッドにマッピングし、実行を管理します。ネットワークI/Oのようなブロッキング操作が発生した場合、Goランタイムはゴルーチンをブロックせず、代わりにそのゴルーチンを非同期I/Oの完了を待つ状態にし、他のゴルーチンを実行できるようにします。

ネットワークI/Oとnetpoll

GoのネットワークI/Oは、内部的にOSの非同期I/Oメカニズム(Linuxのepoll、macOSのkqueue、WindowsのIOCPなど)を利用しています。GoランタイムはこれらのOS機能の上にnetpollという抽象化レイヤーを提供します。

  • PollDesc: ネットワーク接続(ソケット)の状態を管理するためのデータ構造です。I/Oの準備状況やタイムアウト情報などが含まれます。
  • netpollblock: PollDescの状態に基づいてゴルーチンをブロックする関数です。I/Oが準備できるか、タイムアウトが発生するまでゴルーチンをスリープさせます。
  • runtime_pollWait: ネットワークI/O操作(読み書きなど)を行う際に呼び出されるランタイム関数です。PollDescの状態をチェックし、必要に応じてnetpollblockを呼び出してゴルーチンを待機させます。

タイムアウトとデッドライン

Goのnet.Connインターフェースには、SetReadDeadlineSetWriteDeadlineSetDeadlineといったメソッドがあり、ネットワーク操作にタイムアウトを設定できます。これらのデッドラインは、内部的にGoランタイムのタイマー機能と連携し、指定された時間内にI/Oが完了しない場合に操作を中断させます。

競合状態(Race Condition)

複数のゴルーチンが共有リソース(この場合はPollDescの状態や関連するタイマー)に同時にアクセスし、そのアクセス順序によって結果が非決定的に変わる状況を指します。このコミットで修正された問題は、タイムアウトタイマーが発火してゴルーチンがアンブロックされるタイミングと、別のゴルーチンがデッドラインをリセットするタイミングの間に発生する競合状態でした。

技術的詳細

このコミットの技術的詳細は、src/pkg/runtime/netpoll.goc内のruntime_pollWait関数の変更に集約されます。

元のコードでは、netpollblockが一度呼び出され、それがfalse(つまり、I/Oが準備できたかタイムアウトした)を返した場合、checkerrでエラーをチェックし、エラーがなければruntime·throw("runtime_pollWait: unblocked by ioready")というパニックを引き起こしていました。これは、netpollblockfalseを返したにもかかわらず、checkerrがエラーを返さない(つまり、I/Oが準備できたわけでもタイムアウトしたわけでもない)という矛盾した状態を検出した場合の防御的な措置でした。

しかし、このロジックには、タイムアウトタイマーが発火してゴルーチンがアンブロックされた直後に、別のゴルーチンがSetDeadlineを呼び出して新しいタイムアウトを設定するという特定の競合状態が考慮されていませんでした。このシナリオでは、netpollblockはタイムアウトによってfalseを返しますが、その直後にデッドラインがリセットされるため、checkerrはエラーを返さなくなります。結果として、ランタイムは「ioreadyによってアンブロックされた」と誤解し、パニックを引き起こしていました。

この修正では、netpollblockの呼び出しをwhileループで囲むことで、この競合状態に対処しています。

-		if(!netpollblock(pd, mode)) {
+		while(!netpollblock(pd, mode)) {
 			err = checkerr(pd, mode);
-			if(err == 0)
-				runtime·throw("runtime_pollWait: unblocked by ioready");
+			if(err != 0)
+				break;
+			// Can happen if timeout has fired and unblocked us,
+			// but before we had a chance to run, timeout has been reset.
+			// Pretend it has not happened and retry.
 		}

変更後のロジックは以下のようになります。

  1. netpollblock(pd, mode)を呼び出し、I/Oが準備できるかタイムアウトするまで待機します。
  2. netpollblockfalseを返した場合(アンブロックされた場合)、checkerr(pd, mode)を呼び出してエラーをチェックします。
  3. checkerrがエラーを返した場合(err != 0)、ループを抜けます。これは、I/Oエラーまたはタイムアウトによってアンブロックされた正当なケースです。
  4. checkerrがエラーを返さなかった場合(err == 0)、これは競合状態が発生した可能性を示唆します。つまり、タイムアウトによってアンブロックされたが、その直後にデッドラインがリセットされたため、checkerrがエラーを検出できなかった状況です。この場合、ランタイムはパニックを起こす代わりに、コメントにあるように「タイムアウトが発火してアンブロックされたが、実行される前にタイムアウトがリセットされた」と判断し、whileループの先頭に戻ってnetpollblockを再試行します。これにより、ゴルーチンは正しい状態になるまで再度ブロックされることになります。

この変更により、デッドラインのリセットとタイムアウトの発火が同時に発生するような稀な競合状態においても、ランタイムがクラッシュすることなく、正しく動作を継続できるようになりました。

また、この修正を検証するために、src/pkg/net/timeout_test.goTestDeadlineRaceという新しいテストケースが追加されています。このテストは、複数のゴルーチンが非常に短い間隔でSetDeadlineを繰り返し呼び出し、同時にRead操作を行うことで、競合状態を意図的に発生させ、修正が正しく機能することを確認します。

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

src/pkg/net/timeout_test.go

--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -703,3 +703,36 @@ func TestProlongTimeout(t *testing.T) {
 		c.Write(buf[:])
 	}
 }
+
+func TestDeadlineRace(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("skipping test on %q", runtime.GOOS)
+	}
+
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+	ln := newLocalListener(t)
+	defer ln.Close()
+	c, err := Dial("tcp", ln.Addr().String())
+	if err != nil {
+		t.Fatalf("Dial: %v", err)
+	}
+	defer c.Close()
+	done := make(chan bool)
+	go func() {
+		t := time.NewTicker(2 * time.Microsecond).C
+		for {
+			if err := c.SetDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+				break
+			}
+			<-t
+		}
+		done <- true
+	}()
+	var buf [1]byte
+	for i := 0; i < 1024; i++ {
+		c.Read(buf[:]) // ignore possible timeout errors
+	}
+	c.Close()
+	<-done
+}

src/pkg/runtime/netpoll.goc

--- a/src/pkg/runtime/netpoll.goc
+++ b/src/pkg/runtime/netpoll.goc
@@ -113,10 +113,13 @@ func runtime_pollWait(pd *PollDesc, mode int) (err int) {
 	runtime·lock(pd);
 	err = checkerr(pd, mode);
 	if(err == 0) {
-		if(!netpollblock(pd, mode)) {
+		while(!netpollblock(pd, mode)) {
 			err = checkerr(pd, mode);
-			if(err == 0)
-				runtime·throw("runtime_pollWait: unblocked by ioready");
+			if(err != 0)
+				break;
+			// Can happen if timeout has fired and unblocked us,
+			// but before we had a chance to run, timeout has been reset.
+			// Pretend it has not happened and retry.
 		}
 	}
 	runtime·unlock(pd);

コアとなるコードの解説

src/pkg/runtime/netpoll.gocの変更

runtime_pollWait関数内のnetpollblockの呼び出しがif文からwhileループに変更されました。

  • 変更前:

    if(!netpollblock(pd, mode)) {
        err = checkerr(pd, mode);
        if(err == 0)
            runtime·throw("runtime_pollWait: unblocked by ioready");
    }
    

    このロジックでは、netpollblockfalseを返した場合(つまり、待機が解除された場合)、checkerrでエラーを確認します。もしエラーがなければ、それはI/Oが準備できたことを意味するはずですが、もしそうでない場合は「ioreadyによってアンブロックされた」というパニックを引き起こしていました。しかし、前述の競合状態では、タイムアウトでアンブロックされた直後にデッドラインがリセットされるため、checkerrがエラーを返さず、このパニックが誤って発生していました。

  • 変更後:

    while(!netpollblock(pd, mode)) {
        err = checkerr(pd, mode);
        if(err != 0)
            break;
        // Can happen if timeout has fired and unblocked us,
        // but before we had a chance to run, timeout has been reset.
        // Pretend it has not happened and retry.
    }
    

    whileループに変更されたことで、netpollblockfalseを返し、かつcheckerrがエラーを返さない(err == 0)という状況が発生した場合でも、パニックを起こさずにループを再試行するようになりました。これは、タイムアウトによってアンブロックされたが、その直後にデッドラインがリセットされたためにcheckerrがエラーを検出できなかったケースに対応します。ランタイムは、この状況を「一時的な状態」とみなし、再度netpollblockを呼び出してゴルーチンをブロックし直すことで、正しい状態に収束させます。これにより、ランタイムのクラッシュを防ぎ、堅牢性を向上させています。

src/pkg/net/timeout_test.goの追加

TestDeadlineRaceテストは、この修正の有効性を検証するために追加されました。

  • runtime.GOMAXPROCS(4)を設定し、複数のOSスレッドでゴルーチンが並行して実行される環境をシミュレートします。
  • ローカルのTCPリスナーと接続を作成します。
  • 新しいゴルーチンを起動し、非常に短い間隔(2マイクロ秒)で接続のデッドラインを繰り返し設定します。これにより、デッドラインの頻繁なリセットとタイムアウトの発火が同時に発生する可能性を高めます。
  • メインのゴルーチンでは、接続に対して繰り返しRead操作を行います。Readは内部的にruntime_pollWaitを呼び出すため、ここで競合状態が発生する可能性があります。
  • テストは、クラッシュせずに正常に完了することを確認します。これにより、修正が正しく機能し、デッドラインの競合状態によるクラッシュが防止されていることが保証されます。

関連リンク

参考にした情報源リンク

  • Go Issue #6103の議論
  • Goのソースコード(src/pkg/runtime/netpoll.goc, src/pkg/net/timeout_test.go
  • Goのネットワークプログラミングに関する公式ドキュメントやブログ記事(一般的なGoのネットワークI/Oとランタイムの動作理解のため)
  • Goのスケジューラに関する技術解説記事
  • 競合状態に関する一般的なプログラミングの概念