[インデックス 13510] ファイルの概要
このコミットは、Go言語の標準ライブラリos
パッケージにおけるepipecheck
関数の配置を修正し、Windowsビルドの問題を解決することを目的としています。具体的には、epipecheck
関数がfile_posix.go
からfile_unix.go
へ移動され、Windows環境向けにはfile_windows.go
に空の(no-op)実装が追加されました。これにより、SIGPIPE
シグナル処理というUnix固有のメカニズムが、適切なプラットフォームでのみコンパイルされるようになります。
コミット
commit 9c8ae6ca34640eb835eda8afc268815c6fbde039
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Fri Jul 27 22:21:33 2012 +1000
os: move epipecheck from file_posix.go and into file_unix.go to fix windows build
R=golang-dev
CC=golang-dev
https://golang.org/cl/6450058
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9c8ae6ca34640eb835eda8afc268815c6fbde039
元コミット内容
このコミットは、os
パッケージ内のepipecheck
関数の定義場所を変更しています。
src/pkg/os/file_posix.go
からepipecheck
関数が削除されました。src/pkg/os/file_unix.go
にepipecheck
関数が追加されました。src/pkg/os/file_windows.go
に、空の(no-op)epipecheck
関数が追加されました。
これにより、epipecheck
関数はUnix系システムでのみ実体を持ち、Windowsでは何もしない関数として定義されることになります。
変更の背景
コミットメッセージに明記されている通り、この変更の主な目的は「Windowsビルドを修正する」ことです。
epipecheck
関数は、syscall.EPIPE
エラー(Broken pipe、壊れたパイプ)を検出し、特定の条件下でsigpipe()
関数を呼び出すロジックを含んでいます。SIGPIPE
シグナルは、Unix系オペレーティングシステムにおいて、書き込み先のパイプやソケットが読み込み側によって閉じられた場合に発生するシグナルです。このシグナルは、デフォルトではプロセスを終了させる挙動を持ちます。
Windowsオペレーティングシステムでは、Unix系システムのようなSIGPIPE
シグナルによるエラー処理メカニズムは存在しません。代わりに、書き込み操作が失敗した場合に直接エラーコード(例えばWSAECONNRESET
など)を返します。
epipecheck
関数がfile_posix.go
に存在していた場合、GoのビルドシステムがWindows向けにコンパイルする際に、SIGPIPE
シグナルや関連するUnix固有の機能への参照が問題となり、ビルドエラーが発生していたと考えられます。file_posix.go
はPOSIX互換システム(Unix、Linux、macOSなど)向けの共通コードを含むことが多いですが、Windowsは完全なPOSIX互換ではないため、このような問題が発生することがあります。
この問題を解決するため、epipecheck
関数を真にUnix固有の機能としてfile_unix.go
に移動し、Windowsビルドがこの関数に依存しないように、file_windows.go
に空のスタブ(何もしない関数)を提供することで、クロスプラットフォームでのコンパイルエラーを回避しています。
前提知識の解説
1. syscall.EPIPE
(Broken pipe)
EPIPE
は、Unix系システムにおけるエラーコードの一つで、「Broken pipe」(壊れたパイプ)を意味します。これは、プロセスがパイプやソケットにデータを書き込もうとした際に、そのパイプやソケットの読み込み側がすでに閉じられている場合に発生します。例えば、cat file | head -n 1
のようなコマンドで、cat
が大量のデータを生成し続けている間にhead
が最初の1行を読み取って終了すると、cat
は閉じられたパイプに書き込もうとしてEPIPE
エラーを受け取ります。
2. SIGPIPE
シグナル
Unix系システムでは、EPIPE
エラーが発生すると、カーネルは書き込み側のプロセスにSIGPIPE
シグナルを送信します。このシグナルのデフォルトの動作は、プロセスを即座に終了させることです。多くのアプリケーションでは、このデフォルト動作を回避するためにSIGPIPE
シグナルを無視するか、カスタムのシグナルハンドラを設定して、エラーを適切に処理します。Go言語のランタイムも、このようなシグナルを適切に処理するための内部メカニズムを持っています。
3. sync/atomic
パッケージ
sync/atomic
パッケージは、Go言語においてアトミック操作(不可分操作)を提供します。アトミック操作とは、複数のゴルーチン(軽量スレッド)が同時に同じメモリ位置にアクセスしても、データ競合が発生しないように保証された操作のことです。
このコミットでは、atomic.AddInt32
とatomic.StoreInt32
が使用されています。
atomic.AddInt32(&file.nepipe, 1)
:file.nepipe
というint32
型の変数に1をアトミックに加算します。atomic.StoreInt32(&file.nepipe, 0)
:file.nepipe
というint32
型の変数に0をアトミックに格納します。 これにより、複数の並行処理がfile.nepipe
を更新しようとしても、値の整合性が保たれます。
4. Go言語のプラットフォーム固有ファイル
Go言語では、異なるオペレーティングシステムやアーキテクチャに対応するために、ファイル名にサフィックスを付けることでプラットフォーム固有のコードを記述する慣習があります。
_posix.go
: POSIX互換システム(Unix、Linux、macOSなど)向けの共通コード。_unix.go
: Unix系システム(Linux、macOSなど)向けのコード。_windows.go
: Windowsシステム向けのコード。 ビルド時には、ターゲットとするプラットフォームに応じて適切なファイルが選択され、コンパイルされます。例えば、Windows向けにビルドする場合、_windows.go
ファイルがコンパイルに含まれ、_unix.go
や_posix.go
の一部は含まれないか、異なる方法で処理されます。
技術的詳細
このコミットの中心は、epipecheck
関数のプラットフォーム依存性の適切な管理です。
epipecheck
関数は、ファイル操作中に発生したエラーe
がsyscall.EPIPE
であるかどうかをチェックします。
- もしエラーが
syscall.EPIPE
であれば、file.nepipe
というカウンタをアトミックにインクリメントします。 - このカウンタが10以上になった場合、
sigpipe()
関数を呼び出します。sigpipe()
はGoランタイム内部で定義されており、おそらくSIGPIPE
シグナルを処理するためのメカニズムをトリガーします。これは、連続してEPIPE
エラーが発生した場合に、プロセスが適切に反応できるようにするためのものです。 - エラーが
syscall.EPIPE
でなければ、カウンタfile.nepipe
を0にリセットします。これは、EPIPE
エラーが連続しているかどうかを判断するためのロジックです。
元のコードでは、このepipecheck
関数がfile_posix.go
に定義されていました。しかし、WindowsはPOSIXシステムとは異なるシグナル処理メカニズムを持つため、Windows環境でfile_posix.go
がコンパイルされる際に、SIGPIPE
関連のコードが問題を引き起こしていました。
このコミットでは、この問題を解決するために以下の変更が行われました。
-
file_posix.go
からの削除:epipecheck
関数はfile_posix.go
から完全に削除されました。これにより、POSIX互換システム全般に適用されるコードから、Unix固有のシグナル処理ロジックが切り離されました。 -
file_unix.go
への移動:epipecheck
関数の実体はfile_unix.go
に移動されました。これにより、この関数がUnix系システム(Linux、macOSなど)でのみコンパイルされ、実行されることが保証されます。file_unix.go
は、Unix固有のシステムコールや機能に特化したコードを格納するためのファイルです。 -
file_windows.go
へのno-op実装の追加: Windows環境ではepipecheck
関数が不要であるため、file_windows.go
に以下のような空の(no-op)実装が追加されました。func epipecheck(file *File, e error) { }
このno-op実装は、他の共通コードが
epipecheck
を呼び出す可能性がある場合に、Windowsビルドでコンパイルエラーが発生しないようにするためのものです。WindowsではSIGPIPE
シグナルが存在しないため、この関数が呼び出されても何もする必要がありません。
この変更により、Goのビルドシステムは、ターゲットOSに応じて適切なepipecheck
の実装を選択するようになり、Windows環境でのビルドエラーが解消されました。これは、Go言語がクロスプラットフォーム開発を強力にサポートするための典型的なアプローチの一つです。
コアとなるコードの変更箇所
src/pkg/os/file_posix.go
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -7,23 +7,12 @@
package os
import (
-"sync/atomic"
"syscall"
"time"
)
func sigpipe() // implemented in package runtime
-func epipecheck(file *File, e error) {
- if e == syscall.EPIPE {
- if atomic.AddInt32(&file.nepipe, 1) >= 10 {
- sigpipe()
- }
- } else {
- atomic.StoreInt32(&file.nepipe, 0)
- }
-}
-
// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
epipecheck
関数とその関連するimport "sync/atomic"
が削除されました。
src/pkg/os/file_unix.go
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -8,6 +8,7 @@ package os
import (
"runtime"
+ "sync/atomic"
"syscall"
)
@@ -53,6 +54,16 @@ type dirInfo struct {
bufp int // location of next record in buf.
}
+func epipecheck(file *File, e error) {
+ if e == syscall.EPIPE {
+ if atomic.AddInt32(&file.nepipe, 1) >= 10 {
+ sigpipe()
+ }
+ } else {
+ atomic.StoreInt32(&file.nepipe, 0)
+ }
+}
+
// DevNull is the name of the operating system's ``null device.''
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"
epipecheck
関数が追加されました。epipecheck
関数内で使用されるsync/atomic
パッケージがインポートされました。
src/pkg/os/file_windows.go
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -54,6 +54,9 @@ type dirInfo struct {
path string
}
+func epipecheck(file *File, e error) {
+}
+
const DevNull = "NUL"
func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
- 空の(no-op)
epipecheck
関数が追加されました。
コアとなるコードの解説
このコミットの核心は、Go言語のクロスプラットフォーム開発における慣用的なパターンを示しています。
-
プラットフォーム固有の機能の分離:
epipecheck
関数は、syscall.EPIPE
エラーとsigpipe()
関数(SIGPIPE
シグナル処理に関連)に強く依存しています。これらはUnix系システムに特有の概念であり、Windowsには直接対応するものがありません。このため、epipecheck
をfile_posix.go
からfile_unix.go
に移動することで、Unix固有のコードを適切なファイルに隔離しています。これにより、Unix系システム向けにビルドされる場合にのみ、このロジックがコンパイルされるようになります。 -
共通インターフェースのためのno-op実装:
epipecheck
関数がos
パッケージ内の他の場所から呼び出される可能性がある場合、Windows向けにビルドする際に、この関数が存在しないとコンパイルエラーになります。これを避けるため、file_windows.go
に空のepipecheck
関数が追加されました。この「何もしない」実装は、Windows環境ではepipecheck
が不要であることを示しつつ、関数シグネチャを維持することで、共通コードからの呼び出しを可能にします。これは、Go言語でプラットフォーム間の差異を吸収し、統一されたAPIを提供する一般的な手法です。 -
アトミック操作の重要性:
epipecheck
関数内でatomic.AddInt32
とatomic.StoreInt32
が使用されている点は重要です。file.nepipe
カウンタは、複数のゴルーチンが同時にファイルに書き込みを行う可能性があるため、競合状態を避けるためにアトミック操作で更新する必要があります。これにより、カウンタの正確性が保証され、SIGPIPE
シグナルをトリガーする条件(10回連続のEPIPE
エラー)が正しく評価されます。
この変更は、Go言語の標準ライブラリが、多様なオペレーティングシステムの特性を考慮しつつ、いかに堅牢で移植性の高いコードベースを維持しているかを示す良い例です。
関連リンク
- Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Go言語の
sync/atomic
パッケージドキュメント: https://pkg.go.dev/sync/atomic - Go言語のクロスコンパイルに関する情報: https://go.dev/doc/install/source#environment
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go Code Review Comments (Platform-specific code): https://go.dev/doc/effective_go#platform-specific_code
- POSIX
SIGPIPE
signal: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_04_02 - Windowsにおけるパイプのエラー処理 (Microsoft Learn): https://learn.microsoft.com/en-us/windows/win32/ipc/pipes (一般的な情報として)