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

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

コミット

コミットハッシュ: b7e9d22528abec3529d2a28fd59beaae5e21023c
作成者: Alex Brainman alex.brainman@gmail.com
日付: 2011年12月20日 火曜日 11:53:24 +1100
コミットメッセージ: "net/http: test should not leave tmp files behind on windows"

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

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

元コミット内容

このコミットはsrc/pkg/net/http/request_test.goファイルに対する小規模な修正を含みます。変更は1つのファイルに対して8行の追加と6行の削除で、計14行の変更が行われました。

主な変更点:

  • testMultipartFile関数によって返されるファイルディスクリプタに適切なdefer Close()を追加
  • 変数名をfdからfdaおよびfdbに変更して、複数のファイルディスクリプタを区別可能にした
  • 各ファイルディスクリプタに対してdefer Close()呼び出しを追加してリソースリークを防止

変更の背景

このコミットは、GoのHTTPパッケージのテストがWindows環境で一時ファイルを残さないようにするための修正です。Windowsでは、ファイルハンドルが適切に閉じられない場合、一時ファイルが削除されずにディスクに残ってしまうという問題がありました。

この問題は、GoのHTTPパッケージがマルチパートフォームデータを処理する際に発生していました。テスト中にファイルディスクリプタが適切に閉じられないと、特にWindows環境では「The process cannot access the file because it is being used by another process」エラーが発生し、一時ファイルの削除が失敗することがありました。

前提知識の解説

マルチパートフォームデータについて

マルチパートフォームデータは、HTMLフォームでファイルアップロードを行う際に使用されるコンテンツタイプです。RFC 7578で定義されており、異なるデータタイプ(テキスト、ファイル等)を単一のHTTPリクエストで送信できます。

Goのdefer文について

Goのdefer文は、関数の実行が終了する際に特定の処理を実行する仕組みです。LIFOスタック形式で実行されるため、複数のdefer文がある場合は最後に定義されたものから順番に実行されます。リソースの解放処理(ファイルクローズ、ミューテックス解放など)によく使用されます。

Windowsのファイル削除制限について

Windows環境では、ファイルハンドルが開いている間はそのファイルを削除できません。Unix系システムとは異なり、開いているファイルに対する削除操作は「The process cannot access the file because it is being used by another process」エラーで失敗します。

ファイルディスクリプタリークについて

ファイルディスクリプタ(ファイルハンドル)が適切に閉じられない場合、OSのリソースが消費され続けます。これをファイルディスクリプタリークと呼びます。システムの制限に達すると、新しいファイルやソケットが開けなくなり、アプリケーションが正常に動作しなくなります。

技術的詳細

修正前の問題点

fd := testMultipartFile(t, req, "filea", "filea.txt", fileaContents)
assertMem("filea", fd)
fd = testMultipartFile(t, req, "fileb", "fileb.txt", filebContents)

この実装では以下の問題がありました:

  1. fd変数の再利用により、最初のファイルディスクリプタが参照を失い、適切に閉じられない
  2. 変数のスコープが終了してもファイルディスクリプタが自動的に閉じられない
  3. Windowsでは開いているファイルハンドルがあると一時ファイルが削除できない

修正後の実装

fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents)
defer fda.Close()
assertMem("filea", fda)
fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents)
defer fdb.Close()

この修正により:

  1. 各ファイルディスクリプタに固有の変数名を付与
  2. defer Close()を使用してリソースの確実な解放を保証
  3. 関数終了時に自動的にファイルハンドルがクローズされる

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

@@ -214,14 +214,16 @@ func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) {
 		}
 	}
-	fd := testMultipartFile(t, req, "filea", "filea.txt", fileaContents)
-	assertMem("filea", fd)
-	fd = testMultipartFile(t, req, "fileb", "fileb.txt", filebContents)
+	fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents)
+	defer fda.Close()
+	assertMem("filea", fda)
+	fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents)
+	defer fdb.Close()
 	if allMem {
-		assertMem("fileb", fd)
+		assertMem("fileb", fdb)
 	} else {
-		if _, ok := fd.(*os.File); !ok {
-			t.Errorf("fileb has unexpected underlying type %T", fd)
+		if _, ok := fdb.(*os.File); !ok {
+			t.Errorf("fileb has unexpected underlying type %T", fdb)
 		}
 	}

コアとなるコードの解説

変更の核心部分

  1. 変数名の変更: fdからfdafdbへの変更

    • 複数のファイルディスクリプタを同時に管理するための命名規則改善
    • 変数の再利用による参照の喪失を防止
  2. defer文の追加: 各ファイルディスクリプタに対するdefer Close()の追加

    • defer fda.Close()defer fdb.Close()により、関数終了時の確実なリソース解放を保証
    • LIFO(後入れ先出し)の順序でクローズ処理が実行される
  3. 型アサーション部分の修正: fdからfdbへの変更

    • 正しい変数を参照するよう修正
    • テストの意図を明確化

リソース管理の改善

この修正により、Windows環境での以下の問題が解決されました:

  1. 一時ファイルの削除失敗: ファイルハンドルが開いている間はファイル削除ができないWindows固有の制限
  2. ファイルディスクリプタリーク: 適切に閉じられないファイルハンドルによるシステムリソースの消費
  3. テスト環境の汚染: テスト実行後に残る一時ファイルによるディスク容量の無駄な消費

パフォーマンスへの影響

この修正はテストの実行時間やメモリ使用量に大きな影響を与えませんが、長期的なシステムの安定性に貢献します:

  • ファイルディスクリプタの適切な管理により、システムリソースの枯渇を防止
  • 一時ファイルの確実な削除により、ディスク容量の無駄な消費を防止
  • Windows環境でのテストの信頼性向上

関連リンク

参考にした情報源リンク