[インデックス 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
インターフェースには、SetReadDeadline
、SetWriteDeadline
、SetDeadline
といったメソッドがあり、ネットワーク操作にタイムアウトを設定できます。これらのデッドラインは、内部的に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")
というパニックを引き起こしていました。これは、netpollblock
がfalse
を返したにもかかわらず、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.
}
変更後のロジックは以下のようになります。
netpollblock(pd, mode)
を呼び出し、I/Oが準備できるかタイムアウトするまで待機します。netpollblock
がfalse
を返した場合(アンブロックされた場合)、checkerr(pd, mode)
を呼び出してエラーをチェックします。checkerr
がエラーを返した場合(err != 0
)、ループを抜けます。これは、I/Oエラーまたはタイムアウトによってアンブロックされた正当なケースです。checkerr
がエラーを返さなかった場合(err == 0
)、これは競合状態が発生した可能性を示唆します。つまり、タイムアウトによってアンブロックされたが、その直後にデッドラインがリセットされたため、checkerr
がエラーを検出できなかった状況です。この場合、ランタイムはパニックを起こす代わりに、コメントにあるように「タイムアウトが発火してアンブロックされたが、実行される前にタイムアウトがリセットされた」と判断し、while
ループの先頭に戻ってnetpollblock
を再試行します。これにより、ゴルーチンは正しい状態になるまで再度ブロックされることになります。
この変更により、デッドラインのリセットとタイムアウトの発火が同時に発生するような稀な競合状態においても、ランタイムがクラッシュすることなく、正しく動作を継続できるようになりました。
また、この修正を検証するために、src/pkg/net/timeout_test.go
にTestDeadlineRace
という新しいテストケースが追加されています。このテストは、複数のゴルーチンが非常に短い間隔で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"); }
このロジックでは、
netpollblock
がfalse
を返した場合(つまり、待機が解除された場合)、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
ループに変更されたことで、netpollblock
がfalse
を返し、かつ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: https://github.com/golang/go/issues/6103
- Go CL 12686045: https://golang.org/cl/12686045
参考にした情報源リンク
- 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
インターフェースには、SetReadDeadline
、SetWriteDeadline
、SetDeadline
といったメソッドがあり、ネットワーク操作にタイムアウトを設定できます。これらのデッドラインは、内部的に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")
というパニックを引き起こしていました。これは、netpollblock
がfalse
を返したにもかかわらず、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.
}
変更後のロジックは以下のようになります。
netpollblock(pd, mode)
を呼び出し、I/Oが準備できるかタイムアウトするまで待機します。netpollblock
がfalse
を返した場合(アンブロックされた場合)、checkerr(pd, mode)
を呼び出してエラーをチェックします。checkerr
がエラーを返した場合(err != 0
)、ループを抜けます。これは、I/Oエラーまたはタイムアウトによってアンブロックされた正当なケースです。checkerr
がエラーを返さなかった場合(err == 0
)、これは競合状態が発生した可能性を示唆します。つまり、タイムアウトによってアンブロックされたが、その直後にデッドラインがリセットされたため、checkerr
がエラーを検出できなかった状況です。この場合、ランタイムはパニックを起こす代わりに、コメントにあるように「タイムアウトが発火してアンブロックされたが、実行される前にタイムアウトがリセットされた」と判断し、while
ループの先頭に戻ってnetpollblock
を再試行します。これにより、ゴルーチンは正しい状態になるまで再度ブロックされることになります。
この変更により、デッドラインのリセットとタイムアウトの発火が同時に発生するような稀な競合状態においても、ランタイムがクラッシュすることなく、正しく動作を継続できるようになりました。
また、この修正を検証するために、src/pkg/net/timeout_test.go
にTestDeadlineRace
という新しいテストケースが追加されています。このテストは、複数のゴルーチンが非常に短い間隔で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"); }
このロジックでは、
netpollblock
がfalse
を返した場合(つまり、待機が解除された場合)、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
ループに変更されたことで、netpollblock
がfalse
を返し、かつ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: https://github.com/golang/go/issues/6103
- Go CL 12686045: https://golang.org/cl/12686045
参考にした情報源リンク
- Go Issue #6103の議論
- Goのソースコード(
src/pkg/runtime/netpoll.goc
,src/pkg/net/timeout_test.go
) - Goのネットワークプログラミングに関する公式ドキュメントやブログ記事(一般的なGoのネットワークI/Oとランタイムの動作理解のため)
- Goのスケジューラに関する技術解説記事
- 競合状態に関する一般的なプログラミングの概念