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

[インデックス 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.goepipecheck 関数が追加されました。
  • 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.AddInt32atomic.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関数は、ファイル操作中に発生したエラーesyscall.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関連のコードが問題を引き起こしていました。

このコミットでは、この問題を解決するために以下の変更が行われました。

  1. file_posix.goからの削除: epipecheck関数はfile_posix.goから完全に削除されました。これにより、POSIX互換システム全般に適用されるコードから、Unix固有のシグナル処理ロジックが切り離されました。

  2. file_unix.goへの移動: epipecheck関数の実体はfile_unix.goに移動されました。これにより、この関数がUnix系システム(Linux、macOSなど)でのみコンパイルされ、実行されることが保証されます。file_unix.goは、Unix固有のシステムコールや機能に特化したコードを格納するためのファイルです。

  3. 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言語のクロスプラットフォーム開発における慣用的なパターンを示しています。

  1. プラットフォーム固有の機能の分離: epipecheck関数は、syscall.EPIPEエラーとsigpipe()関数(SIGPIPEシグナル処理に関連)に強く依存しています。これらはUnix系システムに特有の概念であり、Windowsには直接対応するものがありません。このため、epipecheckfile_posix.goからfile_unix.goに移動することで、Unix固有のコードを適切なファイルに隔離しています。これにより、Unix系システム向けにビルドされる場合にのみ、このロジックがコンパイルされるようになります。

  2. 共通インターフェースのためのno-op実装: epipecheck関数がosパッケージ内の他の場所から呼び出される可能性がある場合、Windows向けにビルドする際に、この関数が存在しないとコンパイルエラーになります。これを避けるため、file_windows.goに空のepipecheck関数が追加されました。この「何もしない」実装は、Windows環境ではepipecheckが不要であることを示しつつ、関数シグネチャを維持することで、共通コードからの呼び出しを可能にします。これは、Go言語でプラットフォーム間の差異を吸収し、統一されたAPIを提供する一般的な手法です。

  3. アトミック操作の重要性: epipecheck関数内でatomic.AddInt32atomic.StoreInt32が使用されている点は重要です。file.nepipeカウンタは、複数のゴルーチンが同時にファイルに書き込みを行う可能性があるため、競合状態を避けるためにアトミック操作で更新する必要があります。これにより、カウンタの正確性が保証され、SIGPIPEシグナルをトリガーする条件(10回連続のEPIPEエラー)が正しく評価されます。

この変更は、Go言語の標準ライブラリが、多様なオペレーティングシステムの特性を考慮しつつ、いかに堅牢で移植性の高いコードベースを維持しているかを示す良い例です。

関連リンク

参考にした情報源リンク