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

[インデックス 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では、ReadFileWriteFileのような非同期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)が呼び出された際に、以下のいずれかの結果を返します。

  1. 操作が即座に完了した場合: 関数は成功を示す値を返し、I/O操作の結果がすぐに利用可能になります。
  2. 操作が非同期に開始された場合: 関数は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ビルドにおける潜在的なバグが解消されました。

関連リンク

参考にした情報源リンク

  • 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 (コミットメッセージに記載)