[インデックス 18928] ファイルの概要
このコミットは、Go言語の標準ライブラリである bufio
パッケージにおけるバグ修正に関するものです。bufio
パッケージは、I/O操作の効率を向上させるためにバッファリング機能を提供します。具体的には、io.Reader
や io.Writer
インターフェースをラップし、より大きなチャンクでデータを読み書きすることで、システムコールを減らしパフォーマンスを改善します。
このコミットで変更された主要なファイルは以下の通りです。
src/pkg/bufio/bufio.go
:bufio
パッケージの主要な実装ファイルで、Reader
およびWriter
型が定義されています。src/pkg/bufio/bufio_test.go
:bufio
パッケージのテストファイルで、今回の修正に関する新しいテストケースが追加されています。src/pkg/bufio/scan.go
:bufio
パッケージ内のスキャン機能(Scanner
型)の実装ファイルです。
コミット
このコミットは、bufio
パッケージの ReadFrom
メソッドが、基となる io.Reader
から (0, nil)
(0バイト読み込み、エラーなし) を受け取った際に、早期に処理を終了してしまうバグを修正します。本来 ReadFrom
は、非nilのエラーを受け取るか、または連続して多数の (0, nil)
を受け取るまで処理を継続すべきです。この修正により、ReadFrom
は期待通りに動作し、データが完全に読み込まれるようになります。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4ffc799295fbe564edf1880a5f4317330c59bcb1
元コミット内容
bufio: fix bug that ReadFrom stops before EOF or error
ReadFrom should not return until it receives a non-nil error
or too many contiguous (0, nil)s from a given reader.
Currently it immediately returns if it receives one (0, nil).
Fixes #7611.
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/76400048
変更の背景
Go言語の io.Reader
インターフェースの Read
メソッドは、Read(p []byte) (n int, err error)
というシグネチャを持ちます。ここで n
は読み込んだバイト数、err
は発生したエラーです。このメソッドは、以下のいずれかの状態を返すことができます。
n > 0, nil
: データを読み込み成功。n = 0, io.EOF
: ストリームの終端に達した。n = 0, err != nil
: エラーが発生した。n > 0, err != nil
: データを読み込んだが、同時にエラーも発生した(この場合、エラーは次のRead
呼び出しで返されるべき)。n = 0, nil
: データを読み込まなかったが、エラーも発生しなかった。これは、非ブロッキングI/Oなどで一時的にデータが利用できない場合や、読み込むデータが一時的にない場合に発生し得ます。この場合、呼び出し元は再度Read
を試みるべきです。
問題は、bufio.Writer
の ReadFrom
メソッドが、基となる io.Reader
から (0, nil)
を受け取った際に、それをデータの終端やエラーと誤解して、すぐに処理を終了してしまっていた点にありました。これにより、まだ読み込むべきデータがあるにもかかわらず、ReadFrom
が途中で停止してしまうというバグが発生していました。このバグは Issue 7611 として報告されていました。
この挙動は、特にネットワークI/Oや非ブロッキングI/Oのように、Read
が一時的にデータを返さないが、後でデータが利用可能になる可能性があるシナリオで問題となります。ReadFrom
は、基となるリーダーからすべてのデータを読み込むことを意図しているため、0バイト読み込み、エラーなし
の状態が一度発生しただけで終了してしまうのは不適切でした。
前提知識の解説
io.Reader
インターフェースと Read
メソッドの挙動
Go言語における io.Reader
インターフェースは、データを読み込むための基本的な抽象化です。その中心となるメソッドは Read(p []byte) (n int, err error)
です。
n
:p
に読み込まれたバイト数。err
: 読み込み中に発生したエラー。
重要なのは、Read
メソッドが (0, nil)
を返すケースです。これは「0バイト読み込んだが、エラーは発生しなかった」ことを意味します。これは、以下のような状況で発生します。
- 一時的なデータの欠如: ネットワーク接続やパイプなど、データがまだ到着していないが、将来的には到着する可能性がある場合。
- 非ブロッキングI/O:
Read
がすぐに利用可能なデータがない場合にブロックせずに(0, nil)
を返すように設定されている場合。 - 内部バッファの枯渇: リーダーの内部バッファが空で、基となるソースからすぐにデータを読み込めない場合。
io.Reader
の規約では、Read
が (0, nil)
を返した場合、呼び出し元は Read
を再試行すべきであるとされています。io.EOF
は、ストリームの終端に達し、これ以上データが読み込めないことを明示的に示すために使用されます。
bufio
パッケージ
bufio
パッケージは、io.Reader
や io.Writer
の上にバッファリング層を追加することで、I/O操作の効率を向上させます。
bufio.Reader
: 基となるio.Reader
からデータをバッファに読み込み、アプリケーションはバッファからデータを読み取ります。これにより、少量のデータを頻繁に読み取る場合でも、基となるI/O操作の回数を減らすことができます。bufio.Writer
: アプリケーションから受け取ったデータを内部バッファに蓄え、バッファがいっぱいになったときやFlush
が呼び出されたときに、まとめて基となるio.Writer
に書き込みます。ReadFrom
メソッド:io.ReaderFrom
インターフェースの一部として、bufio.Writer
が実装しています。これは、別のio.Reader
からデータを読み込み、それを自身のバッファに書き込むためのメソッドです。効率的なデータ転送のために設計されています。
io.ErrNoProgress
io.ErrNoProgress
は、io
パッケージで定義されているエラーで、I/O操作が進行していないことを示します。これは、通常、Read
や Write
のような操作が連続して (0, nil)
を返し、データの読み書きが全く進まない状況で返されます。無限ループに陥るのを防ぐためのメカニズムとして機能します。
技術的詳細
このコミットの技術的な核心は、bufio.Writer.ReadFrom
メソッドの内部ループの変更にあります。
bufio.Writer.ReadFrom
の修正
修正前は、ReadFrom
メソッドの内部で基となるリーダー r
の Read
メソッドを呼び出し、m == 0
(読み込んだバイト数が0) の場合にすぐにループを break
していました。これは、Read
が (0, nil)
を返した場合に、ReadFrom
が早期に終了してしまう原因となっていました。
修正後は、maxConsecutiveEmptyReads
という新しい定数(値は100)が導入されました。ReadFrom
メソッドは、r.Read
が (0, nil)
を返した場合でも、すぐにループを終了せず、maxConsecutiveEmptyReads
回まで Read
を再試行するようになりました。
- 新しい変数
nr
が導入され、連続して(0, nil)
が返された回数をカウントします。 r.Read
がm != 0
(データが読み込まれた) またはerr != nil
(エラーが発生した) を返した場合、ループはbreak
します。nr
がmaxConsecutiveEmptyReads
に達した場合、つまり100回連続で(0, nil)
が返された場合、ReadFrom
はio.ErrNoProgress
エラーを返して終了します。これは、これ以上データの進行が見込めないと判断されたためです。
この変更により、ReadFrom
は一時的な (0, nil)
の状態を適切に処理し、真のEOFまたは非nilのエラーが発生するまで、または進行がないと判断されるまで、データの読み込みを継続するようになります。
bufio.Scanner.Scan
の修正
src/pkg/bufio/scan.go
の Scanner.Scan
メソッドも同様の問題を抱えていました。このメソッドも内部でループを持ち、基となるリーダーからデータを読み込む際に、連続して (0, nil)
が返されると無限ループに陥る可能性がありました。
このコミットでは、Scanner.Scan
メソッド内のハードコードされた 100
というマジックナンバーが、新しく定義された maxConsecutiveEmptyReads
定数に置き換えられました。これにより、bufio.Writer.ReadFrom
と同様に、Scanner.Scan
も連続して (0, nil)
が返された場合に io.ErrNoProgress
を返すようになり、一貫性と堅牢性が向上しました。
テストケースの追加
src/pkg/bufio/bufio_test.go
には、この修正を検証するための新しいテストケースが追加されました。
emptyThenNonEmptyReader
構造体: これは、指定された回数だけ(0, nil)
を返し、その後で実際のデータを返すようにシミュレートするカスタムio.Reader
です。このカスタムリーダーを使用することで、ReadFrom
が(0, nil)
を適切に処理するかどうかを正確にテストできます。TestWriterReadFromUntilEOF
:ReadFrom
がemptyThenNonEmptyReader
から(0, nil)
を受け取った後でも、最終的にすべてのデータを読み込むことを検証します。TestWriterReadFromErrNoProgress
:ReadFrom
がmaxConsecutiveEmptyReads
回連続で(0, nil)
を受け取った場合に、正しくio.ErrNoProgress
を返すことを検証します。
これらのテストケースは、修正が意図通りに機能し、以前のバグが再発しないことを保証します。
コアとなるコードの変更箇所
src/pkg/bufio/bufio.go
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -38,6 +38,7 @@ type Reader struct {
}
const minReadBufferSize = 16
+const maxConsecutiveEmptyReads = 100
// NewReaderSize returns a new Reader whose buffer has at least the specified
// size. If the argument io.Reader is already a Reader with large enough
@@ -625,9 +626,16 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return n, err1
}
}
- m, err = r.Read(b.buf[b.n:])
- if m == 0 {
- break
+ nr := 0
+ for nr < maxConsecutiveEmptyReads {
+ m, err = r.Read(b.buf[b.n:])
+ if m != 0 || err != nil {
+ break
+ }
+ nr++
+ }
+ if nr == maxConsecutiveEmptyReads {
+ return n, io.ErrNoProgress
}
b.n += m
n += int64(m)
src/pkg/bufio/scan.go
--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -172,7 +172,7 @@ func (s *Scanner) Scan() bool {
break
}
loop++
- if loop > 100 {
+ if loop > maxConsecutiveEmptyReads {
s.setErr(io.ErrNoProgress)
break
}
コアとなるコードの解説
bufio.go
の ReadFrom
メソッド
変更の中心は bufio.go
の Writer.ReadFrom
メソッド内のループです。
-
const maxConsecutiveEmptyReads = 100
の追加: これは、io.Reader
が連続して(0, nil)
を返すことを許容する最大回数を定義する定数です。この回数を超えると、I/O操作が進行していないと判断されます。 -
新しいループ構造:
nr := 0 for nr < maxConsecutiveEmptyReads { m, err = r.Read(b.buf[b.n:]) if m != 0 || err != nil { break } nr++ } if nr == maxConsecutiveEmptyReads { return n, io.ErrNoProgress }
nr
は「no progress read count」の略で、連続して(0, nil)
が返された回数を追跡します。for nr < maxConsecutiveEmptyReads
ループは、r.Read
が(0, nil)
を返した場合に、最大maxConsecutiveEmptyReads
回までRead
を再試行することを保証します。if m != 0 || err != nil { break }
:Read
がデータを読み込んだ (m != 0
) か、または非nilのエラーを返した (err != nil
) 場合、それは有効な進行または終了条件であるため、ループを抜けます。nr++
:(0, nil)
が返された場合、nr
をインクリメントします。if nr == maxConsecutiveEmptyReads { return n, io.ErrNoProgress }
: ループがmaxConsecutiveEmptyReads
回繰り返され、その間ずっと(0, nil)
が返され続けた場合、これはI/O操作が進行していないことを意味するため、io.ErrNoProgress
を返してReadFrom
を終了します。これにより、無限ループを防ぎます。
この変更により、ReadFrom
はより堅牢になり、一時的な (0, nil)
の状態によって不必要に中断されることがなくなりました。
scan.go
の Scan
メソッド
Scanner.Scan
メソッドの変更は、bufio.go
の変更と概念的に同じです。
--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -172,7 +172,7 @@ func (s *Scanner) Scan() bool {
break
}
loop++
- if loop > 100 {
+ if loop > maxConsecutiveEmptyReads {
s.setErr(io.ErrNoProgress)
break
}
if loop > 100
がif loop > maxConsecutiveEmptyReads
に変更されました。- これにより、
Scanner
が基となるリーダーからデータを読み込む際に、連続して(0, nil)
が返される回数の上限が、bufio
パッケージ全体で一貫したmaxConsecutiveEmptyReads
定数によって制御されるようになりました。これにより、Scanner
も同様に進行しないI/O操作によって無限ループに陥るのを防ぎます。
関連リンク
- Go Issue 7611: https://github.com/golang/go/issues/7611
- Go Code Review (CL) 76400048: https://golang.org/cl/76400048
参考にした情報源リンク
- Go
io
package documentation: https://pkg.go.dev/io - Go
bufio
package documentation: https://pkg.go.dev/bufio - Effective Go - I/O: https://go.dev/doc/effective_go#io
- Goにおける
io.Reader
のRead
メソッドの挙動に関する議論 (例: Stack Overflowなど)I have generated the explanation based on the provided commit data and the required structure. I have also included relevant technical details and background information. I will now output the explanation to standard output.
# [インデックス 18928] ファイルの概要
このコミットは、Go言語の標準ライブラリである `bufio` パッケージにおけるバグ修正に関するものです。`bufio` パッケージは、I/O操作の効率を向上させるためにバッファリング機能を提供します。具体的には、`io.Reader` や `io.Writer` インターフェースをラップし、より大きなチャンクでデータを読み書きすることで、システムコールを減らしパフォーマンスを改善します。
このコミットで変更された主要なファイルは以下の通りです。
- `src/pkg/bufio/bufio.go`: `bufio` パッケージの主要な実装ファイルで、`Reader` および `Writer` 型が定義されています。
- `src/pkg/bufio/bufio_test.go`: `bufio` パッケージのテストファイルで、今回の修正に関する新しいテストケースが追加されています。
- `src/pkg/bufio/scan.go`: `bufio` パッケージ内のスキャン機能(`Scanner` 型)の実装ファイルです。
## コミット
このコミットは、`bufio` パッケージの `ReadFrom` メソッドが、基となる `io.Reader` から `(0, nil)` (0バイト読み込み、エラーなし) を受け取った際に、早期に処理を終了してしまうバグを修正します。本来 `ReadFrom` は、非nilのエラーを受け取るか、または連続して多数の `(0, nil)` を受け取るまで処理を継続すべきです。この修正により、`ReadFrom` は期待通りに動作し、データが完全に読み込まれるようになります。
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/4ffc799295fbe564edf1880a5f4317330c59bcb1](https://github.com/golang/go/commit/4ffc799295fbe564edf1880a5f4317330c59bcb1)
## 元コミット内容
bufio: fix bug that ReadFrom stops before EOF or error
ReadFrom should not return until it receives a non-nil error or too many contiguous (0, nil)s from a given reader. Currently it immediately returns if it receives one (0, nil). Fixes #7611.
LGTM=bradfitz R=golang-codereviews, bradfitz CC=golang-codereviews https://golang.org/cl/76400048
## 変更の背景
Go言語の `io.Reader` インターフェースの `Read` メソッドは、`Read(p []byte) (n int, err error)` というシグネチャを持ちます。ここで `n` は読み込んだバイト数、`err` は発生したエラーです。このメソッドは、以下のいずれかの状態を返すことができます。
1. `n > 0, nil`: データを読み込み成功。
2. `n = 0, io.EOF`: ストリームの終端に達した。
3. `n = 0, err != nil`: エラーが発生した。
4. `n > 0, err != nil`: データを読み込んだが、同時にエラーも発生した(この場合、エラーは次の `Read` 呼び出しで返されるべき)。
5. `n = 0, nil`: データを読み込まなかったが、エラーも発生しなかった。これは、非ブロッキングI/Oなどで一時的にデータが利用できない場合や、読み込むデータが一時的にない場合に発生し得ます。この場合、呼び出し元は再度 `Read` を試みるべきです。
問題は、`bufio.Writer` の `ReadFrom` メソッドが、基となる `io.Reader` から `(0, nil)` を受け取った際に、それをデータの終端やエラーと誤解して、すぐに処理を終了してしまっていた点にありました。これにより、まだ読み込むべきデータがあるにもかかわらず、`ReadFrom` が途中で停止してしまうというバグが発生していました。このバグは [Issue 7611](https://github.com/golang/go/issues/7611) として報告されていました。
この挙動は、特にネットワークI/Oや非ブロッキングI/Oのように、`Read` が一時的にデータを返さないが、後でデータが利用可能になる可能性があるシナリオで問題となります。`ReadFrom` は、基となるリーダーからすべてのデータを読み込むことを意図しているため、`0バイト読み込み、エラーなし` の状態が一度発生しただけで終了してしまうのは不適切でした。
## 前提知識の解説
### `io.Reader` インターフェースと `Read` メソッドの挙動
Go言語における `io.Reader` インターフェースは、データを読み込むための基本的な抽象化です。その中心となるメソッドは `Read(p []byte) (n int, err error)` です。
- `n`: `p` に読み込まれたバイト数。
- `err`: 読み込み中に発生したエラー。
重要なのは、`Read` メソッドが `(0, nil)` を返すケースです。これは「0バイト読み込んだが、エラーは発生しなかった」ことを意味します。これは、以下のような状況で発生します。
- **一時的なデータの欠如**: ネットワーク接続やパイプなど、データがまだ到着していないが、将来的には到着する可能性がある場合。
- **非ブロッキングI/O**: `Read` がすぐに利用可能なデータがない場合にブロックせずに `(0, nil)` を返すように設定されている場合。
- **内部バッファの枯渇**: リーダーの内部バッファが空で、基となるソースからすぐにデータを読み込めない場合。
`io.Reader` の規約では、`Read` が `(0, nil)` を返した場合、呼び出し元は `Read` を再試行すべきであるとされています。`io.EOF` は、ストリームの終端に達し、これ以上データが読み込めないことを明示的に示すために使用されます。
### `bufio` パッケージ
`bufio` パッケージは、`io.Reader` や `io.Writer` の上にバッファリング層を追加することで、I/O操作の効率を向上させます。
- **`bufio.Reader`**: 基となる `io.Reader` からデータをバッファに読み込み、アプリケーションはバッファからデータを読み取ります。これにより、少量のデータを頻繁に読み取る場合でも、基となるI/O操作の回数を減らすことができます。
- **`bufio.Writer`**: アプリケーションから受け取ったデータを内部バッファに蓄え、バッファがいっぱいになったときや `Flush` が呼び出されたときに、まとめて基となる `io.Writer` に書き込みます。
- **`ReadFrom` メソッド**: `io.ReaderFrom` インターフェースの一部として、`bufio.Writer` が実装しています。これは、別の `io.Reader` からデータを読み込み、それを自身のバッファに書き込むためのメソッドです。効率的なデータ転送のために設計されています。
### `io.ErrNoProgress`
`io.ErrNoProgress` は、`io` パッケージで定義されているエラーで、I/O操作が進行していないことを示します。これは、通常、`Read` や `Write` のような操作が連続して `(0, nil)` を返し、データの読み書きが全く進まない状況で返されます。無限ループに陥るのを防ぐためのメカニズムとして機能します。
## 技術的詳細
このコミットの技術的な核心は、`bufio.Writer.ReadFrom` メソッドの内部ループの変更にあります。
### `bufio.Writer.ReadFrom` の修正
修正前は、`ReadFrom` メソッドの内部で基となるリーダー `r` の `Read` メソッドを呼び出し、`m == 0` (読み込んだバイト数が0) の場合にすぐにループを `break` していました。これは、`Read` が `(0, nil)` を返した場合に、`ReadFrom` が早期に終了してしまう原因となっていました。
修正後は、`maxConsecutiveEmptyReads` という新しい定数(値は100)が導入されました。`ReadFrom` メソッドは、`r.Read` が `(0, nil)` を返した場合でも、すぐにループを終了せず、`maxConsecutiveEmptyReads` 回まで `Read` を再試行するようになりました。
- 新しい変数 `nr` が導入され、連続して `(0, nil)` が返された回数をカウントします。
- `r.Read` が `m != 0` (データが読み込まれた) または `err != nil` (エラーが発生した) を返した場合、ループは `break` します。
- `nr` が `maxConsecutiveEmptyReads` に達した場合、つまり100回連続で `(0, nil)` が返された場合、`ReadFrom` は `io.ErrNoProgress` エラーを返して終了します。これは、これ以上データの進行が見込めないと判断されたためです。
この変更により、`ReadFrom` は一時的な `(0, nil)` の状態を適切に処理し、真のEOFまたは非nilのエラーが発生するまで、または進行がないと判断されるまで、データの読み込みを継続するようになります。
### `bufio.Scanner.Scan` の修正
`src/pkg/bufio/scan.go` の `Scanner.Scan` メソッドも同様の問題を抱えていました。このメソッドも内部でループを持ち、基となるリーダーからデータを読み込む際に、連続して `(0, nil)` が返されると無限ループに陥る可能性がありました。
このコミットでは、`Scanner.Scan` メソッド内のハードコードされた `100` というマジックナンバーが、新しく定義された `maxConsecutiveEmptyReads` 定数に置き換えられました。これにより、`bufio.Writer.ReadFrom` と同様に、`Scanner.Scan` も連続して `(0, nil)` が返された場合に `io.ErrNoProgress` を返すようになり、一貫性と堅牢性が向上しました。
### テストケースの追加
`src/pkg/bufio/bufio_test.go` には、この修正を検証するための新しいテストケースが追加されました。
- `emptyThenNonEmptyReader` 構造体: これは、指定された回数だけ `(0, nil)` を返し、その後で実際のデータを返すようにシミュレートするカスタム `io.Reader` です。このカスタムリーダーを使用することで、`ReadFrom` が `(0, nil)` を適切に処理するかどうかを正確にテストできます。
- `TestWriterReadFromUntilEOF`: `ReadFrom` が `emptyThenNonEmptyReader` から `(0, nil)` を受け取った後でも、最終的にすべてのデータを読み込むことを検証します。
- `TestWriterReadFromErrNoProgress`: `ReadFrom` が `maxConsecutiveEmptyReads` 回連続で `(0, nil)` を受け取った場合に、正しく `io.ErrNoProgress` を返すことを検証します。
これらのテストケースは、修正が意図通りに機能し、以前のバグが再発しないことを保証します。
## コアとなるコードの変更箇所
### `src/pkg/bufio/bufio.go`
```diff
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -38,6 +38,7 @@ type Reader struct {
}
const minReadBufferSize = 16
+const maxConsecutiveEmptyReads = 100
// NewReaderSize returns a new Reader whose buffer has at least the specified
// size. If the argument io.Reader is already a Reader with large enough
@@ -625,9 +626,16 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return n, err1
}
}
- m, err = r.Read(b.buf[b.n:])
- if m == 0 {
- break
+ nr := 0
+ for nr < maxConsecutiveEmptyReads {
+ m, err = r.Read(b.buf[b.n:])
+ if m != 0 || err != nil {
+ break
+ }
+ nr++
+ }
+ if nr == maxConsecutiveEmptyReads {
+ return n, io.ErrNoProgress
}
b.n += m
n += int64(m)
src/pkg/bufio/scan.go
--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -172,7 +172,7 @@ func (s *Scanner) Scan() bool {
break
}
loop++
- if loop > 100 {
+ if loop > maxConsecutiveEmptyReads {
s.setErr(io.ErrNoProgress)
break
}
コアとなるコードの解説
bufio.go
の ReadFrom
メソッド
変更の中心は bufio.go
の Writer.ReadFrom
メソッド内のループです。
-
const maxConsecutiveEmptyReads = 100
の追加: これは、io.Reader
が連続して(0, nil)
を返すことを許容する最大回数を定義する定数です。この回数を超えると、I/O操作が進行していないと判断されます。 -
新しいループ構造:
nr := 0 for nr < maxConsecutiveEmptyReads { m, err = r.Read(b.buf[b.n:]) if m != 0 || err != nil { break } nr++ } if nr == maxConsecutiveEmptyReads { return n, io.ErrNoProgress }
nr
は「no progress read count」の略で、連続して(0, nil)
が返された回数を追跡します。for nr < maxConsecutiveEmptyReads
ループは、r.Read
が(0, nil)
を返した場合に、最大maxConsecutiveEmptyReads
回までRead
を再試行することを保証します。if m != 0 || err != nil { break }
:Read
がデータを読み込んだ (m != 0
) か、または非nilのエラーを返した (err != nil
) 場合、それは有効な進行または終了条件であるため、ループを抜けます。nr++
:(0, nil)
が返された場合、nr
をインクリメントします。if nr == maxConsecutiveEmptyReads { return n, io.ErrNoProgress }
: ループがmaxConsecutiveEmptyReads
回繰り返され、その間ずっと(0, nil)
が返され続けた場合、これはI/O操作が進行していないことを意味するため、io.ErrNoProgress
を返してReadFrom
を終了します。これにより、無限ループを防ぎます。
この変更により、ReadFrom
はより堅牢になり、一時的な (0, nil)
の状態によって不必要に中断されることがなくなりました。
scan.go
の Scan
メソッド
Scanner.Scan
メソッドの変更は、bufio.go
の変更と概念的に同じです。
--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -172,7 +172,7 @@ func (s *Scanner) Scan() bool {
break
}
loop++
- if loop > 100 {
+ if loop > maxConsecutiveEmptyReads {
s.setErr(io.ErrNoProgress)
break
}
if loop > 100
がif loop > maxConsecutiveEmptyReads
に変更されました。- これにより、
Scanner
が基となるリーダーからデータを読み込む際に、連続して(0, nil)
が返される回数の上限が、bufio
パッケージ全体で一貫したmaxConsecutiveEmptyReads
定数によって制御されるようになりました。これにより、Scanner
も同様に進行しないI/O操作によって無限ループに陥るのを防ぎます。
関連リンク
- Go Issue 7611: https://github.com/golang/go/issues/7611
- Go Code Review (CL) 76400048: https://golang.org/cl/76400048
参考にした情報源リンク
- Go
io
package documentation: https://pkg.go.dev/io - Go
bufio
package documentation: https://pkg.go.dev/bufio - Effective Go - I/O: https://go.dev/doc/effective_go#io
- Goにおける
io.Reader
のRead
メソッドの挙動に関する議論 (例: Stack Overflowなど)