[インデックス 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言語の型システムに関するブログ記事やドキュメント)