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

[インデックス 1512] ファイルの概要

このコミットは、Go言語の標準ライブラリにおける複数のファイル、特にsrc/lib/netsrc/lib/sync内のコードに対して、命名規則の統一("casify")と、一部の内部ヘルパー関数のリネームを行っています。主な目的は、Go言語の慣習に従い、外部に公開されるべき識別子(エクスポートされる識別子)と、パッケージ内部でのみ使用されるべき識別子(アンエクスポートされる識別子)の区別を明確にすることです。

コミット

commit ec9f2b0cd46300b9da3a82aa3604c98fce017baa
Author: Rob Pike <r@golang.org>
Date:   Fri Jan 16 14:16:31 2009 -0800

    casify linux syscall dependents, plus a few stragglers
    
    R=rsc
    DELTA=97  (0 added, 0 deleted, 97 changed)
    OCL=22971
    CL=22973

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

https://github.com/golang/go/commit/ec9f2b0cd46300b9da3a82aa3604c98fce017baa

元コミット内容

このコミットの元のメッセージは「casify linux syscall dependents, plus a few stragglers」です。これは、Linuxシステムコールに関連する依存関係の識別子をGoの命名規則に合わせて大文字・小文字を修正("casify")し、それに加えていくつかの残っていた("stragglers")命名規則に合わない識別子も修正したことを意味します。

変更の背景

Go言語には厳格な命名規則があります。特に、識別子(変数名、関数名、型名など)がパッケージ外からアクセス可能(エクスポートされる)であるか、パッケージ内部でのみ使用される(アンエクスポートされる)かは、その識別子の最初の文字が大文字か小文字かによって決まります。

  • 大文字で始まる識別子: エクスポートされ、他のパッケージからアクセス可能です。
  • 小文字で始まる識別子: アンエクスポートされ、その識別子が定義されているパッケージ内でのみアクセス可能です。

このコミットが行われた2009年1月は、Go言語がまだ初期開発段階にあり、言語仕様や標準ライブラリの設計が固まりつつある時期でした。この時期には、このような命名規則の統一やAPIの整理が頻繁に行われていました。このコミットは、特にLinuxシステムコールをラップするGoのsyscallパッケージや、ネットワーク関連のコード、テストコードにおいて、この命名規則が徹底されていなかった部分を修正し、コードベース全体の整合性を高めることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念とLinuxのシステムコールに関する知識が必要です。

  1. Go言語の命名規則(Exported vs. Unexported Identifiers): 前述の通り、Goでは識別子の最初の文字が大文字か小文字かで、その可視性(スコープ)が決定されます。これは、API設計において非常に重要であり、どの要素が公開APIの一部であり、どの要素が内部実装の詳細であるかを明確にするためのGoの設計思想の一部です。

  2. Linuxシステムコールとsyscallパッケージ: Linuxシステムコールは、ユーザー空間のプログラムがカーネルの機能(ファイルI/O、ネットワーク通信、プロセス管理など)にアクセスするためのインターフェースです。Go言語では、syscallパッケージがこれらのシステムコールへの低レベルなアクセスを提供します。このパッケージ内の関数や構造体は、通常、OSのAPIを直接ラップするため、Goの命名規則に従ってエクスポートされる必要があります。

  3. epoll: epollはLinuxカーネルが提供するI/Oイベント通知メカニズムです。多数のファイルディスクリプタ(FD)からのI/Oイベントを効率的に監視するために使用されます。epoll_create, epoll_ctl, epoll_waitなどの関数が主要なAPIです。これらはGoのsyscallパッケージを通じて利用されます。

  4. テストコードの慣習: Goのテストファイル(_test.goで終わるファイル)では、テストヘルパー関数やテスト固有の構造体は、通常、パッケージ外部から参照される必要がないため、アンエクスポートされた(小文字で始まる)名前が付けられます。これにより、テストコードが本番コードのAPIを汚染するのを防ぎ、テストの内部実装の詳細を隠蔽します。

技術的詳細

このコミットの技術的詳細は、主にGo言語の命名規則の適用と、それに伴うコードの修正に集約されます。

  • syscallパッケージのラッパー関数/構造体フィールドのエクスポート: src/lib/net/fd_linux.goでは、syscall.epoll_createsyscall.epoll_ctlsyscall.epoll_waitsyscall.closeといったLinuxシステムコールを直接呼び出す部分が、syscall.Epoll_createsyscall.Epoll_ctlsyscall.Epoll_waitsyscall.Closeのように、最初の文字が大文字に変更されています。これは、これらの関数がsyscallパッケージのエクスポートされたAPIとして提供されるべきであることを示しています。同様に、syscall.EpollEvent構造体のフィールドであるfdeventsFdEventsに修正され、エクスポートされるフィールドとして扱われています。

  • パッケージ内部定数のアンエクスポート: src/lib/net/fd_linux.goでは、ReadWriteという定数がreadFlagswriteFlagsにリネームされています。これらはepollイベントフラグを表す定数ですが、パッケージ内部でのみ使用されるため、アンエクスポートされた名前に変更されています。

  • テストヘルパー関数/型のアンエクスポート: src/lib/net/ip_test.gosrc/lib/net/port_test.gosrc/lib/net/tcpserver_test.gosrc/lib/sync/mutex_test.goといったテストファイルでは、テストの実行を補助するための関数(例: IPv4 -> _IPv4, Equal -> isEqual, Echo -> runEcho, Serve -> runServe, Connect -> connect, DoTest -> doTest, HammerSemaphore -> hammerSemaphore, HammerMutex -> hammerMutex)や型(例: ParseIPTest -> parseIPTest, PortTest -> portTest)が、すべて小文字で始まる名前に変更されています。これにより、これらの識別子がテストファイル内部でのみ有効なヘルパーであり、外部に公開されるAPIではないことが明確になります。

  • 構造体フィールドの命名規則統一: src/lib/net/net_linux.goでは、syscall.SockaddrInet4syscall.SockaddrInet6といったソケットアドレス構造体のフィールド(family, port, addr)が、Family, Port, Addrのように大文字で始まる名前に変更されています。これは、これらのフィールドが構造体の公開された一部であり、外部からアクセス可能であることを示しています。

これらの変更は、Go言語のコードベース全体で一貫した命名規則を適用し、APIの意図を明確にするための重要なステップです。特に、低レベルなシステムコールを扱うsyscallパッケージにおいては、どの関数やフィールドがOSのAPIを直接ラップしているのか、そしてそれがGoの公開APIとして提供されるべきなのかを明確にすることが重要です。

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

このコミットは、特定の機能追加やバグ修正ではなく、コードベース全体の命名規則の統一が主目的であるため、特定の「コアとなるコード」というよりは、広範囲にわたる識別子のリネームが変更の中心です。

主な変更箇所は以下のファイルに分散しています。

  • src/lib/net/fd_linux.go: epoll関連のシステムコールラッパー関数とEpollEvent構造体フィールドの命名変更、および内部定数のリネーム。
  • src/lib/net/ip_test.go: IPアドレス解析テストのヘルパー関数と型の命名変更。
  • src/lib/net/net_linux.go: ソケットアドレス構造体フィールドの命名変更、および内部変数のリネーム。
  • src/lib/net/port_test.go: ポートルックアップテストのヘルパー型の命名変更。
  • src/lib/net/tcpserver_test.go: TCPサーバーテストのヘルパー関数の命名変更。
  • src/lib/sync/mutex_test.go: ミューテックスおよびセマフォテストのヘルパー関数の命名変更。

コアとなるコードの解説

変更は主に命名規則の適用であり、ロジックの変更は伴いません。

例えば、src/lib/net/fd_linux.goにおける変更は、syscall.epoll_createsyscall.Epoll_createになることで、Goのsyscallパッケージ内でこの関数がエクスポートされ、他のパッケージから利用可能になることを示しています。これは、Goの標準ライブラリがOSの低レベルAPIをどのように抽象化し、公開しているかを示す典型的な例です。

--- a/src/lib/net/fd_linux.go
+++ b/src/lib/net/fd_linux.go
@@ -13,8 +13,8 @@ import (
 )
 
 const (
-	Read = syscall.EPOLLIN | syscall.EPOLLRDHUP;
-	Write = syscall.EPOLLOUT
+	readFlags = syscall.EPOLLIN | syscall.EPOLLRDHUP;
+	writeFlags = syscall.EPOLLOUT
 )
 
 export type Pollster struct {
@@ -31,7 +31,7 @@ export func NewPollster() (p *Pollster, err *os.Error) {
 	// The arg to epoll_create is a hint to the kernel
 	// about the number of FDs we will care about.
 	// We don't know.
-	if p.epfd, e = syscall.epoll_create(16); e != 0 {
+	if p.epfd, e = syscall.Epoll_create(16); e != 0 {
 		return nil, os.ErrnoToError(e)
 	}
 	p.events = make(map[int64] uint32);
@@ -41,15 +41,15 @@ export func (p *Pollster) AddFD(fd int64, mode int, repeat bool) *os.Error {
 	var ev syscall.EpollEvent;
 	var already bool;
-	ev.fd = int32(fd);
-	ev.events, already = p.events[fd];
+	ev.Fd = int32(fd);
+	ev.Events, already = p.events[fd];
 	if !repeat {
-		ev.events |= syscall.EPOLLONESHOT
+		ev.Events |= syscall.EPOLLONESHOT
 	}
 	if mode == 'r' {
-		ev.events |= Read
+		ev.Events |= readFlags
 	} else {
-		ev.events |= Write
+		ev.Events |= writeFlags
 	}
 
 	var op int64;
@@ -58,10 +58,10 @@ func (p *Pollster) AddFD(fd int64, mode int, repeat bool) *os.Error {
 	} else {
 		op = syscall.EPOLL_CTL_ADD
 	}
-	if e := syscall.epoll_ctl(p.epfd, op, fd, &ev); e != 0 {
+	if e := syscall.Epoll_ctl(p.epfd, op, fd, &ev); e != 0 {
 		return os.ErrnoToError(e)
 	}
-	p.events[fd] = ev.events;
+	p.events[fd] = ev.Events;
 	return nil
 }
 
@@ -84,14 +84,14 @@ func (p *Pollster) StopWaiting(fd int64, bits uint) {
 	events &= ^uint32(bits);
 	if int32(events) & ^syscall.EPOLLONESHOT != 0 {
 		var ev syscall.EpollEvent;
-		ev.fd = int32(fd);
-		ev.events = events;
-		if e := syscall.epoll_ctl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 {
+		ev.Fd = int32(fd);
+		ev.Events = events;
+		if e := syscall.Epoll_ctl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 {
 			print("Epoll modify fd=", fd, ": ", os.ErrnoToError(e).String(), "\n")
 		}
 		p.events[fd] = events
 	} else {
-		if e := syscall.epoll_ctl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
+		if e := syscall.Epoll_ctl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
 			print("Epoll delete fd=", fd, ": ", os.ErrnoToError(e).String(), "\n")
 		}
 		p.events[fd] = 0, false
@@ -102,35 +102,35 @@ func (p *Pollster) WaitFD() (fd int64, mode int, err *os.Error) {
 	// Get an event.
 	var evarray [1]syscall.EpollEvent;
 	ev := &evarray[0];
-	n, e := syscall.epoll_wait(p.epfd, evarray, -1);
+	n, e := syscall.Epoll_wait(p.epfd, evarray, -1);
 	for e == syscall.EAGAIN || e == syscall.EINTR {
-		n, e = syscall.epoll_wait(p.epfd, evarray, -1)
+		n, e = syscall.Epoll_wait(p.epfd, evarray, -1)
 	}
 	if e != 0 {
 		return -1, 0, os.ErrnoToError(e)
 	}
-	fd = int64(ev.fd);
+	fd = int64(ev.Fd);
 
-	if ev.events & Write != 0 {
-		p.StopWaiting(fd, Write);
+	if ev.Events & writeFlags != 0 {
+		p.StopWaiting(fd, writeFlags);
 		return fd, 'w', nil
 	}
-	if ev.events & Read != 0 {
-		p.StopWaiting(fd, Read);
+	if ev.Events & readFlags != 0 {
+		p.StopWaiting(fd, readFlags);
 		return fd, 'r', nil
 	}
 
 	// Other events are error conditions - wake whoever is waiting.
 	events, already := p.events[fd];
-	if events & Write != 0 {
-		p.StopWaiting(fd, Write);
+	if events & writeFlags != 0 {
+		p.StopWaiting(fd, writeFlags);
 		return fd, 'w', nil
 	}
-	p.StopWaiting(fd, Read);
+	p.StopWaiting(fd, readFlags);
 	return fd, 'r', nil
 }
 
 func (p *Pollster) Close() *os.Error {
-	r, e := syscall.close(p.epfd);\n
+	r, e := syscall.Close(p.epfd);\n
 	return os.ErrnoToError(e)
 }

この差分は、syscallパッケージ内のepoll関連の関数呼び出しが、Goの命名規則に従って大文字で始まるように変更されたことを示しています。また、EpollEvent構造体のフィールドも同様に大文字化されています。これにより、これらの要素がパッケージ外部に公開されるAPIの一部であることが明確になります。同時に、ReadWriteという定数がreadFlagswriteFlagsにリネームされ、これらがパッケージ内部でのみ使用されることを示しています。

テストファイルにおける変更も同様に、テストヘルパー関数や型がアンエクスポートされた名前に変更され、テストコードの内部実装の詳細が隠蔽されています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Linux man ページ
  • Go言語のソースコードリポジトリ (GitHub)
  • Go言語の初期開発に関する議論やメーリングリストのアーカイブ (Go言語の歴史的背景を理解するため)