[インデックス 11549] ファイルの概要
このコミットは、Go言語のosパッケージにおけるWindows環境でのファイルハンドルの扱いに関する修正です。具体的には、無効なファイルハンドルをチェックする際に、従来の-1との比較から、Windows APIの慣習に沿ったsyscall.InvalidHandle定数を使用するように変更しています。これにより、コードの正確性と可読性が向上しています。
コミット
commit 4ea5d62e5a0013b21c9d796b41a56e71b19159b6
Author: Wei Guangjing <vcc.163@gmail.com>
Date: Thu Feb 2 10:17:52 2012 +1100
os: file windows use syscall.InvalidHandle instead of -1.
R=golang-dev, adg, alex.brainman
CC=golang-dev
https://golang.org/cl/5602050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4ea5d62e5a0013b21c9d796b41a56e71b19159b6
元コミット内容
os: file windows use syscall.InvalidHandle instead of -1.
このコミットは、Go言語のosパッケージにおいて、Windowsプラットフォームでのファイル操作に関するコードを修正するものです。具体的には、ファイルディスクリプタ(fd)が無効であるかどうかのチェックを、これまでの-1との比較から、syscall.InvalidHandle定数との比較に変更しています。
変更の背景
Windows APIでは、ファイルやデバイスのハンドルが無効であることを示すために、通常INVALID_HANDLE_VALUEという特別な値が使用されます。この値は、C/C++のヘッダファイルでは(HANDLE)-1として定義されており、実質的には符号なし整数型で表現される最大値、またはポインタ型で-1をキャストした値となります。
Go言語のsyscallパッケージは、各OSのシステムコールをラップしており、Windows固有の定数も提供しています。syscall.InvalidHandleは、WindowsのINVALID_HANDLE_VALUEに対応するGoの定数です。
従来のコードでは、ファイルディスクリプタが-1であるかどうかで無効なハンドルを判断していました。これはUNIX系のシステムでは一般的な慣習ですが、WindowsではINVALID_HANDLE_VALUEを使用するのがより正確で、プラットフォームの慣習に沿った方法です。この変更は、GoのosパッケージがWindowsのネイティブなAPIのセマンティクスをより正確に反映し、コードの堅牢性と可読性を向上させることを目的としています。
前提知識の解説
ファイルディスクリプタとハンドル
- ファイルディスクリプタ (File Descriptor, FD): UNIX系OSでファイルやI/Oリソースを識別するために使われる非負の整数です。通常、
open()システムコールによって取得され、read(),write(),close()などのシステムコールで使用されます。無効なFDは通常-1で示されます。 - ハンドル (Handle): Windows OSでファイル、レジストリキー、イベント、ミューテックスなどのカーネルオブジェクトを識別するために使われる抽象的な参照です。
CreateFile()などのAPIによって取得され、ReadFile(),WriteFile(),CloseHandle()などのAPIで使用されます。無効なハンドルはINVALID_HANDLE_VALUE(通常は(HANDLE)-1)で示されます。
syscallパッケージ
Go言語の標準ライブラリの一部であるsyscallパッケージは、低レベルなオペレーティングシステムプリミティブへのアクセスを提供します。これには、システムコール、プロセス管理、ファイルシステム操作、ネットワーク操作などが含まれます。OSに依存する定数や関数もこのパッケージで定義されており、クロスプラットフォームなGoプログラムが特定のOSの機能を利用する際に用いられます。
syscall.InvalidHandle
syscall.InvalidHandleは、GoのsyscallパッケージがWindows向けに提供する定数で、Windows APIのINVALID_HANDLE_VALUEに対応します。これは、無効なハンドルを示すために使用される特別な値です。
技術的詳細
Windows APIでは、HANDLE型はポインタ型であり、INVALID_HANDLE_VALUEは通常(HANDLE)-1として定義されます。これは、ポインタが指すアドレスが0xFFFFFFFF(32ビットシステムの場合)または0xFFFFFFFFFFFFFFFF(64ビットシステムの場合)であることを意味します。Go言語では、syscall.Handle型は通常uintptr(符号なし整数ポインタ)として定義されるため、-1という負の値は、uintptrにキャストされると非常に大きな正の値になります。
例えば、32ビットシステムで-1をuintptrにキャストすると0xFFFFFFFFになります。したがって、fd < 0というチェックは、Goのsyscall.Handleがuintptrである場合、常にfalseになる可能性があります(uintptrは符号なしのため)。
このコミットでは、fd < 0という比較をfd == syscall.InvalidHandleに変更することで、以下の利点が得られます。
- 正確性:
syscall.InvalidHandleはWindowsのINVALID_HANDLE_VALUEを正確に表現しており、Windowsのセマンティクスに完全に合致します。 - 可読性: コードを読む開発者にとって、
syscall.InvalidHandleを使用することで、このチェックが「無効なハンドル」を意図していることがより明確になります。 - 堅牢性:
uintptrの符号なし特性による潜在的なバグを防ぎます。
この変更は、GoのosパッケージがWindows上でより正確かつ堅牢に動作するために重要です。
コアとなるコードの変更箇所
変更はsrc/pkg/os/file_windows.goファイル内の3箇所で行われています。
-
NewFile関数の内部:--- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -39,7 +39,7 @@ func (file *File) Fd() syscall.Handle { // NewFile returns a new File with the given file descriptor and name. func NewFile(fd syscall.Handle, name string) *File { - if fd < 0 { + if fd == syscall.InvalidHandle { return nil } f := &File{&file{fd: fd, name: name}} -
file.closeメソッドの内部:--- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -115,7 +115,7 @@ func (file *File) Close() error {\n }\n \n func (file *file) close() error {\n- if file == nil || file.fd < 0 {\n+ if file == nil || file.fd == syscall.InvalidHandle {\n return EINVAL }\n var e error -
File.readdirメソッドの内部:--- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -136,7 +136,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {\n }\n \n func (file *File) readdir(n int) (fi []FileInfo, err error) {\n- if file == nil || file.fd < 0 {\n+ if file == nil || file.fd == syscall.InvalidHandle {\n return nil, EINVAL }\n if !file.isdir() {\
コアとなるコードの解説
NewFile関数
NewFile関数は、既存のファイルディスクリプタ(Windowsではハンドル)とファイル名から新しい*os.Fileオブジェクトを作成します。
変更前: if fd < 0
変更後: if fd == syscall.InvalidHandle
この変更により、渡されたfdがWindowsの無効なハンドル値と一致する場合にのみnilを返すようになり、WindowsのAPIセマンティクスに正確に準拠します。
file.closeメソッド
file.closeメソッドは、os.Fileオブジェクトに関連付けられたファイルハンドルを閉じます。
変更前: if file == nil || file.fd < 0
変更後: if file == nil || file.fd == syscall.InvalidHandle
この変更により、ファイルオブジェクトがnilであるか、またはそのハンドルが無効な場合にEINVALエラーを返すようになります。これにより、無効なハンドルを閉じようとする不正な操作を防ぎます。
File.readdirメソッド
File.readdirメソッドは、ディレクトリの内容を読み取ります。
変更前: if file == nil || file.fd < 0
変更後: if file == nil || file.fd == syscall.InvalidHandle
この変更により、ディレクトリを表すファイルオブジェクトがnilであるか、またはそのハンドルが無効な場合にnilとEINVALエラーを返すようになります。これにより、無効なハンドルでディレクトリを読み取ろうとする操作を防ぎます。
これらの変更はすべて、Windows環境におけるファイルハンドルの無効性チェックを、より正確で慣用的なsyscall.InvalidHandleを使用するように統一するものです。
関連リンク
- Go言語の
osパッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscallパッケージドキュメント: https://pkg.go.dev/syscall - Windows API
INVALID_HANDLE_VALUE: https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-invalid_handle_value
参考にした情報源リンク
- Go CL 5602050: https://golang.org/cl/5602050
- Stack Overflow: What is INVALID_HANDLE_VALUE?: https://stackoverflow.com/questions/1000580/what-is-invalid-handle-value
- Microsoft Docs: Handles and Objects: https://learn.microsoft.com/en-us/windows/win32/sysinfo/handles-and-objects
- Go言語の
uintptr型に関する情報 (例: Go言語の型システムに関するブログ記事やドキュメント)