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

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

このコミットは、Go言語の標準ライブラリである bufio パッケージにおけるバグ修正に関するものです。bufio パッケージは、I/O操作の効率を向上させるためにバッファリング機能を提供します。具体的には、io.Readerio.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 は発生したエラーです。このメソッドは、以下のいずれかの状態を返すことができます。

  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.WriterReadFrom メソッドが、基となる 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.Readerio.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操作が進行していないことを示します。これは、通常、ReadWrite のような操作が連続して (0, nil) を返し、データの読み書きが全く進まない状況で返されます。無限ループに陥るのを防ぐためのメカニズムとして機能します。

技術的詳細

このコミットの技術的な核心は、bufio.Writer.ReadFrom メソッドの内部ループの変更にあります。

bufio.Writer.ReadFrom の修正

修正前は、ReadFrom メソッドの内部で基となるリーダー rRead メソッドを呼び出し、m == 0 (読み込んだバイト数が0) の場合にすぐにループを break していました。これは、Read(0, nil) を返した場合に、ReadFrom が早期に終了してしまう原因となっていました。

修正後は、maxConsecutiveEmptyReads という新しい定数(値は100)が導入されました。ReadFrom メソッドは、r.Read(0, nil) を返した場合でも、すぐにループを終了せず、maxConsecutiveEmptyReads 回まで Read を再試行するようになりました。

  • 新しい変数 nr が導入され、連続して (0, nil) が返された回数をカウントします。
  • r.Readm != 0 (データが読み込まれた) または err != nil (エラーが発生した) を返した場合、ループは break します。
  • nrmaxConsecutiveEmptyReads に達した場合、つまり100回連続で (0, nil) が返された場合、ReadFromio.ErrNoProgress エラーを返して終了します。これは、これ以上データの進行が見込めないと判断されたためです。

この変更により、ReadFrom は一時的な (0, nil) の状態を適切に処理し、真のEOFまたは非nilのエラーが発生するまで、または進行がないと判断されるまで、データの読み込みを継続するようになります。

bufio.Scanner.Scan の修正

src/pkg/bufio/scan.goScanner.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: ReadFromemptyThenNonEmptyReader から (0, nil) を受け取った後でも、最終的にすべてのデータを読み込むことを検証します。
  • TestWriterReadFromErrNoProgress: ReadFrommaxConsecutiveEmptyReads 回連続で (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.goReadFrom メソッド

変更の中心は bufio.goWriter.ReadFrom メソッド内のループです。

  1. const maxConsecutiveEmptyReads = 100 の追加: これは、io.Reader が連続して (0, nil) を返すことを許容する最大回数を定義する定数です。この回数を超えると、I/O操作が進行していないと判断されます。

  2. 新しいループ構造:

    	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.goScan メソッド

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 > 100if loop > maxConsecutiveEmptyReads に変更されました。
  • これにより、Scanner が基となるリーダーからデータを読み込む際に、連続して (0, nil) が返される回数の上限が、bufio パッケージ全体で一貫した maxConsecutiveEmptyReads 定数によって制御されるようになりました。これにより、Scanner も同様に進行しないI/O操作によって無限ループに陥るのを防ぎます。

関連リンク

参考にした情報源リンク

  • 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.ReaderReadメソッドの挙動に関する議論 (例: 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.goReadFrom メソッド

変更の中心は bufio.goWriter.ReadFrom メソッド内のループです。

  1. const maxConsecutiveEmptyReads = 100 の追加: これは、io.Reader が連続して (0, nil) を返すことを許容する最大回数を定義する定数です。この回数を超えると、I/O操作が進行していないと判断されます。

  2. 新しいループ構造:

    	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.goScan メソッド

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 > 100if loop > maxConsecutiveEmptyReads に変更されました。
  • これにより、Scanner が基となるリーダーからデータを読み込む際に、連続して (0, nil) が返される回数の上限が、bufio パッケージ全体で一貫した maxConsecutiveEmptyReads 定数によって制御されるようになりました。これにより、Scanner も同様に進行しないI/O操作によって無限ループに陥るのを防ぎます。

関連リンク

参考にした情報源リンク