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

[インデックス 10368] Go syscallパッケージのエラーハンドリング改革

コミット

  • コミットハッシュ: c017a8299fec188913726e5c0d19e669bc4a2feb
  • 作成者: Russ Cox rsc@golang.org
  • 作成日: 2011年11月13日 22:44:52 -0500
  • コミットメッセージ: "syscall: use error"

GitHub上でのコミットページへのリンク

このコミットは2011年のもので、当時はGitHubではなくGoogle Codeで管理されていました。コードレビューは https://golang.org/cl/5372080 で行われました。

元コミット内容

syscall: use error

- syscall (not os) now defines the Errno type.
- the low-level assembly functions Syscall, Syscall6, and so on
  return Errno, not uintptr
- syscall wrappers all return error, not uintptr.

R=golang-dev, mikioh.mikioh, r, alex.brainman
CC=golang-dev
https://golang.org/cl/5372080

このコミットは、syscallパッケージのエラーハンドリングを根本的に改革する重要な変更です。対象ファイルは100を超える大規模なリファクタリングで、以下の変更が含まれています:

  • doc/progs/file.go
  • src/cmd/cgo/out.go
  • src/pkg/crypto/rand/rand_windows.go
  • src/pkg/crypto/tls/root_windows.go
  • src/pkg/exp/inotify/inotify_linux.go
  • src/pkg/net/ 配下の多数のファイル
  • src/pkg/os/ 配下の多数のファイル
  • src/pkg/syscall/ 配下の多数のファイル

変更の背景

このコミットが行われた2011年は、Go言語の正式リリース(Go 1.0)に向けた重要な時期でした。Go 1.0は2012年3月にリリースされる予定であり、このコミットは言語の安定性とAPIの一貫性を向上させるための重要な改革の一部でした。

当時のGoは、システムコールの戻り値としてuintptrを使用していましたが、これはエラーハンドリングの観点から以下の問題を抱えていました:

  1. 型安全性の欠如: uintptrは汎用的な整数型であり、エラー状態を明示的に表現できない
  2. エラーの判定ロジックの複雑化: 各システムコールの戻り値を個別に解釈する必要がある
  3. 一貫性の欠如: osパッケージとsyscallパッケージでエラーハンドリングの方法が異なる

前提知識の解説

Go言語のエラーハンドリング

Go言語は、例外処理機能を持たず、エラーを値として扱う設計哲学を採用しています。これにより、エラーの発生箇所と処理箇所が明確になり、プログラムの制御フローが予測しやすくなります。

syscallパッケージの役割

syscallパッケージは、オペレーティングシステムのシステムコールへの低レベルアクセスを提供します。このパッケージは、osパッケージやnetパッケージなどの高レベルパッケージの基盤となっています。

Errno型の意味

Errno(Error Number)は、UNIXシステムにおけるエラーコードの標準的な表現方法です。各エラーコードは、特定のエラー状態を示す数値で、例えば:

  • ENOENT (2): ファイルまたはディレクトリが存在しない
  • EACCES (13): アクセスが拒否された
  • EINVAL (22): 無効な引数

技術的詳細

この変更の核心は、syscallパッケージのエラーハンドリングを以下のように改革することです:

1. Errno型の定義場所の変更

変更前: osパッケージでErrno型を定義 変更後: syscallパッケージでErrno型を定義

これにより、エラーハンドリングの責任がより適切な場所に移動しました。syscallパッケージは低レベルなシステムコールを直接扱うため、エラーコードの定義もここで行うのが自然です。

2. 低レベル関数の戻り値型の変更

変更前: Syscall(), Syscall6() などの関数がuintptrを返す 変更後: これらの関数がErrno型を返す

// 変更前
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)

// 変更後
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err Errno)

3. syscallラッパー関数の戻り値型の変更

変更前: syscallラッパー関数がuintptrを返す 変更後: syscallラッパー関数がerror インターフェースを返す

// 変更前
func Open(path string, mode int, perm uint32) (fd int, errno uintptr)

// 変更後
func Open(path string, mode int, perm uint32) (fd int, err error)

4. エラーインターフェースの実装

Errno型が error インターフェースを実装するようになりました:

type Errno uintptr

func (e Errno) Error() string {
    // エラーメッセージを返す実装
}

コアとなるコードの変更箇所

syscall_unix.go の変更

// 変更前
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)

// 変更後
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err Errno)

syscall_windows.go の変更

// 変更前
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr)

// 変更後
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2, err Errno)

各種システムコールラッパーの変更

例えば、openシステムコールのラッパー:

// 変更前
func Open(path string, mode int, perm uint32) (fd int, errno uintptr) {
    // ...
}

// 変更後
func Open(path string, mode int, perm uint32) (fd int, err error) {
    // ...
}

コアとなるコードの解説

1. Errno型の定義

type Errno uintptr

func (e Errno) Error() string {
    if 0 <= int(e) && int(e) < len(errors) {
        s := errors[e]
        if s != "" {
            return s
        }
    }
    return "errno " + itoa(int(e))
}

この実装により、Errno型はerrorインターフェースを満たし、エラーメッセージを提供できるようになりました。

2. システムコールラッパーの改善

func Open(path string, mode int, perm uint32) (fd int, err error) {
    var _p0 *byte
    _p0, err = BytePtrFromString(path)
    if err != nil {
        return
    }
    r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
    fd = int(r0)
    if e1 != 0 {
        err = e1
    }
    return
}

この変更により、エラーハンドリングがより直感的かつ一貫性のあるものになりました。

3. エラーの伝播

// osパッケージでの利用例
file, err := os.Open("filename")
if err != nil {
    // syscallパッケージから伝播したエラーを処理
    return err
}

syscallパッケージが返すエラーは、osパッケージを経由してアプリケーションレベルまで一貫した形で伝播されます。

関連リンク

参考にした情報源リンク

この変更は、Go言語のエラーハンドリング哲学を体現する重要な改革であり、現在のGoプログラムで使用されているエラーハンドリングパターンの基盤を築いた歴史的に重要なコミットです。