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

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

このコミットは、Go言語のruntimeパッケージのAPIを整理し、特定の低レベル関数を公開APIから削除することを目的としています。具体的には、メモリ割り当て/解放に関連するAllocFreeLookup関数と、セマフォ操作に関連するSemacquireSemrelease関数が対象です。これらの関数は、Goの内部実装やテストでのみ使用されるべきものであり、外部のユーザーが直接利用することを意図していませんでした。この変更により、runtimeパッケージの公開APIがよりクリーンになり、Goの同期プリミティブ(syncパッケージ)がこれらの内部関数をより適切に利用するようになります。

コミット

commit 03f2289f7e3b419df36cdf97f4c49911c56b7b66
Author: Russ Cox <rsc@golang.org>
Date:   Sun Feb 19 00:11:44 2012 -0500

    runtime: API
    
    Delete Alloc, Free, Lookup, Semacquire, Semrelease
    
    Fixes #2955.
    
    R=golang-dev, r, bradfitz
    CC=golang-dev
    https://golang.org/cl/5675093

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

https://github.com/golang/go/commit/03f2289f7e3b419df36cdf97f4c49911c56b7b66

元コミット内容

このコミットの元の内容は、Goのruntimeパッケージから以下の5つの関数を削除することです。

  • Alloc(uintptr) *byte
  • Free(*byte)
  • Lookup(*byte) (*byte, uintptr)
  • Semacquire(s *uint32)
  • Semrelease(s *uint32)

これらの関数は、それぞれメモリ管理とセマフォ操作のための低レベルなプリミティブでした。コミットメッセージには「FOR TESTING AND DEBUGGING ONLY.」や「It is intended as a simple sleep primitive for use by the synchronization library and should not be used directly.」といったコメントが付されており、これらが公開APIとして提供されるべきではないという意図が示されています。

変更の背景

この変更の背景には、Go言語のruntimeパッケージのAPI設計の洗練があります。Goの開発チームは、ユーザーが直接操作すべきではない低レベルな内部関数を公開APIから削除し、より高レベルで安全な抽象化を提供することを目指しています。

具体的には、以下の点が背景として考えられます。

  1. APIのクリーンアップ: runtimeパッケージはGoランタイムのコア機能を提供しますが、その中にはデバッグや内部実装のためにのみ必要な関数も含まれていました。これらの関数を公開APIから削除することで、ユーザーが誤って低レベルな実装詳細に依存することを防ぎ、APIの意図を明確にします。
  2. カプセル化の強化: Alloc, Free, Lookupはメモリ管理の、Semacquire, Semreleaseはセマフォの低レベルな操作を提供していました。これらを内部化することで、Goのメモリ管理や並行処理の内部実装が変更されても、外部のコードに影響を与えにくくなります。
  3. syncパッケージとの連携: syncパッケージはGoの並行処理の基本的なプリミティブ(Mutex, Cond, WaitGroupなど)を提供します。これらのプリミティブは内部的にセマフォを利用しており、以前はruntime.Semacquireruntime.Semreleaseを直接呼び出していました。このコミットでは、これらの関数をruntime_Semacquireruntime_Semreleaseという内部関数にリネームし、syncパッケージが引き続きこれらを利用できるようにしつつ、外部からは見えないようにしています。
  4. Issue #2955の解決: コミットメッセージに「Fixes #2955」とあるように、この変更は特定の課題を解決するものです。GoのIssueトラッカーにおける#2955は、GoランタイムにおけるTypeとその実装の削除、そしてreflectパッケージの使用を推奨する変更に関連していることが示唆されています。これは、runtimeパッケージのAPIをより洗練されたものにするという、より広範な取り組みの一部であると考えられます。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念と背景知識が必要です。

  1. Goランタイム (runtimeパッケージ): Goプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。runtimeパッケージは、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理、システムコールなど、Goプログラムの実行に必要な低レベルな機能を提供します。通常、開発者がこのパッケージの関数を直接呼び出すことは稀で、Goコンパイラや標準ライブラリが内部的に利用します。

  2. ゴルーチンと並行処理: Goはゴルーチンと呼ばれる軽量な並行処理の仕組みを提供します。ゴルーチンはOSのスレッドよりもはるかに軽量で、数百万個を同時に実行することも可能です。ゴルーチン間の同期には、チャネルやsyncパッケージのプリミティブが使用されます。

  3. セマフォ: セマフォは、並行プログラミングにおいて共有リソースへのアクセスを制御するための同期メカニズムです。セマフォはカウンタを持ち、acquire(取得)操作でカウンタを減らし、release(解放)操作でカウンタを増やします。カウンタが0の場合、acquire操作はブロックされます。Goのsyncパッケージの多くの同期プリミティブ(例: sync.Mutex, sync.WaitGroup, sync.Cond)は、内部的にセマフォのような低レベルなメカニズムを利用してゴルーチンの待機と通知を実現しています。

  4. 公開APIと内部API: ソフトウェアライブラリやフレームワークでは、外部のユーザーが利用できる「公開API」と、ライブラリ内部でのみ使用される「内部API」を区別することが一般的です。公開APIは安定性が求められ、互換性を維持しながら進化しますが、内部APIはライブラリの実装詳細であり、予告なく変更される可能性があります。Goでは、パッケージ名が小文字で始まる関数や変数、あるいは特定のパッケージ内でのみ使用されることを意図した関数は、慣習的に内部APIとみなされます。

  5. go testとビルドタグ (+build ignore): Goのテストは通常、go testコマンドで実行されます。ソースファイルに+build ignoreというビルドタグが記述されている場合、そのファイルは通常のビルドプロセスやテストプロセスから除外されます。これは、特定のファイルがGoのツールチェーンによって直接実行されることを意図していない場合(例: 内部的なテストヘルパー、コード生成スクリプトなど)に利用されます。

技術的詳細

このコミットの技術的詳細は、主にruntimeパッケージとsyncパッケージ間の依存関係の変更、および内部関数の命名規則の統一にあります。

  1. runtimeパッケージからの公開API削除:

    • src/pkg/runtime/debug.goからAlloc, Free, Lookupが削除されました。これらの関数は、Goのメモリ管理の低レベルな側面を公開していましたが、デバッグやテスト目的以外での使用は推奨されていませんでした。
    • src/pkg/runtime/extern.goからSemacquire, Semreleaseが削除されました。これらはセマフォの低レベルな操作を提供し、syncパッケージが内部的に利用していましたが、直接の利用は非推奨でした。
    • src/pkg/runtime/malloc.gocでは、C言語で実装されていたAlloc, Free, Lookupの定義が削除されました。
  2. syncパッケージへのセマフォ関数の内部化と移動:

    • src/pkg/runtime/sema.goc内のSemacquireSemrelease関数が、それぞれruntime_Semacquireruntime_Semreleaseにリネームされました。
    • さらに重要なのは、このファイルがpackage runtimeからpackage syncに変更されたことです。これにより、セマフォの低レベルな実装がsyncパッケージの内部に移動し、runtimeパッケージの公開APIからは見えなくなりました。ただし、実際のセマフォのプリミティブは引き続きGoランタイム(C言語部分)で実装されており、runtime_Semacquireruntime_SemreleaseはそれらへのGo側のラッパーとして機能します。
  3. syncパッケージの変更:

    • src/pkg/sync/cond.go, src/pkg/sync/mutex.go, src/pkg/sync/rwmutex.go, src/pkg/sync/waitgroup.goといったsyncパッケージ内のファイルから、"runtime"のインポートが削除されました。
    • これらのファイル内のruntime.Semacquireおよびruntime.Semreleaseへの呼び出しが、新しく内部化されたruntime_Semacquireおよびruntime_Semreleaseへの呼び出しに置き換えられました。これにより、syncパッケージは引き続きランタイムのセマフォ機能を利用できますが、その依存関係はより明確に内部的なものとして扱われます。
  4. テストファイルの移動と変更:

    • test/malloc*.goのようなテストファイルがsrc/pkg/runtime/malloc*.goに移動し、+build ignoreタグが追加されました。これは、これらのテストがGoの標準テストスイートの一部ではなく、ランタイムの内部テストとして扱われることを意味します。
    • src/pkg/runtime/sema_test.gosrc/pkg/sync/runtime_sema_test.goにリネームされ、パッケージもruntime_testからsync_testに変更されました。これにより、セマフォ関連のテストがsyncパッケージのテストとして適切に配置されました。また、syncパッケージからエクスポートされたテスト用のセマフォ関数(Runtime_Semacquire, Runtime_Semrelease)を使用するように変更されています。
  5. src/pkg/sync/runtime.goの導入:

    • 新しくsrc/pkg/sync/runtime.goファイルが追加されました。このファイルは、syncパッケージ内でruntime_Semacquireruntime_Semreleaseという関数を宣言しています。これらの宣言は、実際の関数がruntimeパッケージのC言語部分で定義されていることをGoコンパイラに伝えます。これにより、syncパッケージはこれらの内部関数を型安全に呼び出すことができます。
  6. src/pkg/sync/export_test.goの導入:

    • 新しくsrc/pkg/sync/export_test.goファイルが追加されました。このファイルは、テスト目的でruntime_Semacquireruntime_SemreleaseRuntime_SemacquireRuntime_Semreleaseとしてエクスポートしています。これにより、syncパッケージのテストコードが内部のセマフォ関数にアクセスできるようになります。

これらの変更は、Goのランタイムと標準ライブラリの間の境界をより明確にし、内部実装の詳細をカプセル化することで、将来的な変更に対する堅牢性を高めるものです。

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

このコミットにおけるコアとなるコードの変更箇所は以下の通りです。

  1. src/pkg/runtime/debug.go:

    --- a/src/pkg/runtime/debug.go
    +++ b/src/pkg/runtime/debug.go
    @@ -32,18 +32,6 @@ func NumCgoCall() int64
     // NumGoroutine returns the number of goroutines that currently exist.
     func NumGoroutine() int32
     
    -// Alloc allocates a block of the given size.
    -// FOR TESTING AND DEBUGGING ONLY.
    -func Alloc(uintptr) *byte
    -// Free frees the block starting at the given pointer.
    -// FOR TESTING AND DEBUGGING ONLY.
    -func Free(*byte)
    -// Lookup returns the base and size of the block containing the given pointer.
    -// FOR TESTING AND DEBUGGING ONLY.
    -func Lookup(*byte) (*byte, uintptr)
    -
     // MemProfileRate controls the fraction of memory allocations
     // that are recorded and reported in the memory profile.
     // The profiler aims to sample an average of
    

    Alloc, Free, Lookup関数の定義が削除されています。

  2. src/pkg/runtime/extern.go:

    --- a/src/pkg/runtime/extern.go
    +++ b/src/pkg/runtime/extern.go
    @@ -68,17 +68,6 @@ func funcline_go(*Func, uintptr) (string, int)
     // mid returns the current os thread (m) id.
     func mid() uint32
     
    -// Semacquire waits until *s > 0 and then atomically decrements it.
    -// It is intended as a simple sleep primitive for use by the synchronization
    -// library and should not be used directly.
    -func Semacquire(s *uint32)
    -// Semrelease atomically increments *s and notifies a waiting goroutine
    -// if one is blocked in Semacquire.
    -// It is intended as a simple wakeup primitive for use by the synchronization
    -// library and should not be used directly.
    -func Semrelease(s *uint32)
    -
     // SetFinalizer sets the finalizer associated with x to f.
     // When the garbage collector finds an unreachable block
     // with an associated finalizer, it clears the association and runs
    

    Semacquire, Semrelease関数の定義が削除されています。

  3. src/pkg/runtime/sema.goc:

    --- a/src/pkg/runtime/sema.goc
    +++ b/src/pkg/runtime/sema.goc
    @@ -17,7 +17,7 @@
     // See Mullender and Cox, ``Semaphores in Plan 9,''\n // http://swtch.com/semaphore.pdf
     
    -package runtime
    +package sync
     #include "runtime.h"
     #include "arch_GOARCH.h"
     
    @@ -169,10 +169,10 @@ runtime·semrelease(uint32 volatile *addr)
     	runtime·ready(s->g);
     }
     
    -func Semacquire(addr *uint32) {
    +func runtime_Semacquire(addr *uint32) {
     	runtime·semacquire(addr);
     }
     
    -func Semrelease(addr *uint32) {
    +func runtime_Semrelease(addr *uint32) {
     	runtime·semrelease(addr);
     }
    

    パッケージがruntimeからsyncに変更され、SemacquireSemreleaseruntime_Semacquireruntime_Semreleaseにリネームされています。

  4. src/pkg/sync/runtime.go (新規ファイル):

    --- /dev/null
    +++ b/src/pkg/sync/runtime.go
    @@ -0,0 +1,18 @@
    +// Copyright 2012 The Go Authors.  All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file.
    +
    +package sync
    +
    +// defined in package runtime
    +
    +// Semacquire waits until *s > 0 and then atomically decrements it.
    +// It is intended as a simple sleep primitive for use by the synchronization
    +// library and should not be used directly.
    +func runtime_Semacquire(s *uint32)
    +
    +// Semrelease atomically increments *s and notifies a waiting goroutine
    +// if one is blocked in Semacquire.
    +// It is intended as a simple wakeup primitive for use by the synchronization
    +// library and should not be used directly.
    +func runtime_Semrelease(s *uint32)
    

    syncパッケージ内でruntime_Semacquireruntime_Semreleaseが宣言されています。

  5. src/pkg/sync/cond.go, src/pkg/sync/mutex.go, src/pkg/sync/rwmutex.go, src/pkg/sync/waitgroup.go: これらのファイルでは、"runtime"のインポートが削除され、runtime.Semacquireruntime.Semreleaseへの呼び出しがruntime_Semacquireruntime_Semreleaseに置き換えられています。

コアとなるコードの解説

このコミットの核心は、Goのランタイムと標準ライブラリの間のインターフェースを再定義し、低レベルな内部実装の詳細を公開APIから隠蔽することにあります。

  • runtimeパッケージからの削除: Alloc, Free, Lookupは、Goのメモリ管理の非常に低レベルな側面を直接操作するものでした。これらは通常、Goのガベージコレクタやアロケータによって自動的に処理されるべきであり、ユーザーが直接介入することは稀で、誤用につながる可能性がありました。同様に、SemacquireSemreleaseも、Goの並行処理の内部メカニズムを直接公開していました。これらの関数をruntimeパッケージの公開APIから削除することで、GoのAPIはより高レベルで抽象化されたものとなり、ユーザーはより安全で意図された方法でGoの機能を利用できるようになります。

  • sema.gocの移動とリネーム: src/pkg/runtime/sema.gocpackage syncに変更され、関数名がSemacquireからruntime_SemacquireSemreleaseからruntime_Semreleaseにリネームされたことは非常に重要です。これは、セマフォの低レベルな実装が、もはやruntimeパッケージの公開部分ではなく、syncパッケージの内部的な詳細として扱われることを意味します。runtime_というプレフィックスは、Goの慣習として、その関数がランタイムによって提供される内部的なものであることを示唆しています。

  • syncパッケージの適応: syncパッケージ内のMutex, Cond, WaitGroupなどの同期プリミティブは、これまでruntime.Semacquireruntime.Semreleaseを直接呼び出してゴルーチンの待機と通知を行っていました。このコミットでは、これらの呼び出しが新しい内部関数runtime_Semacquireruntime_Semreleaseに切り替えられました。これにより、syncパッケージは引き続きランタイムのセマフォ機能を利用できますが、その依存関係はより明確に内部的なものとして扱われます。src/pkg/sync/runtime.goは、syncパッケージがこれらの内部関数を型安全に呼び出すための「橋渡し」の役割を果たします。

  • テストの再編成: テストファイルの移動と+build ignoreタグの追加は、これらのテストがGoのランタイムの内部的な動作を検証するためのものであり、一般的なGoプログラムのテストとは異なる性質を持つことを明確にしています。また、syncパッケージのテストが内部のセマフォ関数にアクセスできるようにexport_test.goが導入されたことも、テストの分離と内部APIへのアクセス制御のバランスを示しています。

全体として、このコミットはGoの内部アーキテクチャの洗練と、公開APIの堅牢性および使いやすさの向上を目的としたものです。低レベルな詳細をカプセル化し、高レベルな抽象化を通じて機能を提供することで、Goはより安定した開発体験を提供し、将来的なランタイムの変更にも柔軟に対応できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: runtimeパッケージ, syncパッケージ
  • Go言語のソースコード
  • Go言語のIssueトラッカー
  • Go言語のコードレビューシステム (Gerrit)
  • セマフォに関する一般的な情報(並行プログラミングの概念)
  • Goのビルドタグに関する情報 (+build ignore)