[インデックス 11511] ファイルの概要
このコミットは、Go言語のネットワークパッケージにおけるWindowsビルドの問題を修正するものです。具体的には、非同期I/O操作が即座に完了した場合に発生する可能性のあるエラーハンドリングの不備を解消しています。
コミット
commit 31d908baaf480db25db3d1fa2c229410fa0e76c6
Author: Russ Cox <rsc@golang.org>
Date: Tue Jan 31 11:20:34 2012 -0500
net: fix windows build
TBR=mikioh
CC=golang-dev
https://golang.org/cl/5588048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/31d908baaf480db25db3d1fa2c229410fa0e76c6
元コミット内容
net: fix windows build
TBR=mikioh
CC=golang-dev
https://golang.org/cl/5588048
変更の背景
このコミットは、Go言語のnet
パッケージがWindows環境でビルドされる際に発生していた問題を修正するために行われました。特に、Windowsの非同期I/O(Overlapped I/O)の挙動に関連するものです。
Windows APIでは、ReadFile
やWriteFile
のような非同期I/O関数が呼び出された際、操作が即座に完了しない場合にsyscall.ERROR_IO_PENDING
というエラーコードを返します。これはエラーではなく、I/O操作が正常に開始され、バックグラウンドで進行中であることを示すステータスコードです。しかし、Goのnet
パッケージ内のコードがこのERROR_IO_PENDING
を適切に処理せず、本来エラーではないものをエラーとして扱ってしまう可能性がありました。
具体的には、I/O操作が即座に完了した場合(つまり、ERROR_IO_PENDING
が返されない場合)に、以前のI/O操作で設定されたエラー値がクリアされずに残ってしまうことが問題でした。これにより、後続の処理で誤ったエラーが報告され、Windows上でのネットワーク関連のビルドや実行に問題が生じていたと考えられます。
前提知識の解説
Windowsの非同期I/O (Overlapped I/O)
Windowsオペレーティングシステムでは、I/O操作を非同期に実行するためのメカニズムとして「Overlapped I/O(オーバーラップI/O)」が提供されています。これは、I/O操作の完了を待たずにアプリケーションが他の処理を続行できるようにするもので、特にネットワーク通信やディスクI/Oなど、時間がかかる可能性のある操作でシステムの応答性を高めるために重要です。
Overlapped I/Oでは、I/O操作を開始する関数(例: ReadFile
, WriteFile
)が呼び出された際に、以下のいずれかの結果を返します。
- 操作が即座に完了した場合: 関数は成功を示す値を返し、I/O操作の結果がすぐに利用可能になります。
- 操作が非同期に開始された場合: 関数は
FALSE
を返し、GetLastError()
がERROR_IO_PENDING
を返します。これは、I/O操作がバックグラウンドで開始され、完了待ちの状態であることを意味します。この場合、アプリケーションはI/O完了ポート(IOCP)などのメカニズムを使用して、操作の完了を非同期に通知されるのを待ちます。
ERROR_IO_PENDING
は、その名の通り「I/Oが保留中」という意味であり、エラーではありません。これは、非同期I/Oの正常な動作の一部です。このステータスコードをエラーとして扱うと、アプリケーションのロジックが破綻する可能性があります。
Go言語のsyscall
パッケージ
Go言語の標準ライブラリにはsyscall
パッケージが含まれており、これを通じてオペレーティングシステムが提供する低レベルのシステムコールにアクセスできます。Windowsの場合、このパッケージはWin32 APIの関数や定数(例: syscall.ERROR_IO_PENDING
)をGoのコードから呼び出すためのインターフェースを提供します。
Goのネットワークパッケージ(net
)のような低レベルのI/Oを扱う部分では、パフォーマンスやOS固有の機能を利用するために、このsyscall
パッケージを直接使用することがあります。
技術的詳細
このコミットの技術的詳細は、Windowsの非同期I/OにおけるERROR_IO_PENDING
の扱い方に集約されます。
Goのnet
パッケージは、Windows上でネットワークI/Oを処理する際に、内部的にI/O完了ポート(IOCP)を使用しています。fd_windows.go
ファイルは、ファイルディスクリプタ(ネットワークソケットなど)に対するI/O操作をWindows固有のAPIと連携させるためのロジックを含んでいます。
ExecIO
関数は、I/O操作を実行し、その結果を処理する役割を担っています。この関数内で、Windows APIの呼び出し結果を評価するswitch
文が存在します。
修正前のコードでは、syscall.ERROR_IO_PENDING
が返された場合、case syscall.ERROR_IO_PENDING:
ブロックに入りますが、このブロック内でerr
変数を明示的にnil
にリセットしていませんでした。
問題は、I/O操作が即座に完了した場合(syscall.ERROR_IO_PENDING
が返されない場合)に発生しました。この場合、default:
ブロックに到達する前に、err
変数が以前のI/O操作で設定されたエラー値(もしあれば)を保持したままになる可能性がありました。
修正は、case syscall.ERROR_IO_PENDING:
ブロックにerr = nil
という行を追加することです。これにより、I/O操作が非同期に開始され、ERROR_IO_PENDING
が返された場合、err
変数が明示的にnil
に設定されます。これは、ERROR_IO_PENDING
がエラーではないことを明確にし、後続の処理で誤ったエラーが伝播するのを防ぎます。
この変更により、I/O操作が即座に完了した場合でも、err
変数が不適切なエラー値を保持することがなくなり、Windows上でのnet
パッケージの動作が安定しました。
コアとなるコードの変更箇所
変更はsrc/pkg/net/fd_windows.go
ファイルの一箇所のみです。
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -169,6 +169,7 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
// IO completed immediately, but we need to get our completion message anyway.
case syscall.ERROR_IO_PENDING:
// IO started, and we have to wait for its completion.
+ err = nil
default:
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}
}
コアとなるコードの解説
変更が加えられたのは、ExecIO
関数内のswitch
文のcase syscall.ERROR_IO_PENDING:
ブロックです。
case syscall.ERROR_IO_PENDING:
: このケースは、Windows APIのI/O関数がERROR_IO_PENDING
を返した場合に実行されます。これは、I/O操作が非同期に開始され、完了を待つ必要があることを意味します。err = nil
: この行が追加されました。これにより、ERROR_IO_PENDING
が返された際に、err
変数が明示的にnil
(エラーなし)に設定されます。これは、ERROR_IO_PENDING
がエラー状態ではないことをコードで明確に表現し、以前のI/O操作で設定された可能性のあるエラー値をクリアする役割を果たします。
この修正により、ExecIO
関数がERROR_IO_PENDING
を受け取った際に、err
変数が常に正しい状態(エラーなし)になることが保証され、Windowsビルドにおける潜在的なバグが解消されました。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
- Go言語の
syscall
パッケージ: https://pkg.go.dev/syscall - Go言語の
net
パッケージ: https://pkg.go.dev/net - Windows API
ReadFile
関数: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile - Windows API
WriteFile
関数: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile ERROR_IO_PENDING
(Win32 Error Codes): https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- (エラーコード 997)
参考にした情報源リンク
- Web検索結果: "golang syscall.ERROR_IO_PENDING windows build fix 2012"
syscall.ERROR_IO_PENDING
に関する一般的な説明- GoにおけるWindowsビルドの問題に関する議論(特に古いGoバージョンでの問題やレースコンディションとの関連)
- 非同期I/Oの正しいハンドリング方法に関する情報
- Go言語のソースコード(
src/pkg/net/fd_windows.go
) - Go言語のコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/5588048 (コミットメッセージに記載)